xen-4.9.2/0000775000175000017500000000000013263626161010454 5ustar smbsmbxen-4.9.2/tools/0000775000175000017500000000000013263626151011613 5ustar smbsmbxen-4.9.2/tools/Rules.mk0000664000175000017500000002617113256712137013247 0ustar smbsmb# -*- mode: Makefile; -*- # `all' is the default target all: -include $(XEN_ROOT)/config/Tools.mk include $(XEN_ROOT)/Config.mk export _INSTALL := $(INSTALL) INSTALL = $(XEN_ROOT)/tools/cross-install XEN_INCLUDE = $(XEN_ROOT)/tools/include XEN_LIBXENTOOLLOG = $(XEN_ROOT)/tools/libs/toollog XEN_LIBXENEVTCHN = $(XEN_ROOT)/tools/libs/evtchn XEN_LIBXENGNTTAB = $(XEN_ROOT)/tools/libs/gnttab XEN_LIBXENCALL = $(XEN_ROOT)/tools/libs/call XEN_LIBXENFOREIGNMEMORY = $(XEN_ROOT)/tools/libs/foreignmemory XEN_LIBXENDEVICEMODEL = $(XEN_ROOT)/tools/libs/devicemodel XEN_LIBXC = $(XEN_ROOT)/tools/libxc XEN_XENLIGHT = $(XEN_ROOT)/tools/libxl # Currently libxlutil lives in the same directory as libxenlight XEN_XLUTIL = $(XEN_XENLIGHT) XEN_XENSTORE = $(XEN_ROOT)/tools/xenstore XEN_LIBXENSTAT = $(XEN_ROOT)/tools/xenstat/libxenstat/src XEN_BLKTAP2 = $(XEN_ROOT)/tools/blktap2 XEN_LIBVCHAN = $(XEN_ROOT)/tools/libvchan CFLAGS_xeninclude = -I$(XEN_INCLUDE) XENSTORE_XENSTORED ?= y # A debug build of tools? debug ?= n debug_symbols ?= $(debug) # Set CONFIG_GOLANG=y in .config (or in make) to build golang CONFIG_GOLANG ?= n XEN_GOPATH = $(XEN_ROOT)/tools/golang XEN_GOCODE_URL = golang.xenproject.org ifeq ($(debug_symbols),y) CFLAGS += -g3 endif ifneq ($(nosharedlibs),y) INSTALL_SHLIB = $(INSTALL_PROG) SYMLINK_SHLIB = ln -sf libextension = .so else libextension = .a XENSTORE_STATIC_CLIENTS=y # If something tries to use these it is a mistake. Provide references # to nonexistent programs to produce a sane error message. INSTALL_SHLIB = : install-shlib-unsupported-fail SYMLINK_SHLIB = : symlink-shlib-unsupported-fail endif # Compiling and linking against in tree libraries. # # In order to compile and link against an in-tree library various # cpp/compiler/linker options are required. # # For example consider a library "libfoo" which itself uses two other # libraries: # libbar - whose use is entirely internal to libfoo and not exposed # to users of libfoo at all. # libbaz - whose use is entirely internal to libfoo but libfoo's # public headers include one or more of libbaz's # public headers. Users of libfoo are therefore transitively # using libbaz's header but not linking against libbaz. # # SHDEPS_libfoo: Flags for linking recursive dependencies of # libfoo. Must contain SHLIB for every library which # libfoo links against. So must contain both # $(SHLIB_libbar) and $(SHLIB_libbaz). # # SHLIB_libfoo: Flags for recursively linking against libfoo. Must # contains SHDEPS_libfoo and: # -Wl,-rpath-link= # # CFLAGS_libfoo: Flags for compiling against libfoo. Must add the # directories containing libfoo's headers to the # include path. Must recursively include # $(CFLAGS_libbaz), to satisfy the transitive inclusion # of the headers but not $(CFLAGS_libbar) since none of # libbar's headers are required to build against # libfoo. # # LDLIBS_libfoo: Flags for linking against libfoo. Must contain # $(SHDEPS_libfoo) and the path to libfoo.so # # Consumers of libfoo should include $(CFLAGS_libfoo) and # $(LDLIBS_libfoo) in their appropriate directories. They should not # include any CFLAGS or LDLIBS relating to libbar or libbaz unless # they use those libraries directly (not via libfoo) too. # # Consumers of libfoo should not directly use $(SHDEPS_libfoo) or # $(SHLIB_libfoo) CFLAGS_libxentoollog = -I$(XEN_LIBXENTOOLLOG)/include $(CFLAGS_xeninclude) SHDEPS_libxentoollog = LDLIBS_libxentoollog = $(SHDEPS_libxentoollog) $(XEN_LIBXENTOOLLOG)/libxentoollog$(libextension) SHLIB_libxentoollog = $(SHDEPS_libxentoollog) -Wl,-rpath-link=$(XEN_LIBXENTOOLLOG) CFLAGS_libxenevtchn = -I$(XEN_LIBXENEVTCHN)/include $(CFLAGS_xeninclude) SHDEPS_libxenevtchn = LDLIBS_libxenevtchn = $(SHDEPS_libxenevtchn) $(XEN_LIBXENEVTCHN)/libxenevtchn$(libextension) SHLIB_libxenevtchn = $(SHDEPS_libxenevtchn) -Wl,-rpath-link=$(XEN_LIBXENEVTCHN) CFLAGS_libxengnttab = -I$(XEN_LIBXENGNTTAB)/include $(CFLAGS_xeninclude) SHDEPS_libxengnttab = $(SHLIB_libxentoollog) LDLIBS_libxengnttab = $(SHDEPS_libxengnttab) $(XEN_LIBXENGNTTAB)/libxengnttab$(libextension) SHLIB_libxengnttab = $(SHDEPS_libxengnttab) -Wl,-rpath-link=$(XEN_LIBXENGNTTAB) CFLAGS_libxencall = -I$(XEN_LIBXENCALL)/include $(CFLAGS_xeninclude) SHDEPS_libxencall = LDLIBS_libxencall = $(SHDEPS_libxencall) $(XEN_LIBXENCALL)/libxencall$(libextension) SHLIB_libxencall = $(SHDEPS_libxencall) -Wl,-rpath-link=$(XEN_LIBXENCALL) CFLAGS_libxenforeignmemory = -I$(XEN_LIBXENFOREIGNMEMORY)/include $(CFLAGS_xeninclude) SHDEPS_libxenforeignmemory = LDLIBS_libxenforeignmemory = $(SHDEPS_libxenforeignmemory) $(XEN_LIBXENFOREIGNMEMORY)/libxenforeignmemory$(libextension) SHLIB_libxenforeignmemory = $(SHDEPS_libxenforeignmemory) -Wl,-rpath-link=$(XEN_LIBXENFOREIGNMEMORY) CFLAGS_libxendevicemodel = -I$(XEN_LIBXENDEVICEMODEL)/include $(CFLAGS_xeninclude) SHDEPS_libxendevicemodel = $(SHLIB_libxentoollog) $(SHLIB_xencall) LDLIBS_libxendevicemodel = $(SHDEPS_libxendevicemodel) $(XEN_LIBXENDEVICEMODEL)/libxendevicemodel$(libextension) SHLIB_libxendevicemodel = $(SHDEPS_libxendevicemodel) -Wl,-rpath-link=$(XEN_LIBXENDEVICEMODEL) # code which compiles against libxenctrl get __XEN_TOOLS__ and # therefore sees the unstable hypercall interfaces. CFLAGS_libxenctrl = -I$(XEN_LIBXC)/include $(CFLAGS_libxentoollog) $(CFLAGS_libxenforeignmemory) $(CFLAGS_libxendevicemodel) $(CFLAGS_xeninclude) -D__XEN_TOOLS__ SHDEPS_libxenctrl = $(SHLIB_libxentoollog) $(SHLIB_libxenevtchn) $(SHLIB_libxengnttab) $(SHLIB_libxencall) $(SHLIB_libxenforeignmemory) $(SHLIB_libxendevicemodel) LDLIBS_libxenctrl = $(SHDEPS_libxenctrl) $(XEN_LIBXC)/libxenctrl$(libextension) SHLIB_libxenctrl = $(SHDEPS_libxenctrl) -Wl,-rpath-link=$(XEN_LIBXC) CFLAGS_libxenguest = -I$(XEN_LIBXC)/include $(CFLAGS_libxenevtchn) $(CFLAGS_libxenforeignmemory) $(CFLAGS_xeninclude) SHDEPS_libxenguest = $(SHLIB_libxenevtchn) LDLIBS_libxenguest = $(SHDEPS_libxenguest) $(XEN_LIBXC)/libxenguest$(libextension) SHLIB_libxenguest = $(SHDEPS_libxenguest) -Wl,-rpath-link=$(XEN_LIBXC) CFLAGS_libxenstore = -I$(XEN_XENSTORE)/include $(CFLAGS_xeninclude) SHDEPS_libxenstore = LDLIBS_libxenstore = $(SHDEPS_libxenstore) $(XEN_XENSTORE)/libxenstore$(libextension) SHLIB_libxenstore = $(SHDEPS_libxenstore) -Wl,-rpath-link=$(XEN_XENSTORE) CFLAGS_libxenstat = -I$(XEN_LIBXENSTAT) SHDEPS_libxenstat = $(SHLIB_libxenctrl) $(SHLIB_libxenstore) LDLIBS_libxenstat = $(SHDEPS_libxenstat) $(XEN_LIBXENSTAT)/libxenstat$(libextension) SHLIB_libxenstat = $(SHDEPS_libxenstat) -Wl,-rpath-link=$(XEN_LIBXENSTAT) CFLAGS_libxenvchan = -I$(XEN_LIBVCHAN) SHDEPS_libxenvchan = $(SHLIB_libxentoollog) $(SHLIB_libxenstore) $(SHLIB_libxenevtchn) $(SHLIB_libxengnttab) LDLIBS_libxenvchan = $(SHDEPS_libxenvchan) $(XEN_LIBVCHAN)/libxenvchan$(libextension) SHLIB_libxenvchan = $(SHDEPS_libxenvchan) -Wl,-rpath-link=$(XEN_LIBVCHAN) ifeq ($(debug),y) # Disable optimizations CFLAGS += -O0 -fno-omit-frame-pointer # But allow an override to -O0 in case Python enforces -D_FORTIFY_SOURCE=. PY_CFLAGS += $(PY_NOOPT_CFLAGS) else CFLAGS += -O2 -fomit-frame-pointer endif LIBXL_BLKTAP ?= $(CONFIG_BLKTAP2) ifeq ($(LIBXL_BLKTAP),y) CFLAGS_libblktapctl = -I$(XEN_BLKTAP2)/control -I$(XEN_BLKTAP2)/include $(CFLAGS_xeninclude) SHDEPS_libblktapctl = LDLIBS_libblktapctl = $(SHDEPS_libblktapctl) $(XEN_BLKTAP2)/control/libblktapctl$(libextension) SHLIB_libblktapctl = $(SHDEPS_libblktapctl) -Wl,-rpath-link=$(XEN_BLKTAP2)/control else CFLAGS_libblktapctl = SHDEPS_libblktapctl = LDLIBS_libblktapctl = SHLIB_libblktapctl = PKG_CONFIG_REMOVE += xenblktapctl endif CFLAGS_libxenlight = -I$(XEN_XENLIGHT) $(CFLAGS_libxenctrl) $(CFLAGS_xeninclude) SHDEPS_libxenlight = $(SHLIB_libxenctrl) $(SHLIB_libxenstore) $(SHLIB_libblktapctl) LDLIBS_libxenlight = $(SHDEPS_libxenlight) $(XEN_XENLIGHT)/libxenlight$(libextension) SHLIB_libxenlight = $(SHDEPS_libxenlight) -Wl,-rpath-link=$(XEN_XENLIGHT) CFLAGS_libxlutil = -I$(XEN_XLUTIL) SHDEPS_libxlutil = $(SHLIB_libxenlight) LDLIBS_libxlutil = $(SHDEPS_libxlutil) $(XEN_XLUTIL)/libxlutil$(libextension) SHLIB_libxlutil = $(SHDEPS_libxlutil) -Wl,-rpath-link=$(XEN_XLUTIL) CFLAGS += -D__XEN_INTERFACE_VERSION__=__XEN_LATEST_INTERFACE_VERSION__ # Get gcc to generate the dependencies for us. CFLAGS += -MMD -MF .$(if $(filter-out .,$(@D)),$(subst /,@,$(@D))@)$(@F).d DEPS = .*.d ifneq ($(FILE_OFFSET_BITS),) CFLAGS += -D_FILE_OFFSET_BITS=$(FILE_OFFSET_BITS) endif ifneq ($(XEN_OS),NetBSD) # Enable implicit LFS support *and* explicit LFS names. CFLAGS += -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE endif # 32-bit x86 does not perform well with -ve segment accesses on Xen. CFLAGS-$(CONFIG_X86_32) += $(call cc-option,$(CC),-mno-tls-direct-seg-refs) CFLAGS += $(CFLAGS-y) CFLAGS += $(EXTRA_CFLAGS_XEN_TOOLS) INSTALL_PYTHON_PROG = \ $(XEN_ROOT)/tools/python/install-wrap "$(PYTHON_PATH)" $(INSTALL_PROG) %.opic: %.c $(CC) $(CPPFLAGS) -DPIC $(CFLAGS) $(CFLAGS_$*.opic) -fPIC -c -o $@ $< $(APPEND_CFLAGS) %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_$*.o) -c -o $@ $< $(APPEND_CFLAGS) %.o: %.cc $(CC) $(CPPFLAGS) $(CXXFLAGS) $(CXXFLAGS_$*.o) -c -o $@ $< $(APPEND_CFLAGS) %.o: %.S $(CC) $(CFLAGS) $(CFLAGS_$*.o) -c $< -o $@ $(APPEND_CFLAGS) %.opic: %.S $(CC) $(CPPFLAGS) -DPIC $(CFLAGS) $(CFLAGS.opic) -fPIC -c -o $@ $< $(APPEND_CFLAGS) headers.chk: for i in $(filter %.h,$^); do \ $(CC) -x c -ansi -Wall -Werror $(CFLAGS_xeninclude) \ -S -o /dev/null $$i || exit 1; \ echo $$i; \ done >$@.new mv $@.new $@ subdirs-all subdirs-clean subdirs-install subdirs-distclean: .phony @set -e; for subdir in $(SUBDIRS) $(SUBDIRS-y); do \ $(MAKE) subdir-$(patsubst subdirs-%,%,$@)-$$subdir; \ done subdir-all-% subdir-clean-% subdir-install-%: .phony $(MAKE) -C $* $(patsubst subdir-%-$*,%,$@) subdir-distclean-%: .phony $(MAKE) -C $* distclean ifeq (,$(findstring clean,$(MAKECMDGOALS))) $(XEN_ROOT)/config/Tools.mk: $(error You have to run ./configure before building or installing the tools) endif PKG_CONFIG_DIR ?= $(XEN_ROOT)/tools/pkg-config PKG_CONFIG_FILTER = $(foreach l,$(PKG_CONFIG_REMOVE),-e 's!\([ ,]\)$(l),!\1!g' -e 's![ ,]$(l)$$!!g') $(PKG_CONFIG_DIR)/%.pc: %.pc.in Makefile mkdir -p $(PKG_CONFIG_DIR) @sed -e 's!@@version@@!$(PKG_CONFIG_VERSION)!g' \ -e 's!@@prefix@@!$(PKG_CONFIG_PREFIX)!g' \ -e 's!@@incdir@@!$(PKG_CONFIG_INCDIR)!g' \ -e 's!@@libdir@@!$(PKG_CONFIG_LIBDIR)!g' \ -e 's!@@firmwaredir@@!$(XENFIRMWAREDIR)!g' \ -e 's!@@libexecbin@@!$(LIBEXEC_BIN)!g' \ -e 's!@@cflagslocal@@!$(PKG_CONFIG_CFLAGS_LOCAL)!g' \ -e 's!@@libsflag@@!-Wl,-rpath-link=!g' \ $(PKG_CONFIG_FILTER) < $< > $@ %.pc: %.pc.in Makefile @sed -e 's!@@version@@!$(PKG_CONFIG_VERSION)!g' \ -e 's!@@prefix@@!$(PKG_CONFIG_PREFIX)!g' \ -e 's!@@incdir@@!$(PKG_CONFIG_INCDIR)!g' \ -e 's!@@libdir@@!$(PKG_CONFIG_LIBDIR)!g' \ -e 's!@@firmwaredir@@!$(XENFIRMWAREDIR)!g' \ -e 's!@@libexecbin@@!$(LIBEXEC_BIN)!g' \ -e 's!@@cflagslocal@@!!g' \ -e 's!@@libsflag@@!-L!g' \ $(PKG_CONFIG_FILTER) < $< > $@ xen-4.9.2/tools/xenpaging/0000775000175000017500000000000013256712137013575 5ustar smbsmbxen-4.9.2/tools/xenpaging/xenpaging.h0000664000175000017500000000405113256712137015726 0ustar smbsmb/****************************************************************************** * tools/xenpaging/xenpaging.h * * Xen domain paging. * * Copyright (c) 2009 Citrix Systems, Inc. (Patrick Colp) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #ifndef __XEN_PAGING2_H__ #define __XEN_PAGING2_H__ #include #define XC_WANT_COMPAT_MAP_FOREIGN_API #include #include #include #include #define XENPAGING_PAGEIN_QUEUE_SIZE 64 struct vm_event { domid_t domain_id; xenevtchn_handle *xce_handle; int port; vm_event_back_ring_t back_ring; uint32_t evtchn_port; void *ring_page; }; struct xenpaging { xc_interface *xc_handle; struct xs_handle *xs_handle; unsigned long *bitmap; unsigned long *slot_to_gfn; int *gfn_to_slot; void *paging_buffer; struct vm_event vm_event; int fd; /* number of pages for which data structures were allocated */ int max_pages; int num_paged_out; int target_tot_pages; int policy_mru_size; int use_poll_timeout; int debug; int stack_count; int *free_slot_stack; unsigned long pagein_queue[XENPAGING_PAGEIN_QUEUE_SIZE]; }; extern void create_page_in_thread(struct xenpaging *paging); extern void page_in_trigger(void); #endif // __XEN_PAGING_H__ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xenpaging/file_ops.c0000664000175000017500000000327313256712137015546 0ustar smbsmb/****************************************************************************** * * Common file operations. * * Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #include #include static int file_op(int fd, void *page, int i, ssize_t (*fn)(int, void *, size_t)) { off_t offset = i; int total = 0; int bytes; offset = lseek(fd, offset << PAGE_SHIFT, SEEK_SET); if ( offset == (off_t)-1 ) return -1; while ( total < PAGE_SIZE ) { bytes = fn(fd, page + total, PAGE_SIZE - total); if ( bytes <= 0 ) return -1; total += bytes; } return 0; } static ssize_t my_write(int fd, void *buf, size_t count) { return write(fd, buf, count); } int read_page(int fd, void *page, int i) { return file_op(fd, page, i, &read); } int write_page(int fd, void *page, int i) { return file_op(fd, page, i, &my_write); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xenpaging/policy_default.c0000664000175000017500000001100413256712137016740 0ustar smbsmb/****************************************************************************** * * Xen domain paging default policy. * * Copyright (c) 2009 Citrix Systems, Inc. (Patrick Colp) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #include "xc_bitops.h" #include "policy.h" #define DEFAULT_MRU_SIZE (1024 * 16) static unsigned long *mru; static unsigned int i_mru; static unsigned int mru_size; static unsigned long *bitmap; static unsigned long *unconsumed; static unsigned int unconsumed_cleared; static unsigned long current_gfn; static unsigned long max_pages; int policy_init(struct xenpaging *paging) { int i; int rc = -ENOMEM; max_pages = paging->max_pages; /* Allocate bitmap for pages not to page out */ bitmap = bitmap_alloc(max_pages); if ( !bitmap ) goto out; /* Allocate bitmap to track unusable pages */ unconsumed = bitmap_alloc(max_pages); if ( !unconsumed ) goto out; /* Initialise MRU list of paged in pages */ if ( paging->policy_mru_size > 0 ) mru_size = paging->policy_mru_size; else mru_size = paging->policy_mru_size = DEFAULT_MRU_SIZE; mru = malloc(sizeof(*mru) * mru_size); if ( mru == NULL ) goto out; for ( i = 0; i < mru_size; i++ ) mru[i] = INVALID_MFN; /* Don't page out page 0 */ set_bit(0, bitmap); /* Start in the middle to avoid paging during BIOS startup */ current_gfn = max_pages / 2; rc = 0; out: return rc; } unsigned long policy_choose_victim(struct xenpaging *paging) { xc_interface *xch = paging->xc_handle; unsigned long i; /* One iteration over all possible gfns */ for ( i = 0; i < max_pages; i++ ) { /* Try next gfn */ current_gfn++; /* Restart on wrap */ if ( current_gfn >= max_pages ) current_gfn = 0; if ( (current_gfn & (BITS_PER_LONG - 1)) == 0 ) { /* All gfns busy */ if ( ~bitmap[current_gfn >> ORDER_LONG] == 0 || ~unconsumed[current_gfn >> ORDER_LONG] == 0 ) { current_gfn += BITS_PER_LONG; i += BITS_PER_LONG; continue; } } /* gfn busy */ if ( test_bit(current_gfn, bitmap) ) continue; /* gfn already tested */ if ( test_bit(current_gfn, unconsumed) ) continue; /* gfn found */ break; } /* Could not nominate any gfn */ if ( i >= max_pages ) { /* No more pages, wait in poll */ paging->use_poll_timeout = 1; /* Count wrap arounds */ unconsumed_cleared++; /* Force retry every few seconds (depends on poll() timeout) */ if ( unconsumed_cleared > 123) { /* Force retry of unconsumed gfns on next call */ bitmap_clear(unconsumed, max_pages); unconsumed_cleared = 0; DPRINTF("clearing unconsumed, current_gfn %lx", current_gfn); } return INVALID_MFN; } set_bit(current_gfn, unconsumed); return current_gfn; } void policy_notify_paged_out(unsigned long gfn) { set_bit(gfn, bitmap); clear_bit(gfn, unconsumed); } static void policy_handle_paged_in(unsigned long gfn, int do_mru) { unsigned long old_gfn = mru[i_mru & (mru_size - 1)]; if ( old_gfn != INVALID_MFN ) clear_bit(old_gfn, bitmap); if (do_mru) { mru[i_mru & (mru_size - 1)] = gfn; } else { clear_bit(gfn, bitmap); mru[i_mru & (mru_size - 1)] = INVALID_MFN; } i_mru++; } void policy_notify_paged_in(unsigned long gfn) { policy_handle_paged_in(gfn, 1); } void policy_notify_paged_in_nomru(unsigned long gfn) { policy_handle_paged_in(gfn, 0); } void policy_notify_dropped(unsigned long gfn) { clear_bit(gfn, bitmap); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xenpaging/policy.h0000664000175000017500000000261713256712137015253 0ustar smbsmb/****************************************************************************** * tools/xenpaging/policy.h * * Xen domain paging policy hooks. * * Copyright (c) 2009 Citrix Systems, Inc. (Patrick Colp) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #ifndef __XEN_PAGING_POLICY_H__ #define __XEN_PAGING_POLICY_H__ #include "xenpaging.h" int policy_init(struct xenpaging *paging); unsigned long policy_choose_victim(struct xenpaging *paging); void policy_notify_paged_out(unsigned long gfn); void policy_notify_paged_in(unsigned long gfn); void policy_notify_paged_in_nomru(unsigned long gfn); void policy_notify_dropped(unsigned long gfn); #endif // __XEN_PAGING_POLICY_H__ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xenpaging/file_ops.h0000664000175000017500000000213613256712137015550 0ustar smbsmb/****************************************************************************** * tools/xenpaging/file_ops.h * * Common file operations. * * Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #ifndef __FILE_OPS_H__ #define __FILE_OPS_H__ int read_page(int fd, void *page, int i); int write_page(int fd, void *page, int i); #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xenpaging/xenpaging.c0000664000175000017500000007550413256712137015734 0ustar smbsmb/****************************************************************************** * * Domain paging. * Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "xc_bitops.h" #include "file_ops.h" #include "policy.h" #include "xenpaging.h" /* Defines number of mfns a guest should use at a time, in KiB */ #define WATCH_TARGETPAGES "memory/target-tot_pages" static char *watch_target_tot_pages; static char *dom_path; static char watch_token[16]; static char *filename; static int interrupted; static void unlink_pagefile(void) { if ( filename && filename[0] ) { unlink(filename); filename[0] = '\0'; } } static void close_handler(int sig) { interrupted = sig; unlink_pagefile(); } static void xenpaging_mem_paging_flush_ioemu_cache(struct xenpaging *paging) { struct xs_handle *xsh = paging->xs_handle; domid_t domain_id = paging->vm_event.domain_id; char path[80]; sprintf(path, "/local/domain/0/device-model/%u/command", domain_id); xs_write(xsh, XBT_NULL, path, "flush-cache", strlen("flush-cache")); } static int xenpaging_wait_for_event_or_timeout(struct xenpaging *paging) { xc_interface *xch = paging->xc_handle; xenevtchn_handle *xce = paging->vm_event.xce_handle; char **vec, *val; unsigned int num; struct pollfd fd[2]; int port; int rc; int timeout; /* Wait for event channel and xenstore */ fd[0].fd = xenevtchn_fd(xce); fd[0].events = POLLIN | POLLERR; fd[1].fd = xs_fileno(paging->xs_handle); fd[1].events = POLLIN | POLLERR; /* No timeout while page-out is still in progress */ timeout = paging->use_poll_timeout ? 100 : 0; rc = poll(fd, 2, timeout); if ( rc < 0 ) { if (errno == EINTR) return 0; PERROR("Poll exited with an error"); return -1; } /* First check for guest shutdown */ if ( rc && fd[1].revents & POLLIN ) { DPRINTF("Got event from xenstore\n"); vec = xs_read_watch(paging->xs_handle, &num); if ( vec ) { DPRINTF("path '%s' token '%s'\n", vec[XS_WATCH_PATH], vec[XS_WATCH_TOKEN]); if ( strcmp(vec[XS_WATCH_TOKEN], watch_token) == 0 ) { /* If our guest disappeared, set interrupt flag and fall through */ if ( xs_is_domain_introduced(paging->xs_handle, paging->vm_event.domain_id) == false ) { xs_unwatch(paging->xs_handle, "@releaseDomain", watch_token); interrupted = SIGQUIT; /* No further poll result processing */ rc = 0; } } else if ( strcmp(vec[XS_WATCH_PATH], watch_target_tot_pages) == 0 ) { int ret, target_tot_pages; val = xs_read(paging->xs_handle, XBT_NULL, vec[XS_WATCH_PATH], NULL); if ( val ) { ret = sscanf(val, "%d", &target_tot_pages); if ( ret > 0 ) { /* KiB to pages */ target_tot_pages >>= 2; if ( target_tot_pages < 0 || target_tot_pages > paging->max_pages ) target_tot_pages = paging->max_pages; paging->target_tot_pages = target_tot_pages; /* Disable poll() delay while new target is not yet reached */ paging->use_poll_timeout = 0; DPRINTF("new target_tot_pages %d\n", target_tot_pages); } free(val); } } free(vec); } } if ( rc && fd[0].revents & POLLIN ) { DPRINTF("Got event from evtchn\n"); port = xenevtchn_pending(xce); if ( port == -1 ) { PERROR("Failed to read port from event channel"); rc = -1; goto err; } rc = xenevtchn_unmask(xce, port); if ( rc < 0 ) { PERROR("Failed to unmask event channel port"); } } err: return rc; } static int xenpaging_get_tot_pages(struct xenpaging *paging) { xc_interface *xch = paging->xc_handle; xc_domaininfo_t domain_info; int rc; rc = xc_domain_getinfolist(xch, paging->vm_event.domain_id, 1, &domain_info); if ( rc != 1 ) { PERROR("Error getting domain info"); return -1; } return domain_info.tot_pages; } static void *init_page(void) { void *buffer; /* Allocated page memory */ errno = posix_memalign(&buffer, PAGE_SIZE, PAGE_SIZE); if ( errno != 0 ) return NULL; /* Lock buffer in memory so it can't be paged out */ if ( mlock(buffer, PAGE_SIZE) < 0 ) { free(buffer); buffer = NULL; } return buffer; } static void usage(void) { printf("usage:\n\n"); printf(" xenpaging [options] -f -d \n\n"); printf("options:\n"); printf(" -d --domain= numerical domain_id of guest. This option is required.\n"); printf(" -f --pagefile= pagefile to use. This option is required.\n"); printf(" -m --max_memkb= maximum amount of memory to handle.\n"); printf(" -r --mru_size= number of paged-in pages to keep in memory.\n"); printf(" -v --verbose enable debug output.\n"); printf(" -h --help this output.\n"); } static int xenpaging_getopts(struct xenpaging *paging, int argc, char *argv[]) { int ch; static const char sopts[] = "hvd:f:m:r:"; static const struct option lopts[] = { {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, {"domain", 1, NULL, 'd'}, {"pagefile", 1, NULL, 'f'}, {"mru_size", 1, NULL, 'm'}, { } }; while ((ch = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { switch(ch) { case 'd': paging->vm_event.domain_id = atoi(optarg); break; case 'f': free(filename); filename = strdup(optarg); break; case 'm': /* KiB to pages */ paging->max_pages = atoi(optarg) >> 2; break; case 'r': paging->policy_mru_size = atoi(optarg); break; case 'v': paging->debug = 1; break; case 'h': case '?': usage(); return 1; } } argv += optind; argc -= optind; /* Path to pagefile is required */ if ( !filename ) { printf("Filename for pagefile missing!\n"); usage(); return 1; } /* Set domain id */ if ( !paging->vm_event.domain_id ) { printf("Numerical missing!\n"); return 1; } return 0; } static struct xenpaging *xenpaging_init(int argc, char *argv[]) { struct xenpaging *paging; xc_domaininfo_t domain_info; xc_interface *xch = NULL; xentoollog_logger *dbg = NULL; char *p; int rc; unsigned long ring_pfn, mmap_pfn; /* Allocate memory */ paging = calloc(1, sizeof(struct xenpaging)); if ( !paging ) goto err; /* Get cmdline options and domain_id */ if ( xenpaging_getopts(paging, argc, argv) ) goto err; /* Enable debug output */ if ( paging->debug ) dbg = (xentoollog_logger *)xtl_createlogger_stdiostream(stderr, XTL_DEBUG, 0); /* Open connection to xen */ paging->xc_handle = xch = xc_interface_open(dbg, NULL, 0); if ( !xch ) goto err; DPRINTF("xenpaging init\n"); /* Open connection to xenstore */ paging->xs_handle = xs_open(0); if ( paging->xs_handle == NULL ) { PERROR("Error initialising xenstore connection"); goto err; } /* write domain ID to watch so we can ignore other domain shutdowns */ snprintf(watch_token, sizeof(watch_token), "%u", paging->vm_event.domain_id); if ( xs_watch(paging->xs_handle, "@releaseDomain", watch_token) == false ) { PERROR("Could not bind to shutdown watch\n"); goto err; } /* Watch xenpagings working target */ dom_path = xs_get_domain_path(paging->xs_handle, paging->vm_event.domain_id); if ( !dom_path ) { PERROR("Could not find domain path\n"); goto err; } if ( asprintf(&watch_target_tot_pages, "%s/%s", dom_path, WATCH_TARGETPAGES) < 0 ) { PERROR("Could not alloc watch path\n"); goto err; } DPRINTF("watching '%s'\n", watch_target_tot_pages); if ( xs_watch(paging->xs_handle, watch_target_tot_pages, "") == false ) { PERROR("Could not bind to xenpaging watch\n"); goto err; } /* Map the ring page */ xc_get_hvm_param(xch, paging->vm_event.domain_id, HVM_PARAM_PAGING_RING_PFN, &ring_pfn); mmap_pfn = ring_pfn; paging->vm_event.ring_page = xc_map_foreign_pages(xch, paging->vm_event.domain_id, PROT_READ | PROT_WRITE, &mmap_pfn, 1); if ( !paging->vm_event.ring_page ) { /* Map failed, populate ring page */ rc = xc_domain_populate_physmap_exact(paging->xc_handle, paging->vm_event.domain_id, 1, 0, 0, &ring_pfn); if ( rc != 0 ) { PERROR("Failed to populate ring gfn\n"); goto err; } paging->vm_event.ring_page = xc_map_foreign_pages(xch, paging->vm_event.domain_id, PROT_READ | PROT_WRITE, &mmap_pfn, 1); if ( !paging->vm_event.ring_page ) { PERROR("Could not map the ring page\n"); goto err; } } /* Initialise Xen */ rc = xc_mem_paging_enable(xch, paging->vm_event.domain_id, &paging->vm_event.evtchn_port); if ( rc != 0 ) { switch ( errno ) { case EBUSY: ERROR("xenpaging is (or was) active on this domain"); break; case ENODEV: ERROR("xenpaging requires Hardware Assisted Paging"); break; case EMLINK: ERROR("xenpaging not supported while iommu passthrough is enabled"); break; case EXDEV: ERROR("xenpaging not supported in a PoD guest"); break; default: PERROR("Error initialising shared page"); break; } goto err; } /* Open event channel */ paging->vm_event.xce_handle = xenevtchn_open(NULL, 0); if ( paging->vm_event.xce_handle == NULL ) { PERROR("Failed to open event channel"); goto err; } /* Bind event notification */ rc = xenevtchn_bind_interdomain(paging->vm_event.xce_handle, paging->vm_event.domain_id, paging->vm_event.evtchn_port); if ( rc < 0 ) { PERROR("Failed to bind event channel"); goto err; } paging->vm_event.port = rc; /* Initialise ring */ SHARED_RING_INIT((vm_event_sring_t *)paging->vm_event.ring_page); BACK_RING_INIT(&paging->vm_event.back_ring, (vm_event_sring_t *)paging->vm_event.ring_page, PAGE_SIZE); /* Now that the ring is set, remove it from the guest's physmap */ if ( xc_domain_decrease_reservation_exact(xch, paging->vm_event.domain_id, 1, 0, &ring_pfn) ) PERROR("Failed to remove ring from guest physmap"); /* Get max_pages from guest if not provided via cmdline */ if ( !paging->max_pages ) { rc = xc_domain_getinfolist(xch, paging->vm_event.domain_id, 1, &domain_info); if ( rc != 1 ) { PERROR("Error getting domain info"); goto err; } /* Record number of max_pages */ paging->max_pages = domain_info.max_pages; } /* Allocate bitmap for tracking pages that have been paged out */ paging->bitmap = bitmap_alloc(paging->max_pages); if ( !paging->bitmap ) { PERROR("Error allocating bitmap"); goto err; } DPRINTF("max_pages = %d\n", paging->max_pages); /* Allocate indicies for pagefile slots */ paging->slot_to_gfn = calloc(paging->max_pages, sizeof(*paging->slot_to_gfn)); paging->gfn_to_slot = calloc(paging->max_pages, sizeof(*paging->gfn_to_slot)); if ( !paging->slot_to_gfn || !paging->gfn_to_slot ) goto err; /* Allocate stack for known free slots in pagefile */ paging->free_slot_stack = calloc(paging->max_pages, sizeof(*paging->free_slot_stack)); if ( !paging->free_slot_stack ) goto err; /* Initialise policy */ rc = policy_init(paging); if ( rc != 0 ) { PERROR("Error initialising policy"); goto err; } paging->paging_buffer = init_page(); if ( !paging->paging_buffer ) { PERROR("Creating page aligned load buffer"); goto err; } /* Open file */ paging->fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); if ( paging->fd < 0 ) { PERROR("failed to open file"); goto err; } return paging; err: if ( paging ) { if ( paging->xs_handle ) xs_close(paging->xs_handle); if ( xch ) xc_interface_close(xch); if ( paging->paging_buffer ) { munlock(paging->paging_buffer, PAGE_SIZE); free(paging->paging_buffer); } if ( paging->vm_event.ring_page ) { munmap(paging->vm_event.ring_page, PAGE_SIZE); } free(dom_path); free(watch_target_tot_pages); free(paging->free_slot_stack); free(paging->slot_to_gfn); free(paging->gfn_to_slot); free(paging->bitmap); free(paging); } return NULL; } static void xenpaging_teardown(struct xenpaging *paging) { int rc; xc_interface *xch = paging->xc_handle; xs_unwatch(paging->xs_handle, watch_target_tot_pages, ""); xs_unwatch(paging->xs_handle, "@releaseDomain", watch_token); paging->xc_handle = NULL; /* Tear down domain paging in Xen */ munmap(paging->vm_event.ring_page, PAGE_SIZE); rc = xc_mem_paging_disable(xch, paging->vm_event.domain_id); if ( rc != 0 ) { PERROR("Error tearing down domain paging in xen"); } /* Unbind VIRQ */ rc = xenevtchn_unbind(paging->vm_event.xce_handle, paging->vm_event.port); if ( rc != 0 ) { PERROR("Error unbinding event port"); } paging->vm_event.port = -1; /* Close event channel */ rc = xenevtchn_close(paging->vm_event.xce_handle); if ( rc != 0 ) { PERROR("Error closing event channel"); } paging->vm_event.xce_handle = NULL; /* Close connection to xenstore */ xs_close(paging->xs_handle); /* Close connection to Xen */ xc_interface_close(xch); } static void get_request(struct vm_event *vm_event, vm_event_request_t *req) { vm_event_back_ring_t *back_ring; RING_IDX req_cons; back_ring = &vm_event->back_ring; req_cons = back_ring->req_cons; /* Copy request */ memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req)); req_cons++; /* Update ring */ back_ring->req_cons = req_cons; back_ring->sring->req_event = req_cons + 1; } static void put_response(struct vm_event *vm_event, vm_event_response_t *rsp) { vm_event_back_ring_t *back_ring; RING_IDX rsp_prod; back_ring = &vm_event->back_ring; rsp_prod = back_ring->rsp_prod_pvt; /* Copy response */ memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp)); rsp_prod++; /* Update ring */ back_ring->rsp_prod_pvt = rsp_prod; RING_PUSH_RESPONSES(back_ring); } /* Evict a given gfn * Returns < 0 on fatal error * Returns 0 on successful evict * Returns > 0 if gfn can not be evicted */ static int xenpaging_evict_page(struct xenpaging *paging, unsigned long gfn, int slot) { xc_interface *xch = paging->xc_handle; void *page; xen_pfn_t victim = gfn; int ret; DECLARE_DOMCTL; /* Nominate page */ ret = xc_mem_paging_nominate(xch, paging->vm_event.domain_id, gfn); if ( ret < 0 ) { /* unpageable gfn is indicated by EBUSY */ if ( errno == EBUSY ) ret = 1; else PERROR("Error nominating page %lx", gfn); goto out; } /* Map page */ page = xc_map_foreign_pages(xch, paging->vm_event.domain_id, PROT_READ, &victim, 1); if ( page == NULL ) { PERROR("Error mapping page %lx", gfn); ret = -1; goto out; } /* Copy page */ ret = write_page(paging->fd, page, slot); if ( ret < 0 ) { PERROR("Error copying page %lx", gfn); munmap(page, PAGE_SIZE); ret = -1; goto out; } /* Release page */ munmap(page, PAGE_SIZE); /* Tell Xen to evict page */ ret = xc_mem_paging_evict(xch, paging->vm_event.domain_id, gfn); if ( ret < 0 ) { /* A gfn in use is indicated by EBUSY */ if ( errno == EBUSY ) { ret = 1; DPRINTF("Nominated page %lx busy", gfn); } else PERROR("Error evicting page %lx", gfn); goto out; } DPRINTF("evict_page > gfn %lx pageslot %d\n", gfn, slot); /* Notify policy of page being paged out */ policy_notify_paged_out(gfn); /* Update index */ paging->slot_to_gfn[slot] = gfn; paging->gfn_to_slot[gfn] = slot; /* Record number of evicted pages */ paging->num_paged_out++; ret = 0; out: return ret; } static int xenpaging_resume_page(struct xenpaging *paging, vm_event_response_t *rsp, int notify_policy) { /* Put the page info on the ring */ put_response(&paging->vm_event, rsp); /* Notify policy of page being paged in */ if ( notify_policy ) { /* * Do not add gfn to mru list if the target is lower than mru size. * This allows page-out of these gfns if the target grows again. */ if (paging->num_paged_out > paging->policy_mru_size) policy_notify_paged_in(rsp->u.mem_paging.gfn); else policy_notify_paged_in_nomru(rsp->u.mem_paging.gfn); /* Record number of resumed pages */ paging->num_paged_out--; } /* Tell Xen page is ready */ return xenevtchn_notify(paging->vm_event.xce_handle, paging->vm_event.port); } static int xenpaging_populate_page(struct xenpaging *paging, unsigned long gfn, int i) { xc_interface *xch = paging->xc_handle; int ret; unsigned char oom = 0; DPRINTF("populate_page < gfn %lx pageslot %d\n", gfn, i); /* Read page */ ret = read_page(paging->fd, paging->paging_buffer, i); if ( ret != 0 ) { PERROR("Error reading page"); goto out; } do { /* Tell Xen to allocate a page for the domain */ ret = xc_mem_paging_load(xch, paging->vm_event.domain_id, gfn, paging->paging_buffer); if ( ret < 0 ) { if ( errno == ENOMEM ) { if ( oom++ == 0 ) DPRINTF("ENOMEM while preparing gfn %lx\n", gfn); sleep(1); continue; } PERROR("Error loading %lx during page-in", gfn); ret = -1; break; } } while ( ret && !interrupted ); out: return ret; } /* Trigger a page-in for a batch of pages */ static void resume_pages(struct xenpaging *paging, int num_pages) { xc_interface *xch = paging->xc_handle; int i, num = 0; for ( i = 0; i < paging->max_pages && num < num_pages; i++ ) { if ( test_bit(i, paging->bitmap) ) { paging->pagein_queue[num] = i; num++; if ( num == XENPAGING_PAGEIN_QUEUE_SIZE ) break; } } /* num may be less than num_pages, caller has to try again */ if ( num ) page_in_trigger(); } /* Evict one gfn and write it to the given slot * Returns < 0 on fatal error * Returns 0 on successful evict * Returns > 0 if no gfn can be evicted */ static int evict_victim(struct xenpaging *paging, int slot) { xc_interface *xch = paging->xc_handle; unsigned long gfn; static int num_paged_out; int ret; do { gfn = policy_choose_victim(paging); if ( gfn == INVALID_MFN ) { /* If the number did not change after last flush command then * the command did not reach qemu yet, or qemu still processes * the command, or qemu has nothing to release. * Right now there is no need to issue the command again. */ if ( num_paged_out != paging->num_paged_out ) { DPRINTF("Flushing qemu cache\n"); xenpaging_mem_paging_flush_ioemu_cache(paging); num_paged_out = paging->num_paged_out; } ret = ENOSPC; goto out; } if ( interrupted ) { ret = EINTR; goto out; } ret = xenpaging_evict_page(paging, gfn, slot); if ( ret < 0 ) goto out; } while ( ret ); if ( test_and_set_bit(gfn, paging->bitmap) ) ERROR("Page %lx has been evicted before", gfn); ret = 0; out: return ret; } /* Evict a batch of pages and write them to a free slot in the paging file * Returns < 0 on fatal error * Returns 0 if no gfn can be evicted * Returns > 0 on successful evict */ static int evict_pages(struct xenpaging *paging, int num_pages) { xc_interface *xch = paging->xc_handle; int rc, slot, num = 0; /* Reuse known free slots */ while ( paging->stack_count > 0 && num < num_pages ) { slot = paging->free_slot_stack[--paging->stack_count]; rc = evict_victim(paging, slot); if ( rc ) { num = rc < 0 ? -1 : num; return num; } num++; } /* Scan all slots slots for remainders */ for ( slot = 0; slot < paging->max_pages && num < num_pages; slot++ ) { /* Slot is allocated */ if ( paging->slot_to_gfn[slot] ) continue; rc = evict_victim(paging, slot); if ( rc ) { num = rc < 0 ? -1 : num; break; } num++; } return num; } int main(int argc, char *argv[]) { struct sigaction act; struct xenpaging *paging; vm_event_request_t req; vm_event_response_t rsp; int num, prev_num = 0; int slot; int tot_pages; int rc; xc_interface *xch; /* Initialise domain paging */ paging = xenpaging_init(argc, argv); if ( paging == NULL ) { fprintf(stderr, "Error initialising paging\n"); return 1; } xch = paging->xc_handle; DPRINTF("starting %s for domain_id %u with pagefile %s\n", argv[0], paging->vm_event.domain_id, filename); /* ensure that if we get a signal, we'll do cleanup, then exit */ act.sa_handler = close_handler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGHUP, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGALRM, &act, NULL); /* listen for page-in events to stop pager */ create_page_in_thread(paging); /* Swap pages in and out */ while ( 1 ) { /* Wait for Xen to signal that a page needs paged in */ rc = xenpaging_wait_for_event_or_timeout(paging); if ( rc < 0 ) { ERROR("Error getting event"); goto out; } else if ( rc != 0 ) { DPRINTF("Got event from Xen\n"); } while ( RING_HAS_UNCONSUMED_REQUESTS(&paging->vm_event.back_ring) ) { /* Indicate possible error */ rc = 1; get_request(&paging->vm_event, &req); if ( req.u.mem_paging.gfn > paging->max_pages ) { ERROR("Requested gfn %"PRIx64" higher than max_pages %x\n", req.u.mem_paging.gfn, paging->max_pages); goto out; } /* Check if the page has already been paged in */ if ( test_and_clear_bit(req.u.mem_paging.gfn, paging->bitmap) ) { /* Find where in the paging file to read from */ slot = paging->gfn_to_slot[req.u.mem_paging.gfn]; /* Sanity check */ if ( paging->slot_to_gfn[slot] != req.u.mem_paging.gfn ) { ERROR("Expected gfn %"PRIx64" in slot %d, but found gfn %lx\n", req.u.mem_paging.gfn, slot, paging->slot_to_gfn[slot]); goto out; } if ( req.u.mem_paging.flags & MEM_PAGING_DROP_PAGE ) { DPRINTF("drop_page ^ gfn %"PRIx64" pageslot %d\n", req.u.mem_paging.gfn, slot); /* Notify policy of page being dropped */ policy_notify_dropped(req.u.mem_paging.gfn); } else { /* Populate the page */ if ( xenpaging_populate_page(paging, req.u.mem_paging.gfn, slot) < 0 ) { ERROR("Error populating page %"PRIx64"", req.u.mem_paging.gfn); goto out; } } /* Prepare the response */ rsp.u.mem_paging.gfn = req.u.mem_paging.gfn; rsp.vcpu_id = req.vcpu_id; rsp.flags = req.flags; if ( xenpaging_resume_page(paging, &rsp, 1) < 0 ) { PERROR("Error resuming page %"PRIx64"", req.u.mem_paging.gfn); goto out; } /* Clear this pagefile slot */ paging->slot_to_gfn[slot] = 0; /* Record this free slot */ paging->free_slot_stack[paging->stack_count++] = slot; } else { DPRINTF("page %s populated (domain = %d; vcpu = %d;" " gfn = %"PRIx64"; paused = %d; evict_fail = %d)\n", req.u.mem_paging.flags & MEM_PAGING_EVICT_FAIL ? "not" : "already", paging->vm_event.domain_id, req.vcpu_id, req.u.mem_paging.gfn, !!(req.flags & VM_EVENT_FLAG_VCPU_PAUSED) , !!(req.u.mem_paging.flags & MEM_PAGING_EVICT_FAIL) ); /* Tell Xen to resume the vcpu */ if (( req.flags & VM_EVENT_FLAG_VCPU_PAUSED ) || ( req.u.mem_paging.flags & MEM_PAGING_EVICT_FAIL )) { /* Prepare the response */ rsp.u.mem_paging.gfn = req.u.mem_paging.gfn; rsp.vcpu_id = req.vcpu_id; rsp.flags = req.flags; if ( xenpaging_resume_page(paging, &rsp, 0) < 0 ) { PERROR("Error resuming page %"PRIx64"", req.u.mem_paging.gfn); goto out; } } } } /* If interrupted, write all pages back into the guest */ if ( interrupted == SIGTERM || interrupted == SIGINT ) { /* If no more pages to process, exit loop. */ if ( !paging->num_paged_out ) break; /* One more round if there are still pages to process. */ resume_pages(paging, paging->num_paged_out); /* Resume main loop */ continue; } /* Exit main loop on any other signal */ if ( interrupted ) break; /* Indicate possible error */ rc = 1; /* Check if the target has been reached already */ tot_pages = xenpaging_get_tot_pages(paging); if ( tot_pages < 0 ) goto out; /* Resume all pages if paging is disabled or no target was set */ if ( paging->target_tot_pages == 0 ) { if ( paging->num_paged_out ) resume_pages(paging, paging->num_paged_out); } /* Evict more pages if target not reached */ else if ( tot_pages > paging->target_tot_pages ) { num = tot_pages - paging->target_tot_pages; if ( num != prev_num ) { DPRINTF("Need to evict %d pages to reach %d target_tot_pages\n", num, paging->target_tot_pages); prev_num = num; } /* Limit the number of evicts to be able to process page-in requests */ if ( num > 42 ) { paging->use_poll_timeout = 0; num = 42; } if ( evict_pages(paging, num) < 0 ) goto out; } /* Resume some pages if target not reached */ else if ( tot_pages < paging->target_tot_pages && paging->num_paged_out ) { num = paging->target_tot_pages - tot_pages; if ( num != prev_num ) { DPRINTF("Need to resume %d pages to reach %d target_tot_pages\n", num, paging->target_tot_pages); prev_num = num; } resume_pages(paging, num); } /* Now target was reached, enable poll() timeout */ else { paging->use_poll_timeout = 1; } } /* No error */ rc = 0; DPRINTF("xenpaging got signal %d\n", interrupted); out: close(paging->fd); unlink_pagefile(); /* Tear down domain paging */ xenpaging_teardown(paging); return rc ? 2 : 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xenpaging/Makefile0000664000175000017500000000203013256712137015230 0ustar smbsmbXEN_ROOT=$(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk # xenpaging.c and file_ops.c incorrectly use libxc internals CFLAGS += $(CFLAGS_libxentoollog) $(CFLAGS_libxenevtchn) $(CFLAGS_libxenctrl) $(CFLAGS_libxenstore) $(PTHREAD_CFLAGS) -I$(XEN_ROOT)/tools/libxc $(CFLAGS_libxencall) LDLIBS += $(LDLIBS_libxentoollog) $(LDLIBS_libxenevtchn) $(LDLIBS_libxenctrl) $(LDLIBS_libxenstore) $(PTHREAD_LIBS) LDFLAGS += $(PTHREAD_LDFLAGS) POLICY = default SRC := SRCS += file_ops.c xenpaging.c policy_$(POLICY).c SRCS += pagein.c CFLAGS += -Werror CFLAGS += -Wno-unused OBJS = $(SRCS:.c=.o) IBINS = xenpaging all: $(IBINS) xenpaging: $(OBJS) $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(APPEND_LDFLAGS) install: all $(INSTALL_DIR) -m 0700 $(DESTDIR)$(XEN_PAGING_DIR) $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN) $(INSTALL_PROG) $(IBINS) $(DESTDIR)$(LIBEXEC_BIN) clean: rm -f *.o *~ $(DEPS) xen TAGS $(IBINS) $(LIB) distclean: clean .PHONY: clean install distclean .PHONY: TAGS TAGS: etags -t $(SRCS) *.h -include $(DEPS) xen-4.9.2/tools/xenpaging/pagein.c0000664000175000017500000000375313256712137015214 0ustar smbsmb/* Trigger a page-in in a separate thread-of-execution to avoid deadlock */ #include #include "xenpaging.h" struct page_in_args { domid_t dom; unsigned long *pagein_queue; xc_interface *xch; }; static struct page_in_args page_in_args; static unsigned long page_in_request; static unsigned int page_in_possible; static pthread_t page_in_thread; static pthread_cond_t page_in_cond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t page_in_mutex = PTHREAD_MUTEX_INITIALIZER; static void *page_in(void *arg) { struct page_in_args *pia = arg; void *page; int i, num; xen_pfn_t gfns[XENPAGING_PAGEIN_QUEUE_SIZE]; while (1) { pthread_mutex_lock(&page_in_mutex); while (!page_in_request) pthread_cond_wait(&page_in_cond, &page_in_mutex); num = 0; for (i = 0; i < XENPAGING_PAGEIN_QUEUE_SIZE; i++) { if (!pia->pagein_queue[i]) continue; gfns[num] = pia->pagein_queue[i]; pia->pagein_queue[i] = 0; num++; } page_in_request = 0; pthread_mutex_unlock(&page_in_mutex); /* Ignore errors */ page = xc_map_foreign_pages(pia->xch, pia->dom, PROT_READ, gfns, num); if (page) munmap(page, PAGE_SIZE * num); } page_in_possible = 0; pthread_exit(NULL); } void page_in_trigger(void) { if (!page_in_possible) return; pthread_mutex_lock(&page_in_mutex); page_in_request = 1; pthread_mutex_unlock(&page_in_mutex); pthread_cond_signal(&page_in_cond); } void create_page_in_thread(struct xenpaging *paging) { page_in_args.dom = paging->vm_event.domain_id; page_in_args.pagein_queue = paging->pagein_queue; page_in_args.xch = paging->xc_handle; if (pthread_create(&page_in_thread, NULL, page_in, &page_in_args) == 0) page_in_possible = 1; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/ocaml/0000775000175000017500000000000013256712137012710 5ustar smbsmbxen-4.9.2/tools/ocaml/test/0000775000175000017500000000000013256712137013667 5ustar smbsmbxen-4.9.2/tools/ocaml/test/dmesg.ml0000664000175000017500000000063713256712137015326 0ustar smbsmbopen Printf let _ = Xenlight.register_exceptions (); let logger = Xtl.create_stdio_logger ~level:Xentoollog.Debug () in let ctx = Xenlight.ctx_alloc logger in let open Xenlight.Host in let reader = xen_console_read_start ctx 0 in (try while true do let line = xen_console_read_line ctx reader in print_string line done with End_of_file -> ()); let _ = xen_console_read_finish ctx reader in () xen-4.9.2/tools/ocaml/test/Makefile0000664000175000017500000000326613256712137015336 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. OCAML_TOPLEVEL = $(CURDIR)/.. include $(OCAML_TOPLEVEL)/common.make CFLAGS += $(APPEND_CFLAGS) OCAMLINCLUDE += \ -I $(OCAML_TOPLEVEL)/libs/xentoollog \ -I $(OCAML_TOPLEVEL)/libs/xl OBJS = xtl send_debug_keys list_domains raise_exception dmesg PROGRAMS = xtl send_debug_keys list_domains raise_exception dmesg xtl_LIBS = \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xentoollog $(OCAML_TOPLEVEL)/libs/xentoollog/xentoollog.cmxa \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xl $(OCAML_TOPLEVEL)/libs/xl/xenlight.cmxa xtl_OBJS = xtl send_debug_keys_LIBS = \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xentoollog $(OCAML_TOPLEVEL)/libs/xentoollog/xentoollog.cmxa \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xl $(OCAML_TOPLEVEL)/libs/xl/xenlight.cmxa send_debug_keys_OBJS = xtl send_debug_keys list_domains_LIBS = \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xentoollog $(OCAML_TOPLEVEL)/libs/xentoollog/xentoollog.cmxa \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xl $(OCAML_TOPLEVEL)/libs/xl/xenlight.cmxa list_domains_OBJS = xtl list_domains raise_exception_LIBS = \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xentoollog $(OCAML_TOPLEVEL)/libs/xentoollog/xentoollog.cmxa \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xl $(OCAML_TOPLEVEL)/libs/xl/xenlight.cmxa raise_exception_OBJS = raise_exception dmesg_LIBS = \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xentoollog $(OCAML_TOPLEVEL)/libs/xentoollog/xentoollog.cmxa \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xl $(OCAML_TOPLEVEL)/libs/xl/xenlight.cmxa dmesg_OBJS = xtl dmesg OCAML_PROGRAM = xtl send_debug_keys list_domains raise_exception dmesg all: $(PROGRAMS) bins: $(PROGRAMS) install: include $(OCAML_TOPLEVEL)/Makefile.rules xen-4.9.2/tools/ocaml/test/raise_exception.ml0000664000175000017500000000033013256712137017376 0ustar smbsmbopen Printf open Xentoollog open Xenlight let _ = try Xenlight.test_raise_exception () with Xenlight.Error(err, fn) -> begin printf "Caught Exception: %s: %s\n" (Xenlight.string_of_error err) fn; end xen-4.9.2/tools/ocaml/test/send_debug_keys.ml0000664000175000017500000000047213256712137017356 0ustar smbsmbopen Arg open Printf open Xenlight let send_keys ctx s = printf "Sending debug key %s\n" s; Xenlight.Host.send_debug_keys ctx s; () let _ = let logger = Xtl.create_stdio_logger () in let ctx = Xenlight.ctx_alloc logger in Arg.parse [ ] (fun s -> send_keys ctx s) "usage: send_debug_keys " xen-4.9.2/tools/ocaml/test/list_domains.ml0000664000175000017500000000171213256712137016707 0ustar smbsmbopen Arg open Printf open Xenlight let bool_as_char b c = if b then c else '-' let print_dominfo dominfo = let id = dominfo.Xenlight.Dominfo.domid and running = bool_as_char dominfo.Xenlight.Dominfo.running 'r' and blocked = bool_as_char dominfo.Xenlight.Dominfo.blocked 'b' and paused = bool_as_char dominfo.Xenlight.Dominfo.paused 'p' and shutdown = bool_as_char dominfo.Xenlight.Dominfo.shutdown 's' and dying = bool_as_char dominfo.Xenlight.Dominfo.dying 'd' and memory = dominfo.Xenlight.Dominfo.current_memkb in printf "Dom %d: %c%c%c%c%c %LdKB\n" id running blocked paused shutdown dying memory let _ = let logger = Xtl.create_stdio_logger (*~level:Xentoollog.Debug*) () in let ctx = Xenlight.ctx_alloc logger in try let domains = Xenlight.Dominfo.list ctx in List.iter (fun d -> print_dominfo d) domains with Xenlight.Error(err, fn) -> begin printf "Caught Exception: %s: %s\n" (Xenlight.string_of_error err) fn; end xen-4.9.2/tools/ocaml/test/xtl.ml0000664000175000017500000000220713256712137015031 0ustar smbsmbopen Arg open Printf open Xentoollog let stdio_vmessage min_level level errno ctx msg = let level_str = level_to_string level and errno_str = match errno with None -> "" | Some s -> sprintf ": errno=%d" s and ctx_str = match ctx with None -> "" | Some s -> sprintf ": %s" s in if compare min_level level <= 0 then begin printf "%s%s%s: %s\n" level_str ctx_str errno_str msg; flush stdout; end let stdio_progress ctx what percent dne total = let nl = if dne = total then "\n" else "" in printf "\rProgress %s %d%% (%Ld/%Ld)%s" what percent dne total nl; flush stdout let create_stdio_logger ?(level=Info) () = let cbs = { vmessage = stdio_vmessage level; progress = stdio_progress; } in create "Xentoollog.stdio_logger" cbs let do_test level = let lgr = create_stdio_logger ~level:level () in begin test lgr; end let () = let debug_level = ref Info in let speclist = [ ("-v", Arg.Unit (fun () -> debug_level := Debug), "Verbose"); ("-q", Arg.Unit (fun () -> debug_level := Critical), "Quiet"); ] in let usage_msg = "usage: xtl [OPTIONS]" in Arg.parse speclist (fun s -> ()) usage_msg; do_test !debug_level xen-4.9.2/tools/ocaml/xenstored/0000775000175000017500000000000013256712137014723 5ustar smbsmbxen-4.9.2/tools/ocaml/xenstored/syslog.mli0000664000175000017500000000173613256712137016755 0ustar smbsmb(* * Copyright (C) 2006-2009 Citrix Systems Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type level = Emerg | Alert | Crit | Err | Warning | Notice | Info | Debug type facility = Auth | Authpriv | Cron | Daemon | Ftp | Kern | Local0 | Local1 | Local2 | Local3 | Local4 | Local5 | Local6 | Local7 | Lpr | Mail | News | Syslog | User | Uucp external log : facility -> level -> string -> unit = "stub_syslog" val facility_of_string : string -> facility xen-4.9.2/tools/ocaml/xenstored/oxenstored.conf.in0000664000175000017500000000502213256712137020370 0ustar smbsmb# default xenstored config # Where the pid file is stored pid-file = @XEN_RUN_DIR@/xenstored.pid # Randomly failed a transaction with EAGAIN. Used for testing Xs user test-eagain = false # Activate transaction merge support merge-activate = true # Limits applied to domains whose writes cause other domains' transaction # commits to fail. Must include decimal point. # The burst limit is the number of conflicts a domain can cause to # fail in a short period; this value is used for both the initial and # the maximum value of each domain's conflict-credit, which falls by # one point for each conflict caused, and when it reaches zero the # domain's requests are ignored. conflict-burst-limit = 5.0 # The conflict-credit is replenished over time: # one point is issued after each conflict-max-history-seconds, so this # is the minimum pause-time during which a domain will be ignored. conflict-max-history-seconds = 0.05 # If the conflict-rate-limit-is-aggregate flag is true then after each # tick one point of conflict-credit is given to just one domain: the # one at the front of the queue. If false, then after each tick each # domain gets a point of conflict-credit. # # In environments where it is known that every transaction will # involve a set of nodes that is writable by at most one other domain, # then it is safe to set this aggregate-limit flag to false for better # performance. (This can be determined by considering the layout of # the xenstore tree and permissions, together with the content of the # transactions that require protection.) # # A transaction which involves a set of nodes which can be modified by # multiple other domains can suffer conflicts caused by any of those # domains, so the flag must be set to true. conflict-rate-limit-is-aggregate = true # Activate node permission system perms-activate = true # Activate quota quota-activate = true quota-maxentity = 1000 quota-maxsize = 2048 quota-maxwatch = 100 quota-transaction = 10 quota-maxrequests = 1024 # Activate filed base backend persistent = false # Xenstored logs # xenstored-log-file = @XEN_LOG_DIR@/xenstored.log # xenstored-log-level = null # xenstored-log-nb-files = 10 # Xenstored access logs # access-log-file = @XEN_LOG_DIR@/xenstored-access.log # access-log-nb-lines = 13215 # acesss-log-nb-chars = 180 # access-log-special-ops = false # Perodically scanning all the rings as a safenet for lazy clients. # Define the interval in seconds, set to negative to disable. # ring-scan-interval = 20 xenstored-kva = @XENSTORED_KVA@ xenstored-port = @XENSTORED_PORT@ xen-4.9.2/tools/ocaml/xenstored/process.ml0000664000175000017500000005027213256712137016741 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) let error fmt = Logging.error "process" fmt let info fmt = Logging.info "process" fmt let debug fmt = Logging.debug "process" fmt open Printf open Stdext exception Transaction_again exception Transaction_nested exception Domain_not_match exception Invalid_Cmd_Args (* This controls the do_debug fn in this module, not the debug logging-function. *) let allow_debug = ref false let c_int_of_string s = let v = ref 0 in let is_digit c = c >= '0' && c <= '9' in let len = String.length s in let i = ref 0 in while !i < len && not (is_digit s.[!i]) do incr i done; while !i < len && is_digit s.[!i] do let x = (Char.code s.[!i]) - (Char.code '0') in v := !v * 10 + x; incr i done; !v (* when we don't want a limit, apply a max limit of 8 arguments. no arguments take more than 3 currently, which is pointless to split more than needed. *) let split limit c s = let limit = match limit with None -> 8 | Some x -> x in String.split ~limit c s let split_one_path data con = let args = split (Some 2) '\000' data in match args with | path :: "" :: [] -> Store.Path.create path (Connection.get_path con) | _ -> raise Invalid_Cmd_Args let process_watch ops cons = let do_op_watch op cons = let recurse = match (fst op) with | Xenbus.Xb.Op.Write -> false | Xenbus.Xb.Op.Mkdir -> false | Xenbus.Xb.Op.Rm -> true | Xenbus.Xb.Op.Setperms -> false | _ -> raise (Failure "huh ?") in Connections.fire_watches cons (snd op) recurse in List.iter (fun op -> do_op_watch op cons) ops let create_implicit_path t perm path = let dirname = Store.Path.get_parent path in if not (Transaction.path_exists t dirname) then ( let rec check_path p = match p with | [] -> [] | h :: l -> if Transaction.path_exists t h then check_path l else p in let ret = check_path (List.tl (Store.Path.get_hierarchy dirname)) in List.iter (fun s -> Transaction.mkdir ~with_watch:false t perm s) ret ) (* packets *) let do_debug con t domains cons data = if not (Connection.is_dom0 con) && not !allow_debug then None else try match split None '\000' data with | "print" :: msg :: _ -> Logging.xb_op ~tid:0 ~ty:Xenbus.Xb.Op.Debug ~con:"=======>" msg; None | "quota" :: domid :: _ -> let domid = int_of_string domid in let quota = (Store.get_quota t.Transaction.store) in Some (Quota.to_string quota domid ^ "\000") | "watches" :: _ -> let watches = Connections.debug cons in Some (watches ^ "\000") | "mfn" :: domid :: _ -> let domid = int_of_string domid in let con = Connections.find_domain cons domid in may (fun dom -> Printf.sprintf "%nd\000" (Domain.get_mfn dom)) (Connection.get_domain con) | _ -> None with _ -> None let do_directory con t domains cons data = let path = split_one_path data con in let entries = Transaction.ls t (Connection.get_perm con) path in if List.length entries > 0 then (Utils.join_by_null entries) ^ "\000" else "" let do_read con t domains cons data = let path = split_one_path data con in Transaction.read t (Connection.get_perm con) path let do_getperms con t domains cons data = let path = split_one_path data con in let perms = Transaction.getperms t (Connection.get_perm con) path in Perms.Node.to_string perms ^ "\000" let do_getdomainpath con t domains cons data = let domid = match (split None '\000' data) with | domid :: "" :: [] -> c_int_of_string domid | _ -> raise Invalid_Cmd_Args in sprintf "/local/domain/%u\000" domid let do_write con t domains cons data = let path, value = match (split (Some 2) '\000' data) with | path :: value :: [] -> Store.Path.create path (Connection.get_path con), value | _ -> raise Invalid_Cmd_Args in create_implicit_path t (Connection.get_perm con) path; Transaction.write t (Connection.get_perm con) path value let do_mkdir con t domains cons data = let path = split_one_path data con in create_implicit_path t (Connection.get_perm con) path; try Transaction.mkdir t (Connection.get_perm con) path with Define.Already_exist -> () let do_rm con t domains cons data = let path = split_one_path data con in try Transaction.rm t (Connection.get_perm con) path with Define.Doesnt_exist -> () let do_setperms con t domains cons data = let path, perms = match (split (Some 2) '\000' data) with | path :: perms :: _ -> Store.Path.create path (Connection.get_path con), (Perms.Node.of_string perms) | _ -> raise Invalid_Cmd_Args in Transaction.setperms t (Connection.get_perm con) path perms let do_error con t domains cons data = raise Define.Unknown_operation let do_isintroduced con t domains cons data = let domid = match (split None '\000' data) with | domid :: _ -> int_of_string domid | _ -> raise Invalid_Cmd_Args in if domid = Define.domid_self || Domains.exist domains domid then "T\000" else "F\000" (* only in xen >= 4.2 *) let do_reset_watches con t domains cons data = Connection.del_watches con; Connection.del_transactions con (* only in >= xen3.3 *) let do_set_target con t domains cons data = if not (Connection.is_dom0 con) then raise Define.Permission_denied; match split None '\000' data with | [ domid; target_domid; "" ] -> Connections.set_target cons (c_int_of_string domid) (c_int_of_string target_domid) | _ -> raise Invalid_Cmd_Args (*------------- Generic handling of ty ------------------*) let send_response ty con t rid response = match response with | Packet.Ack f -> Connection.send_ack con (Transaction.get_id t) rid ty; (* Now do any necessary follow-up actions *) f () | Packet.Reply ret -> Connection.send_reply con (Transaction.get_id t) rid ty ret | Packet.Error e -> Connection.send_error con (Transaction.get_id t) rid e let reply_ack fct con t doms cons data = fct con t doms cons data; Packet.Ack (fun () -> if Transaction.get_id t = Transaction.none then process_watch (Transaction.get_paths t) cons ) let reply_data fct con t doms cons data = let ret = fct con t doms cons data in Packet.Reply ret let reply_data_or_ack fct con t doms cons data = match fct con t doms cons data with | Some ret -> Packet.Reply ret | None -> Packet.Ack (fun () -> ()) let reply_none fct con t doms cons data = (* let the function reply *) fct con t doms cons data (* Functions for 'simple' operations that cannot be part of a transaction *) let function_of_type_simple_op ty = match ty with | Xenbus.Xb.Op.Debug | Xenbus.Xb.Op.Watch | Xenbus.Xb.Op.Unwatch | Xenbus.Xb.Op.Transaction_start | Xenbus.Xb.Op.Transaction_end | Xenbus.Xb.Op.Introduce | Xenbus.Xb.Op.Release | Xenbus.Xb.Op.Isintroduced | Xenbus.Xb.Op.Resume | Xenbus.Xb.Op.Set_target | Xenbus.Xb.Op.Reset_watches | Xenbus.Xb.Op.Invalid -> error "called function_of_type_simple_op on operation %s" (Xenbus.Xb.Op.to_string ty); raise (Invalid_argument (Xenbus.Xb.Op.to_string ty)) | Xenbus.Xb.Op.Directory -> reply_data do_directory | Xenbus.Xb.Op.Read -> reply_data do_read | Xenbus.Xb.Op.Getperms -> reply_data do_getperms | Xenbus.Xb.Op.Getdomainpath -> reply_data do_getdomainpath | Xenbus.Xb.Op.Write -> reply_ack do_write | Xenbus.Xb.Op.Mkdir -> reply_ack do_mkdir | Xenbus.Xb.Op.Rm -> reply_ack do_rm | Xenbus.Xb.Op.Setperms -> reply_ack do_setperms | _ -> reply_ack do_error let input_handle_error ~cons ~doms ~fct ~con ~t ~req = let reply_error e = Packet.Error e in try fct con t doms cons req.Packet.data with | Define.Invalid_path -> reply_error "EINVAL" | Define.Already_exist -> reply_error "EEXIST" | Define.Doesnt_exist -> reply_error "ENOENT" | Define.Lookup_Doesnt_exist s -> reply_error "ENOENT" | Define.Permission_denied -> reply_error "EACCES" | Not_found -> reply_error "ENOENT" | Invalid_Cmd_Args -> reply_error "EINVAL" | Invalid_argument i -> reply_error "EINVAL" | Transaction_again -> reply_error "EAGAIN" | Transaction_nested -> reply_error "EBUSY" | Domain_not_match -> reply_error "EINVAL" | Quota.Limit_reached -> reply_error "EQUOTA" | Quota.Data_too_big -> reply_error "E2BIG" | Quota.Transaction_opened -> reply_error "EQUOTA" | (Failure "int_of_string") -> reply_error "EINVAL" | Define.Unknown_operation -> reply_error "ENOSYS" let write_access_log ~ty ~tid ~con ~data = Logging.xb_op ~ty ~tid ~con data let write_answer_log ~ty ~tid ~con ~data = Logging.xb_answer ~ty ~tid ~con data let write_response_log ~ty ~tid ~con ~response = match response with | Packet.Ack _ -> write_answer_log ~ty ~tid ~con ~data:"" | Packet.Reply x -> write_answer_log ~ty ~tid ~con ~data:x | Packet.Error e -> write_answer_log ~ty:(Xenbus.Xb.Op.Error) ~tid ~con ~data:e let record_commit ~con ~tid ~before ~after = let inc r = r := Int64.add 1L !r in let finish_count = inc Transaction.counter; !Transaction.counter in History.push {History.con=con; tid=tid; before=before; after=after; finish_count=finish_count} (* Replay a stored transaction against a fresh store, check the responses are all equivalent: if so, commit the transaction. Otherwise send the abort to the client. *) let transaction_replay c t doms cons = match t.Transaction.ty with | Transaction.No -> error "attempted to replay a non-full transaction"; false | Transaction.Full(id, oldstore, cstore) -> let tid = Connection.start_transaction c cstore in let replay_t = Transaction.make ~internal:true tid cstore in let con = sprintf "r(%d):%s" id (Connection.get_domstr c) in let perform_exn ~wlog txn (request, response) = if wlog then write_access_log ~ty:request.Packet.ty ~tid ~con ~data:request.Packet.data; let fct = function_of_type_simple_op request.Packet.ty in let response' = input_handle_error ~cons ~doms ~fct ~con:c ~t:txn ~req:request in if wlog then write_response_log ~ty:request.Packet.ty ~tid ~con ~response:response'; if not(Packet.response_equal response response') then raise Transaction_again in finally (fun () -> try Logging.start_transaction ~con ~tid; List.iter (perform_exn ~wlog:true replay_t) (Transaction.get_operations t); (* May throw EAGAIN *) Logging.end_transaction ~con ~tid; Transaction.commit ~con replay_t with | Transaction_again -> ( Transaction.failed_commits := Int64.add !Transaction.failed_commits 1L; let victim_domstr = Connection.get_domstr c in debug "Apportioning blame for EAGAIN in txn %d, domain=%s" id victim_domstr; let punish guilty_con = debug "Blaming domain %s for conflict with domain %s txn %d" (Connection.get_domstr guilty_con) victim_domstr id; Connection.decr_conflict_credit doms guilty_con in let judge_and_sentence hist_rec = ( let can_apply_on store = ( let store = Store.copy store in let trial_t = Transaction.make ~internal:true Transaction.none store in try List.iter (perform_exn ~wlog:false trial_t) (Transaction.get_operations t); true with Transaction_again -> false ) in if can_apply_on hist_rec.History.before && not (can_apply_on hist_rec.History.after) then (punish hist_rec.History.con; true) else false ) in let guilty_cons = History.filter_connections ~ignore:c ~since:t.Transaction.start_count ~f:judge_and_sentence in if Hashtbl.length guilty_cons = 0 then ( debug "Found no culprit for conflict in %s: must be self or not in history." con; Transaction.failed_commits_no_culprit := Int64.add !Transaction.failed_commits_no_culprit 1L ); false ) | e -> info "transaction_replay %d caught: %s" tid (Printexc.to_string e); false ) (fun () -> Connection.end_transaction c tid None ) let do_watch con t domains cons data = let (node, token) = match (split None '\000' data) with | [node; token; ""] -> node, token | _ -> raise Invalid_Cmd_Args in let watch = Connections.add_watch cons con node token in Packet.Ack (fun () -> Connection.fire_single_watch watch) let do_unwatch con t domains cons data = let (node, token) = match (split None '\000' data) with | [node; token; ""] -> node, token | _ -> raise Invalid_Cmd_Args in Connections.del_watch cons con node token let do_transaction_start con t domains cons data = if Transaction.get_id t <> Transaction.none then raise Transaction_nested; let store = Transaction.get_store t in string_of_int (Connection.start_transaction con store) ^ "\000" let do_transaction_end con t domains cons data = let commit = match (split None '\000' data) with | "T" :: _ -> true | "F" :: _ -> false | x :: _ -> raise (Invalid_argument x) | _ -> raise Invalid_Cmd_Args in let commit = commit && not (Transaction.is_read_only t) in let success = let commit = if commit then Some (fun con trans -> transaction_replay con trans domains cons) else None in History.end_transaction t con (Transaction.get_id t) commit in if not success then raise Transaction_again; if commit then begin process_watch (List.rev (Transaction.get_paths t)) cons; match t.Transaction.ty with | Transaction.No -> () (* no need to record anything *) | Transaction.Full(id, oldstore, cstore) -> record_commit ~con ~tid:id ~before:oldstore ~after:cstore end let do_introduce con t domains cons data = if not (Connection.is_dom0 con) then raise Define.Permission_denied; let (domid, mfn, port) = match (split None '\000' data) with | domid :: mfn :: port :: _ -> int_of_string domid, Nativeint.of_string mfn, int_of_string port | _ -> raise Invalid_Cmd_Args; in let dom = if Domains.exist domains domid then Domains.find domains domid else try let ndom = Xenctrl.with_intf (fun xc -> Domains.create xc domains domid mfn port) in Connections.add_domain cons ndom; Connections.fire_spec_watches cons "@introduceDomain"; ndom with _ -> raise Invalid_Cmd_Args in if (Domain.get_remote_port dom) <> port || (Domain.get_mfn dom) <> mfn then raise Domain_not_match let do_release con t domains cons data = if not (Connection.is_dom0 con) then raise Define.Permission_denied; let domid = match (split None '\000' data) with | [domid;""] -> int_of_string domid | _ -> raise Invalid_Cmd_Args in let fire_spec_watches = Domains.exist domains domid in Domains.del domains domid; Connections.del_domain cons domid; if fire_spec_watches then Connections.fire_spec_watches cons "@releaseDomain" else raise Invalid_Cmd_Args let do_resume con t domains cons data = if not (Connection.is_dom0 con) then raise Define.Permission_denied; let domid = match (split None '\000' data) with | domid :: _ -> int_of_string domid | _ -> raise Invalid_Cmd_Args in if Domains.exist domains domid then Domains.resume domains domid else raise Invalid_Cmd_Args let function_of_type ty = match ty with | Xenbus.Xb.Op.Debug -> reply_data_or_ack do_debug | Xenbus.Xb.Op.Watch -> reply_none do_watch | Xenbus.Xb.Op.Unwatch -> reply_ack do_unwatch | Xenbus.Xb.Op.Transaction_start -> reply_data do_transaction_start | Xenbus.Xb.Op.Transaction_end -> reply_ack do_transaction_end | Xenbus.Xb.Op.Introduce -> reply_ack do_introduce | Xenbus.Xb.Op.Release -> reply_ack do_release | Xenbus.Xb.Op.Isintroduced -> reply_data do_isintroduced | Xenbus.Xb.Op.Resume -> reply_ack do_resume | Xenbus.Xb.Op.Set_target -> reply_ack do_set_target | Xenbus.Xb.Op.Reset_watches -> reply_ack do_reset_watches | Xenbus.Xb.Op.Invalid -> reply_ack do_error | _ -> function_of_type_simple_op ty (** * Determines which individual (non-transactional) operations we want to retain. * We only want to retain operations that have side-effects in the store since * these can be the cause of transactions failing. *) let retain_op_in_history ty = match ty with | Xenbus.Xb.Op.Write | Xenbus.Xb.Op.Mkdir | Xenbus.Xb.Op.Rm | Xenbus.Xb.Op.Setperms -> true | Xenbus.Xb.Op.Debug | Xenbus.Xb.Op.Directory | Xenbus.Xb.Op.Read | Xenbus.Xb.Op.Getperms | Xenbus.Xb.Op.Watch | Xenbus.Xb.Op.Unwatch | Xenbus.Xb.Op.Transaction_start | Xenbus.Xb.Op.Transaction_end | Xenbus.Xb.Op.Introduce | Xenbus.Xb.Op.Release | Xenbus.Xb.Op.Getdomainpath | Xenbus.Xb.Op.Watchevent | Xenbus.Xb.Op.Error | Xenbus.Xb.Op.Isintroduced | Xenbus.Xb.Op.Resume | Xenbus.Xb.Op.Set_target | Xenbus.Xb.Op.Reset_watches | Xenbus.Xb.Op.Invalid -> false (** * Nothrow guarantee. *) let process_packet ~store ~cons ~doms ~con ~req = let ty = req.Packet.ty in let tid = req.Packet.tid in let rid = req.Packet.rid in try let fct = function_of_type ty in let t = if tid = Transaction.none then Transaction.make tid store else Connection.get_transaction con tid in let execute () = input_handle_error ~cons ~doms ~fct ~con ~t ~req in let response = (* Note that transactions are recorded in history separately. *) if tid = Transaction.none && retain_op_in_history ty then begin let before = Store.copy store in let response = execute () in let after = Store.copy store in record_commit ~con ~tid ~before ~after; response end else execute () in let response = try if tid <> Transaction.none then (* Remember the request and response for this operation in case we need to replay the transaction *) Transaction.add_operation ~perm:(Connection.get_perm con) t req response; response with Quota.Limit_reached -> Packet.Error "EQUOTA" in (* Put the response on the wire *) send_response ty con t rid response with exn -> error "process packet: %s" (Printexc.to_string exn); Connection.send_error con tid rid "EIO" let do_input store cons doms con = let newpacket = try Connection.do_input con with Xenbus.Xb.Reconnect -> info "%s requests a reconnect" (Connection.get_domstr con); Connection.reconnect con; info "%s reconnection complete" (Connection.get_domstr con); false | Failure exp -> error "caught exception %s" exp; error "got a bad client %s" (sprintf "%-8s" (Connection.get_domstr con)); Connection.mark_as_bad con; false in if newpacket then ( let packet = Connection.pop_in con in let tid, rid, ty, data = Xenbus.Xb.Packet.unpack packet in let req = {Packet.tid=tid; Packet.rid=rid; Packet.ty=ty; Packet.data=data} in (* As we don't log IO, do not call an unnecessary sanitize_data info "[%s] -> [%d] %s \"%s\"" (Connection.get_domstr con) tid (Xenbus.Xb.Op.to_string ty) (sanitize_data data); *) process_packet ~store ~cons ~doms ~con ~req; write_access_log ~ty ~tid ~con:(Connection.get_domstr con) ~data; Connection.incr_ops con; ) let do_output store cons doms con = if Connection.has_output con then ( if Connection.has_new_output con then ( let packet = Connection.peek_output con in let tid, rid, ty, data = Xenbus.Xb.Packet.unpack packet in (* As we don't log IO, do not call an unnecessary sanitize_data info "[%s] <- %s \"%s\"" (Connection.get_domstr con) (Xenbus.Xb.Op.to_string ty) (sanitize_data data);*) write_answer_log ~ty ~tid ~con:(Connection.get_domstr con) ~data; ); try ignore (Connection.do_output con) with Xenbus.Xb.Reconnect -> info "%s requests a reconnect" (Connection.get_domstr con); Connection.reconnect con; info "%s reconnection complete" (Connection.get_domstr con) ) xen-4.9.2/tools/ocaml/xenstored/stdext.ml0000664000175000017500000000725613256712137016602 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008-2010 Citrix Ltd. * Author Vincent Hanquez * Author Dave Scott * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type ('a, 'b) either = Right of 'a | Left of 'b (** apply the clean_f function after fct function has been called. * Even if fct raises an exception, clean_f is applied *) let exnhook = ref None let finally fct clean_f = let result = try fct (); with exn -> (match !exnhook with None -> () | Some f -> f exn); clean_f (); raise exn in clean_f (); result (** if v is not none, apply f on it and return some value else return none. *) let may f v = match v with Some x -> Some (f x) | None -> None (** default value to d if v is none. *) let default d v = match v with Some x -> x | None -> d (** apply f on v if not none *) let maybe f v = match v with None -> () | Some x -> f x module String = struct include String let of_char c = String.make 1 c let rec split ?limit:(limit=(-1)) c s = let i = try String.index s c with Not_found -> -1 in let nlimit = if limit = -1 || limit = 0 then limit else limit - 1 in if i = -1 || nlimit = 0 then [ s ] else let a = String.sub s 0 i and b = String.sub s (i + 1) (String.length s - i - 1) in a :: (split ~limit: nlimit c b) let fold_left f accu string = let accu = ref accu in for i = 0 to length string - 1 do accu := f !accu string.[i] done; !accu (** True if string 'x' starts with prefix 'prefix' *) let startswith prefix x = let x_l = String.length x and prefix_l = String.length prefix in prefix_l <= x_l && String.sub x 0 prefix_l = prefix end module Unixext = struct (** remove a file, but doesn't raise an exception if the file is already removed *) let unlink_safe file = try Unix.unlink file with (* Unix.Unix_error (Unix.ENOENT, _ , _)*) _ -> () (** create a directory but doesn't raise an exception if the directory already exist *) let mkdir_safe dir perm = try Unix.mkdir dir perm with Unix.Unix_error (Unix.EEXIST, _, _) -> () (** create a directory, and create parent if doesn't exist *) let mkdir_rec dir perm = let rec p_mkdir dir = let p_name = Filename.dirname dir in if p_name <> "/" && p_name <> "." then p_mkdir p_name; mkdir_safe dir perm in p_mkdir dir (** daemonize a process *) (* !! Must call this before spawning any threads !! *) let daemonize () = match Unix.fork () with | 0 -> if Unix.setsid () == -1 then failwith "Unix.setsid failed"; begin match Unix.fork () with | 0 -> let nullfd = Unix.openfile "/dev/null" [ Unix.O_WRONLY ] 0 in begin try Unix.close Unix.stdin; Unix.dup2 nullfd Unix.stdout; Unix.dup2 nullfd Unix.stderr; with exn -> Unix.close nullfd; raise exn end; Unix.close nullfd | _ -> exit 0 end | _ -> exit 0 (** write a pidfile file *) let pidfile_write filename = let fd = Unix.openfile filename [ Unix.O_WRONLY; Unix.O_CREAT; Unix.O_TRUNC; ] 0o640 in finally (fun () -> let pid = Unix.getpid () in let buf = string_of_int pid ^ "\n" in let len = String.length buf in if Unix.write fd buf 0 len <> len then failwith "pidfile_write failed"; ) (fun () -> Unix.close fd) end xen-4.9.2/tools/ocaml/xenstored/select.mli0000664000175000017500000000235513256712137016712 0ustar smbsmb(* * Copyright (C) 2014 Zheng Li * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) (** Same interface and semantics as [Unix.select] but with an extra alternative implementation based on poll. Switching implementations is done by calling the [use_poll] function. *) val select: Unix.file_descr list -> Unix.file_descr list -> Unix.file_descr list -> float -> Unix.file_descr list * Unix.file_descr list * Unix.file_descr list (** [use_poll true] will use poll based select with max fds number limitation eliminated; [use_poll false] will use standard [Unix.select] with max fd number set to 1024; not calling this function at all equals to use the standard [Unix.select] with max fd number setting untouched. *) val use_poll: bool -> unit xen-4.9.2/tools/ocaml/xenstored/connection.ml0000664000175000017500000002101113256712137017407 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) exception End_of_file open Stdext let xenstore_payload_max = 4096 (* xen/include/public/io/xs_wire.h *) type watch = { con: t; token: string; path: string; base: string; is_relative: bool; } and t = { xb: Xenbus.Xb.t; dom: Domain.t option; transactions: (int, Transaction.t) Hashtbl.t; mutable next_tid: int; watches: (string, watch list) Hashtbl.t; mutable nb_watches: int; anonid: int; mutable stat_nb_ops: int; mutable perm: Perms.Connection.t; } let mark_as_bad con = match con.dom with |None -> () | Some domain -> Domain.mark_as_bad domain let initial_next_tid = 1 let reconnect con = Xenbus.Xb.reconnect con.xb; (* dom is the same *) Hashtbl.clear con.transactions; con.next_tid <- initial_next_tid; Hashtbl.clear con.watches; (* anonid is the same *) con.nb_watches <- 0; con.stat_nb_ops <- 0; (* perm is the same *) () let get_path con = Printf.sprintf "/local/domain/%i/" (match con.dom with None -> 0 | Some d -> Domain.get_id d) let watch_create ~con ~path ~token = { con = con; token = token; path = path; base = get_path con; is_relative = path.[0] <> '/' && path.[0] <> '@' } let get_con w = w.con let number_of_transactions con = Hashtbl.length con.transactions let get_domain con = con.dom let anon_id_next = ref 1 let get_domstr con = match con.dom with | None -> "A" ^ (string_of_int con.anonid) | Some dom -> "D" ^ (string_of_int (Domain.get_id dom)) let make_perm dom = let domid = match dom with | None -> 0 | Some d -> Domain.get_id d in Perms.Connection.create ~perms:[Perms.READ; Perms.WRITE] domid let create xbcon dom = let id = match dom with | None -> let old = !anon_id_next in incr anon_id_next; old | Some _ -> 0 in let con = { xb = xbcon; dom = dom; transactions = Hashtbl.create 5; next_tid = initial_next_tid; watches = Hashtbl.create 8; nb_watches = 0; anonid = id; stat_nb_ops = 0; perm = make_perm dom; } in Logging.new_connection ~tid:Transaction.none ~con:(get_domstr con); con let get_fd con = Xenbus.Xb.get_fd con.xb let close con = Logging.end_connection ~tid:Transaction.none ~con:(get_domstr con); Xenbus.Xb.close con.xb let get_perm con = con.perm let set_target con target_domid = con.perm <- Perms.Connection.set_target (get_perm con) ~perms:[Perms.READ; Perms.WRITE] target_domid let is_backend_mmap con = match con.xb.Xenbus.Xb.backend with | Xenbus.Xb.Xenmmap _ -> true | _ -> false let send_reply con tid rid ty data = if (String.length data) > xenstore_payload_max && (is_backend_mmap con) then Xenbus.Xb.queue con.xb (Xenbus.Xb.Packet.create tid rid Xenbus.Xb.Op.Error "E2BIG\000") else Xenbus.Xb.queue con.xb (Xenbus.Xb.Packet.create tid rid ty data) let send_error con tid rid err = send_reply con tid rid Xenbus.Xb.Op.Error (err ^ "\000") let send_ack con tid rid ty = send_reply con tid rid ty "OK\000" let get_watch_path con path = if path.[0] = '@' || path.[0] = '/' then path else let rpath = get_path con in rpath ^ path let get_watches (con: t) path = if Hashtbl.mem con.watches path then Hashtbl.find con.watches path else [] let get_children_watches con path = let path = path ^ "/" in List.concat (Hashtbl.fold (fun p w l -> if String.startswith path p then w :: l else l) con.watches []) let is_dom0 con = Perms.Connection.is_dom0 (get_perm con) let add_watch con path token = if !Quota.activate && !Define.maxwatch > 0 && not (is_dom0 con) && con.nb_watches > !Define.maxwatch then raise Quota.Limit_reached; let apath = get_watch_path con path in let l = get_watches con apath in if List.exists (fun w -> w.token = token) l then raise Define.Already_exist; let watch = watch_create ~con ~token ~path in Hashtbl.replace con.watches apath (watch :: l); con.nb_watches <- con.nb_watches + 1; apath, watch let del_watch con path token = let apath = get_watch_path con path in let ws = Hashtbl.find con.watches apath in let w = List.find (fun w -> w.token = token) ws in let filtered = Utils.list_remove w ws in if List.length filtered > 0 then Hashtbl.replace con.watches apath filtered else Hashtbl.remove con.watches apath; con.nb_watches <- con.nb_watches - 1; apath, w let del_watches con = Hashtbl.clear con.watches; con.nb_watches <- 0 let del_transactions con = Hashtbl.clear con.transactions let list_watches con = let ll = Hashtbl.fold (fun _ watches acc -> List.map (fun watch -> watch.path, watch.token) watches :: acc) con.watches [] in List.concat ll let fire_single_watch watch = let data = Utils.join_by_null [watch.path; watch.token; ""] in send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data let fire_watch watch path = let new_path = if watch.is_relative && path.[0] = '/' then begin let n = String.length watch.base and m = String.length path in String.sub path n (m - n) end else path in let data = Utils.join_by_null [ new_path; watch.token; "" ] in send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data (* Search for a valid unused transaction id. *) let rec valid_transaction_id con proposed_id = (* * Clip proposed_id to the range [1, 0x3ffffffe] * * The chosen id must not trucate when written into the uint32_t tx_id * field, and needs to fit within the positive range of a 31 bit ocaml * integer to function when compiled as 32bit. * * Oxenstored therefore supports only 1 billion open transactions. *) let id = if proposed_id <= 0 || proposed_id >= 0x3fffffff then 1 else proposed_id in if Hashtbl.mem con.transactions id then ( (* Outstanding transaction with this id. Try the next. *) valid_transaction_id con (id + 1) ) else id let start_transaction con store = if !Define.maxtransaction > 0 && not (is_dom0 con) && Hashtbl.length con.transactions > !Define.maxtransaction then raise Quota.Transaction_opened; let id = valid_transaction_id con con.next_tid in con.next_tid <- id + 1; let ntrans = Transaction.make id store in Hashtbl.add con.transactions id ntrans; Logging.start_transaction ~tid:id ~con:(get_domstr con); id let end_transaction con tid commit = let trans = Hashtbl.find con.transactions tid in Hashtbl.remove con.transactions tid; Logging.end_transaction ~tid ~con:(get_domstr con); match commit with | None -> true | Some transaction_replay_f -> Transaction.commit ~con:(get_domstr con) trans || transaction_replay_f con trans let get_transaction con tid = Hashtbl.find con.transactions tid let do_input con = Xenbus.Xb.input con.xb let has_input con = Xenbus.Xb.has_in_packet con.xb let pop_in con = Xenbus.Xb.get_in_packet con.xb let has_more_input con = Xenbus.Xb.has_more_input con.xb let has_output con = Xenbus.Xb.has_output con.xb let has_old_output con = Xenbus.Xb.has_old_output con.xb let has_new_output con = Xenbus.Xb.has_new_output con.xb let peek_output con = Xenbus.Xb.peek_output con.xb let do_output con = Xenbus.Xb.output con.xb let has_more_work con = has_more_input con || not (has_old_output con) && has_new_output con let incr_ops con = con.stat_nb_ops <- con.stat_nb_ops + 1 let mark_symbols con = Hashtbl.iter (fun _ t -> Store.mark_symbols (Transaction.get_store t)) con.transactions let stats con = Hashtbl.length con.watches, con.stat_nb_ops let dump con chan = match con.dom with | Some dom -> let domid = Domain.get_id dom in (* dump domain *) Domain.dump dom chan; (* dump watches *) List.iter (fun (path, token) -> Printf.fprintf chan "watch,%d,%s,%s\n" domid (Utils.hexify path) (Utils.hexify token) ) (list_watches con); | None -> () let debug con = let domid = get_domstr con in let watches = List.map (fun (path, token) -> Printf.sprintf "watch %s: %s %s\n" domid path token) (list_watches con) in String.concat "" watches let decr_conflict_credit doms con = match con.dom with | None -> () (* It's a socket connection. We don't know which domain we're in, so treat it as if it's free to conflict *) | Some dom -> Domains.decr_conflict_credit doms dom xen-4.9.2/tools/ocaml/xenstored/store.ml0000664000175000017500000003235313256712137016417 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) open Stdext module Node = struct type t = { name: Symbol.t; perms: Perms.Node.t; value: string; children: t list; } let create _name _perms _value = { name = Symbol.of_string _name; perms = _perms; value = _value; children = []; } let get_owner node = Perms.Node.get_owner node.perms let get_children node = node.children let get_value node = node.value let get_perms node = node.perms let get_name node = Symbol.to_string node.name let set_value node nvalue = if node.value = nvalue then node else { node with value = nvalue } let set_perms node nperms = { node with perms = nperms } let add_child node child = { node with children = child :: node.children } let exists node childname = let childname = Symbol.of_string childname in List.exists (fun n -> n.name = childname) node.children let find node childname = let childname = Symbol.of_string childname in List.find (fun n -> n.name = childname) node.children let replace_child node child nchild = (* this is the on-steroid version of the filter one-replace one *) let rec replace_one_in_list l = match l with | [] -> [] | h :: tl when h.name = child.name -> nchild :: tl | h :: tl -> h :: replace_one_in_list tl in { node with children = (replace_one_in_list node.children) } let del_childname node childname = let sym = Symbol.of_string childname in let rec delete_one_in_list l = match l with | [] -> raise Not_found | h :: tl when h.name = sym -> tl | h :: tl -> h :: delete_one_in_list tl in { node with children = (delete_one_in_list node.children) } let del_all_children node = { node with children = [] } (* check if the current node can be accessed by the current connection with rperm permissions *) let check_perm node connection request = Perms.check connection request node.perms (* check if the current node is owned by the current connection *) let check_owner node connection = if not (Perms.check_owner connection node.perms) then begin Logging.info "store|node" "Permission denied: Domain %d not owner" (get_owner node); raise Define.Permission_denied; end let rec recurse fct node = fct node; List.iter (recurse fct) node.children let unpack node = (Symbol.to_string node.name, node.perms, node.value) end module Path = struct (* represent a path in a store. * [] -> "/" * [ "local"; "domain"; "1" ] -> "/local/domain/1" *) type t = string list let char_is_valid c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c = '_' || c = '-' || c = '@' let name_is_valid name = name <> "" && String.fold_left (fun accu c -> accu && char_is_valid c) true name let is_valid path = List.for_all name_is_valid path let of_string s = if s.[0] = '@' then [s] else if s = "/" then [] else match String.split '/' s with | "" :: path when is_valid path -> path | _ -> raise Define.Invalid_path let of_path_and_name path name = match path, name with | [], "" -> [] | _ -> path @ [name] let create path connection_path = of_string (Utils.path_validate path connection_path) let to_string t = "/" ^ (String.concat "/" t) let to_string_list x = x let get_parent t = if t = [] then [] else List.rev (List.tl (List.rev t)) let get_hierarchy path = Utils.get_hierarchy path let get_common_prefix p1 p2 = let rec compare l1 l2 = match l1, l2 with | h1 :: tl1, h2 :: tl2 -> if h1 = h2 then h1 :: (compare tl1 tl2) else [] | _, [] | [], _ -> (* if l1 or l2 is empty, we found the equal part already *) [] in compare p1 p2 let rec lookup_modify node path fct = match path with | [] -> raise (Define.Invalid_path) | h :: [] -> fct node h | h :: l -> let (n, c) = if not (Node.exists node h) then raise (Define.Lookup_Doesnt_exist h) else (node, Node.find node h) in let nc = lookup_modify c l fct in Node.replace_child n c nc let apply_modify rnode path fct = lookup_modify rnode path fct let rec lookup_get node path = match path with | [] -> raise (Define.Invalid_path) | h :: [] -> (try Node.find node h with Not_found -> raise Define.Doesnt_exist) | h :: l -> let cnode = Node.find node h in lookup_get cnode l let get_node rnode path = if path = [] then Some rnode else ( try Some (lookup_get rnode path) with Define.Doesnt_exist -> None ) (* get the deepest existing node for this path, return the node and a flag on the existence of the full path *) let rec get_deepest_existing_node node = function | [] -> node, true | h :: t -> try get_deepest_existing_node (Node.find node h) t with Not_found -> node, false let set_node rnode path nnode = if path = [] then nnode else let set_node node name = try let ent = Node.find node name in Node.replace_child node ent nnode with Not_found -> Node.add_child node nnode in apply_modify rnode path set_node (* read | ls | getperms use this *) let rec lookup node path fct = match path with | [] -> raise (Define.Invalid_path) | h :: [] -> fct node h | h :: l -> let cnode = Node.find node h in lookup cnode l fct let apply rnode path fct = lookup rnode path fct end (* The Store.t type *) type t = { mutable stat_transaction_coalesce: int; mutable stat_transaction_abort: int; mutable root: Node.t; mutable quota: Quota.t; } let get_root store = store.root let set_root store root = store.root <- root let get_quota store = store.quota let set_quota store quota = store.quota <- quota (* modifying functions *) let path_mkdir store perm path = let do_mkdir node name = try let ent = Node.find node name in Node.check_perm ent perm Perms.WRITE; raise Define.Already_exist with Not_found -> Node.check_perm node perm Perms.WRITE; Node.add_child node (Node.create name node.Node.perms "") in if path = [] then store.root else Path.apply_modify store.root path do_mkdir let path_write store perm path value = let node_created = ref false in let do_write node name = try let ent = Node.find node name in Node.check_perm ent perm Perms.WRITE; let nent = Node.set_value ent value in Node.replace_child node ent nent with Not_found -> node_created := true; Node.check_perm node perm Perms.WRITE; Node.add_child node (Node.create name node.Node.perms value) in if path = [] then ( Node.check_perm store.root perm Perms.WRITE; Node.set_value store.root value, false ) else Path.apply_modify store.root path do_write, !node_created let path_rm store perm path = let do_rm node name = try let ent = Node.find node name in Node.check_perm ent perm Perms.WRITE; Node.del_childname node name with Not_found -> raise Define.Doesnt_exist in if path = [] then Node.del_all_children store.root else Path.apply_modify store.root path do_rm let path_setperms store perm path perms = if path = [] then Node.set_perms store.root perms else let do_setperms node name = let c = Node.find node name in Node.check_owner c perm; Node.check_perm c perm Perms.WRITE; let nc = Node.set_perms c perms in Node.replace_child node c nc in Path.apply_modify store.root path do_setperms (* accessing functions *) let get_node store path = Path.get_node store.root path let get_deepest_existing_node store path = Path.get_deepest_existing_node store.root path let read store perm path = let do_read node name = let ent = Node.find node name in Node.check_perm ent perm Perms.READ; ent.Node.value in if path = [] then ( let ent = store.root in Node.check_perm ent perm Perms.READ; ent.Node.value ) else Path.apply store.root path do_read let ls store perm path = let children = if path = [] then (Node.get_children store.root) else let do_ls node name = let cnode = Node.find node name in Node.check_perm cnode perm Perms.READ; cnode.Node.children in Path.apply store.root path do_ls in List.rev (List.map (fun n -> Symbol.to_string n.Node.name) children) let getperms store perm path = if path = [] then (Node.get_perms store.root) else let fct n name = let c = Node.find n name in Node.check_perm c perm Perms.READ; c.Node.perms in Path.apply store.root path fct let path_exists store path = if path = [] then true else try let check_exist node name = ignore(Node.find node name); true in Path.apply store.root path check_exist with Not_found -> false (* others utils *) let traversal root_node f = let rec _traversal path node = f path node; let node_path = Path.of_path_and_name path (Symbol.to_string node.Node.name) in List.iter (_traversal node_path) node.Node.children in _traversal [] root_node let dump_store_buf root_node = let buf = Buffer.create 8192 in let dump_node path node = let pathstr = String.concat "/" path in Printf.bprintf buf "%s/%s{%s}" pathstr (Symbol.to_string node.Node.name) (String.escaped (Perms.Node.to_string (Node.get_perms node))); if String.length node.Node.value > 0 then Printf.bprintf buf " = %s\n" (String.escaped node.Node.value) else Printf.bprintf buf "\n"; in traversal root_node dump_node; buf let dump_store chan root_node = let buf = dump_store_buf root_node in output_string chan (Buffer.contents buf); Buffer.reset buf let dump_fct store f = traversal store.root f let dump store out_chan = dump_store out_chan store.root let dump_stdout store = dump_store stdout store.root let dump_buffer store = dump_store_buf store.root (* modifying functions with quota udpate *) let set_node store path node orig_quota mod_quota = let root = Path.set_node store.root path node in store.root <- root; Quota.merge orig_quota mod_quota store.quota let write store perm path value = let node, existing = get_deepest_existing_node store path in let owner = Node.get_owner node in if existing || (Perms.Connection.is_dom0 perm) then (* Only check the string length limit *) Quota.check store.quota (-1) (String.length value) else (* Check the domain entries limit too *) Quota.check store.quota owner (String.length value); let root, node_created = path_write store perm path value in store.root <- root; if node_created then Quota.add_entry store.quota owner let mkdir store perm path = let node, existing = get_deepest_existing_node store path in let owner = Node.get_owner node in (* It's upt to the mkdir logic to decide what to do with existing path *) if not (existing || (Perms.Connection.is_dom0 perm)) then Quota.check store.quota owner 0; store.root <- path_mkdir store perm path; Quota.add_entry store.quota owner let rm store perm path = let rmed_node = Path.get_node store.root path in match rmed_node with | None -> raise Define.Doesnt_exist | Some rmed_node -> store.root <- path_rm store perm path; Node.recurse (fun node -> Quota.del_entry store.quota (Node.get_owner node)) rmed_node let setperms store perm path nperms = match Path.get_node store.root path with | None -> raise Define.Doesnt_exist | Some node -> let old_owner = Node.get_owner node in let new_owner = Perms.Node.get_owner nperms in if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then Quota.check store.quota new_owner 0; store.root <- path_setperms store perm path nperms; Quota.del_entry store.quota old_owner; Quota.add_entry store.quota new_owner type ops = { store: t; write: Path.t -> string -> unit; mkdir: Path.t -> unit; rm: Path.t -> unit; setperms: Path.t -> Perms.Node.t -> unit; ls: Path.t -> string list; read: Path.t -> string; getperms: Path.t -> Perms.Node.t; path_exists: Path.t -> bool; } let get_ops store perms = { store = store; write = write store perms; mkdir = mkdir store perms; rm = rm store perms; setperms = setperms store perms; ls = ls store perms; read = read store perms; getperms = getperms store perms; path_exists = path_exists store; } let create () = { stat_transaction_coalesce = 0; stat_transaction_abort = 0; root = Node.create "" Perms.Node.default0 ""; quota = Quota.create (); } let copy store = { stat_transaction_coalesce = store.stat_transaction_coalesce; stat_transaction_abort = store.stat_transaction_abort; root = store.root; quota = Quota.copy store.quota; } let mark_symbols store = Node.recurse (fun node -> Symbol.mark_as_used node.Node.name) store.root let incr_transaction_coalesce store = store.stat_transaction_coalesce <- store.stat_transaction_coalesce + 1 let incr_transaction_abort store = store.stat_transaction_abort <- store.stat_transaction_abort + 1 let stats store = let nb_nodes = ref 0 in traversal store.root (fun path node -> incr nb_nodes ); !nb_nodes, store.stat_transaction_abort, store.stat_transaction_coalesce xen-4.9.2/tools/ocaml/xenstored/utils.ml0000664000175000017500000000553113256712137016421 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) open Printf open Stdext (* lists utils *) let filter_out filter l = List.filter (fun x -> not (List.mem x filter)) l let filter_in filter l = List.filter (fun x -> List.mem x filter) l let list_remove element l = List.filter (fun e -> e != element) l let list_tl_multi n l = let rec do_tl i x = if i = 0 then x else do_tl (i - 1) (List.tl x) in do_tl n l (* string utils *) let get_hierarchy path = let l = List.length path in let revpath = List.rev path in let rec sub i = let x = List.rev (list_tl_multi (l - i) revpath) in if i = l then [ x ] else x :: sub (i + 1) in sub 0 let hexify s = let hexseq_of_char c = sprintf "%02x" (Char.code c) in let hs = String.create (String.length s * 2) in for i = 0 to String.length s - 1 do let seq = hexseq_of_char s.[i] in hs.[i * 2] <- seq.[0]; hs.[i * 2 + 1] <- seq.[1]; done; hs let unhexify hs = let char_of_hexseq seq0 seq1 = Char.chr (int_of_string (sprintf "0x%c%c" seq0 seq1)) in let s = String.create (String.length hs / 2) in for i = 0 to String.length s - 1 do s.[i] <- char_of_hexseq hs.[i * 2] hs.[i * 2 + 1] done; s let trim_path path = try let rindex = String.rindex path '/' in String.sub path 0 rindex with Not_found -> "" let join_by_null ls = String.concat "\000" ls (* unix utils *) let create_unix_socket name = Unixext.unlink_safe name; Unixext.mkdir_rec (Filename.dirname name) 0o700; let sockaddr = Unix.ADDR_UNIX(name) in let sock = Unix.socket Unix.PF_UNIX Unix.SOCK_STREAM 0 in Unix.bind sock sockaddr; Unix.listen sock 1; sock let read_file_single_integer filename = let fd = Unix.openfile filename [ Unix.O_RDONLY ] 0o640 in let buf = String.make 20 (char_of_int 0) in let sz = Unix.read fd buf 0 20 in Unix.close fd; int_of_string (String.sub buf 0 sz) let path_complete path connection_path = if String.get path 0 <> '/' then connection_path ^ path else path let path_validate path connection_path = if String.length path = 0 || String.length path > 1024 then raise Define.Invalid_path else let cpath = path_complete path connection_path in if String.get cpath 0 <> '/' then raise Define.Invalid_path else cpath xen-4.9.2/tools/ocaml/xenstored/syslog.ml0000664000175000017500000000274513256712137016605 0ustar smbsmb(* * Copyright (C) 2006-2009 Citrix Systems Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type level = Emerg | Alert | Crit | Err | Warning | Notice | Info | Debug type options = Cons | Ndelay | Nowait | Odelay | Perror | Pid type facility = Auth | Authpriv | Cron | Daemon | Ftp | Kern | Local0 | Local1 | Local2 | Local3 | Local4 | Local5 | Local6 | Local7 | Lpr | Mail | News | Syslog | User | Uucp external log : facility -> level -> string -> unit = "stub_syslog" exception Unknown_facility of string let facility_of_string s = match s with |"auth"->Auth |"authpriv"->Authpriv |"cron"->Cron |"daemon"->Daemon |"ftp"->Ftp |"kern"->Kern |"local0"->Local0 |"local1"->Local1 |"local2"->Local2 |"local3"->Local3 |"local4"->Local4 |"local5"->Local5 |"local6"->Local6 |"local7"->Local7 |"lpr"->Lpr |"mail"->Mail |"news"->News |"syslog"->Syslog |"user"->User |"uucp"->Uucp |_-> raise (Unknown_facility s) xen-4.9.2/tools/ocaml/xenstored/disk.ml0000664000175000017500000001026713256712137016215 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) let enable = ref false let xs_daemon_database = Paths.xen_run_stored ^ "/db" let error fmt = Logging.error "disk" fmt (* unescape utils *) exception Bad_escape let is_digit c = match c with '0' .. '9' -> true | _ -> false let undec c = match c with | '0' .. '9' -> (Char.code c) - (Char.code '0') | _ -> raise (Failure "undecify") let unhex c = let c = Char.lowercase c in match c with | '0' .. '9' -> (Char.code c) - (Char.code '0') | 'a' .. 'f' -> (Char.code c) - (Char.code 'a') + 10 | _ -> raise (Failure "unhexify") let string_unescaped s = let len = String.length s and i = ref 0 in let d = Buffer.create len in let read_escape () = incr i; match s.[!i] with | 'n' -> '\n' | 'r' -> '\r' | '\\' -> '\\' | '\'' -> '\'' | '"' -> '"' | 't' -> '\t' | 'b' -> '\b' | 'x' -> let v = (unhex s.[!i + 1] * 16) + unhex s.[!i + 2] in i := !i + 2; Char.chr v | c -> if is_digit c then ( let v = (undec s.[!i]) * 100 + (undec s.[!i + 1]) * 10 + (undec s.[!i + 2]) in i := !i + 2; Char.chr v ) else raise Bad_escape in while !i < len do let c = match s.[!i] with | '\\' -> read_escape () | c -> c in Buffer.add_char d c; incr i done; Buffer.contents d (* file -> lines_of_file *) let file_readlines file = let channel = open_in file in let rec input_line_list channel = let line = try input_line channel with End_of_file -> "" in if String.length line > 0 then line :: input_line_list channel else ( close_in channel; [] ) in input_line_list channel let rec map_string_list_range l s = match l with | [] -> [] | (a,b) :: l -> String.sub s a (b - a) :: map_string_list_range l s let is_digit c = try ignore (int_of_char c); true with _ -> false let rec parse_perm s = let len = String.length s in if len = 0 then [] else let i = ref 1 in while !i < len && is_digit s.[!i] do incr i done; let x = String.sub s 0 !i and lx = String.sub s !i len in x :: parse_perm lx let read store = (* don't let the permission get on our way, full perm ! *) let v = Store.get_ops store Perms.Connection.full_rights in (* a line is : path{perm} or path{perm} = value *) let parse_line s = let path, perm, value = let len = String.length s in let si = if String.contains s '=' then String.index s '=' else len - 1 in let pi = String.rindex_from s si '{' in let epi = String.index_from s pi '}' in if String.contains s '=' then let ss = map_string_list_range [ (0, pi); (pi + 1, epi); (si + 2, len); ] s in (List.nth ss 0, List.nth ss 1, List.nth ss 2) else let ss = map_string_list_range [ (0, pi); (pi + 1, epi); ] s in (List.nth ss 0, List.nth ss 1, "") in let path = Store.Path.of_string path in v.Store.write path (string_unescaped value); v.Store.setperms path (Perms.Node.of_strings (parse_perm perm)) in try let lines = file_readlines xs_daemon_database in List.iter (fun s -> parse_line s) lines with exc -> error "caught exn %s" (Printexc.to_string exc) let write store = if !enable then try let tfile = Printf.sprintf "%s#" xs_daemon_database in let channel = open_out_gen [ Open_wronly; Open_creat; Open_trunc; ] 0o600 tfile in Store.dump store channel; flush channel; close_out channel; Unix.rename tfile xs_daemon_database with exc -> error "caught exn %s" (Printexc.to_string exc) xen-4.9.2/tools/ocaml/xenstored/quota.ml0000664000175000017500000000527013256712137016412 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) exception Limit_reached exception Data_too_big exception Transaction_opened let warn fmt = Logging.warn "quota" fmt let activate = ref true let maxent = ref (10000) let maxsize = ref (4096) type t = { maxent: int; (* max entities per domU *) maxsize: int; (* max size of data store in one node *) cur: (Xenctrl.domid, int) Hashtbl.t; (* current domains quota *) } let to_string quota domid = if Hashtbl.mem quota.cur domid then Printf.sprintf "dom%i quota: %i/%i" domid (Hashtbl.find quota.cur domid) quota.maxent else Printf.sprintf "dom%i quota: not set" domid let create () = { maxent = !maxent; maxsize = !maxsize; cur = Hashtbl.create 100; } let copy quota = { quota with cur = (Hashtbl.copy quota.cur) } let del quota id = Hashtbl.remove quota.cur id let _check quota id size = if size > quota.maxsize then ( warn "domain %u err create entry: data too big %d" id size; raise Data_too_big ); if id > 0 && Hashtbl.mem quota.cur id then let entry = Hashtbl.find quota.cur id in if entry >= quota.maxent then ( warn "domain %u cannot create entry: quota reached" id; raise Limit_reached ) let check quota id size = if !activate then _check quota id size let get_entry quota id = Hashtbl.find quota.cur id let set_entry quota id nb = if nb = 0 then Hashtbl.remove quota.cur id else begin if Hashtbl.mem quota.cur id then Hashtbl.replace quota.cur id nb else Hashtbl.add quota.cur id nb end let del_entry quota id = try let nb = get_entry quota id in set_entry quota id (nb - 1) with Not_found -> () let add_entry quota id = let nb = try get_entry quota id with Not_found -> 0 in set_entry quota id (nb + 1) let add quota diff = Hashtbl.iter (fun id nb -> set_entry quota id (get_entry quota id + nb)) diff.cur let merge orig_quota mod_quota dest_quota = Hashtbl.iter (fun id nb -> let diff = nb - (try get_entry orig_quota id with Not_found -> 0) in if diff <> 0 then set_entry dest_quota id ((try get_entry dest_quota id with Not_found -> 0) + diff)) mod_quota.cur xen-4.9.2/tools/ocaml/xenstored/symbol.mli0000664000175000017500000000345213256712137016737 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) (** Node names *) (** Xenstore nodes names are often the same, ie. "local", "domain", "device", ... so it is worth to manipulate them through the use of small identifiers that we call symbols. These symbols can be compared in constant time (as opposite to strings) and should help the ocaml GC. *) type t (** The type of symbols. *) val of_string : string -> t (** Convert a string into a symbol. *) val to_string : t -> string (** Convert a symbol into a string. *) (** {6 Garbage Collection} *) (** Symbols need to be regulary garbage collected. The following steps should be followed: - mark all the knowns symbols as unused (with [mark_all_as_unused]); - mark all the symbols really usefull as used (with [mark_as_used]); and - finally, call [garbage] *) val mark_all_as_unused : unit -> unit val mark_as_used : t -> unit val garbage : unit -> unit (** {6 Statistics } *) val stats : unit -> int (** Get the number of used symbols. *) val created : unit -> int (** Returns the number of symbols created since the last GC. *) val used : unit -> int (** Returns the number of existing symbols used since the last GC *) xen-4.9.2/tools/ocaml/xenstored/symbol.ml0000664000175000017500000000405713256712137016570 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type t = int type 'a record = { data: 'a; mutable garbage: bool } let int_string_tbl : (int,string record) Hashtbl.t = Hashtbl.create 1024 let string_int_tbl : (string,int) Hashtbl.t = Hashtbl.create 1024 let created_counter = ref 0 let used_counter = ref 0 let count = ref 0 let rec fresh () = if Hashtbl.mem int_string_tbl !count then begin incr count; fresh () end else !count let new_record v = { data=v; garbage=false } let of_string name = if Hashtbl.mem string_int_tbl name then begin incr used_counter; Hashtbl.find string_int_tbl name end else begin let i = fresh () in incr created_counter; Hashtbl.add string_int_tbl name i; Hashtbl.add int_string_tbl i (new_record name); i end let to_string i = (Hashtbl.find int_string_tbl i).data let mark_all_as_unused () = Hashtbl.iter (fun _ v -> v.garbage <- true) int_string_tbl let mark_as_used symb = let record1 = Hashtbl.find int_string_tbl symb in record1.garbage <- false let garbage () = let records = Hashtbl.fold (fun symb record accu -> if record.garbage then (symb, record.data) :: accu else accu ) int_string_tbl [] in let remove (int,string) = Hashtbl.remove int_string_tbl int; Hashtbl.remove string_int_tbl string in created_counter := 0; used_counter := 0; List.iter remove records let stats () = Hashtbl.length string_int_tbl let created () = !created_counter let used () = !used_counter xen-4.9.2/tools/ocaml/xenstored/transaction.ml0000664000175000017500000001746013256712137017612 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) let error fmt = Logging.error "transaction" fmt open Stdext let none = 0 let test_eagain = ref false let do_coalesce = ref true let check_parents_perms_identical root1 root2 path = let hierarch = Store.Path.get_hierarchy path in let permdiff = List.fold_left (fun acc path -> let n1 = Store.Path.get_node root1 path and n2 = Store.Path.get_node root2 path in match n1, n2 with | Some n1, Some n2 -> not (Perms.equiv (Store.Node.get_perms n1) (Store.Node.get_perms n2)) || acc | _ -> true || acc ) false hierarch in (not permdiff) let get_lowest path1 path2 = match path2 with | None -> Some path1 | Some path2 -> Some (Store.Path.get_common_prefix path1 path2) let test_coalesce oldroot currentroot optpath = match optpath with | None -> true | Some path -> let oldnode = Store.Path.get_node oldroot path and currentnode = Store.Path.get_node currentroot path in match oldnode, currentnode with | (Some oldnode), (Some currentnode) -> if oldnode == currentnode then ( check_parents_perms_identical oldroot currentroot path ) else ( false ) | None, None -> ( (* ok then it doesn't exists in the old version and the current version, just sneak it in as a child of the parent node if it exists, or else fail *) let pnode = Store.Path.get_node currentroot (Store.Path.get_parent path) in match pnode with | None -> false (* ok it doesn't exists, just bail out. *) | Some pnode -> true ) | _ -> false let can_coalesce oldroot currentroot path = if !do_coalesce then try test_coalesce oldroot currentroot path with _ -> false else false type ty = No | Full of ( int * (* Transaction id *) Store.t * (* Original store *) Store.t (* A pointer to the canonical store: its root changes on each transaction-commit *) ) type t = { ty: ty; start_count: int64; store: Store.t; (* This is the store that we change in write operations. *) quota: Quota.t; mutable paths: (Xenbus.Xb.Op.operation * Store.Path.t) list; mutable operations: (Packet.request * Packet.response) list; mutable read_lowpath: Store.Path.t option; mutable write_lowpath: Store.Path.t option; } let get_id t = match t.ty with No -> none | Full (id, _, _) -> id let counter = ref 0L let failed_commits = ref 0L let failed_commits_no_culprit = ref 0L let reset_conflict_stats () = failed_commits := 0L; failed_commits_no_culprit := 0L (* Scope for optimisation: different data-structure and functions to search/filter it *) let short_running_txns = ref [] let oldest_short_running_transaction () = let rec last = function | [] -> None | [x] -> Some x | x :: xs -> last xs in last !short_running_txns let trim_short_running_transactions txn = let cutoff = Unix.gettimeofday () -. !Define.conflict_max_history_seconds in let keep = match txn with | None -> (function (start_time, _) -> start_time >= cutoff) | Some t -> (function (start_time, tx) -> start_time >= cutoff && tx != t) in short_running_txns := List.filter keep !short_running_txns let make ?(internal=false) id store = let ty = if id = none then No else Full(id, Store.copy store, store) in let txn = { ty = ty; start_count = !counter; store = if id = none then store else Store.copy store; quota = Quota.copy store.Store.quota; paths = []; operations = []; read_lowpath = None; write_lowpath = None; } in if id <> none && not internal then ( let now = Unix.gettimeofday () in short_running_txns := (now, txn) :: !short_running_txns ); txn let get_store t = t.store let get_paths t = t.paths let is_read_only t = t.paths = [] let add_wop t ty path = t.paths <- (ty, path) :: t.paths let add_operation ~perm t request response = if !Define.maxrequests >= 0 && not (Perms.Connection.is_dom0 perm) && List.length t.operations >= !Define.maxrequests then raise Quota.Limit_reached; t.operations <- (request, response) :: t.operations let get_operations t = List.rev t.operations let set_read_lowpath t path = t.read_lowpath <- get_lowest path t.read_lowpath let set_write_lowpath t path = t.write_lowpath <- get_lowest path t.write_lowpath let path_exists t path = Store.path_exists t.store path let write t perm path value = let path_exists = path_exists t path in Store.write t.store perm path value; if path_exists then set_write_lowpath t path else set_write_lowpath t (Store.Path.get_parent path); add_wop t Xenbus.Xb.Op.Write path let mkdir ?(with_watch=true) t perm path = Store.mkdir t.store perm path; set_write_lowpath t path; if with_watch then add_wop t Xenbus.Xb.Op.Mkdir path let setperms t perm path perms = Store.setperms t.store perm path perms; set_write_lowpath t path; add_wop t Xenbus.Xb.Op.Setperms path let rm t perm path = Store.rm t.store perm path; set_write_lowpath t (Store.Path.get_parent path); add_wop t Xenbus.Xb.Op.Rm path let ls t perm path = let r = Store.ls t.store perm path in set_read_lowpath t path; r let read t perm path = let r = Store.read t.store perm path in set_read_lowpath t path; r let getperms t perm path = let r = Store.getperms t.store perm path in set_read_lowpath t path; r let commit ~con t = let has_write_ops = List.length t.paths > 0 in let has_coalesced = ref false in let has_commited = match t.ty with | No -> true | Full (id, oldstore, cstore) -> (* "cstore" meaning current canonical store *) let commit_partial oldroot cstore store = (* get the lowest path of the query and verify that it hasn't been modified by others transactions. *) if can_coalesce oldroot (Store.get_root cstore) t.read_lowpath && can_coalesce oldroot (Store.get_root cstore) t.write_lowpath then ( maybe (fun p -> let n = Store.get_node store p in (* it has to be in the store, otherwise it means bugs in the lowpath registration. we don't need to handle none. *) maybe (fun n -> Store.set_node cstore p n t.quota store.Store.quota) n; Logging.write_coalesce ~tid:(get_id t) ~con (Store.Path.to_string p); ) t.write_lowpath; maybe (fun p -> Logging.read_coalesce ~tid:(get_id t) ~con (Store.Path.to_string p) ) t.read_lowpath; has_coalesced := true; Store.incr_transaction_coalesce cstore; true ) else ( (* cannot do anything simple, just discard the queries, and the client need to redo it later *) Store.incr_transaction_abort cstore; false ) in let try_commit oldroot cstore store = if oldroot == Store.get_root cstore then ( (* move the new root to the current store, if the oldroot has not been modified *) if has_write_ops then ( Store.set_root cstore (Store.get_root store); Store.set_quota cstore (Store.get_quota store) ); true ) else (* we try a partial commit if possible *) commit_partial oldroot cstore store in if !test_eagain && Random.int 3 = 0 then false else try_commit (Store.get_root oldstore) cstore t.store in if has_commited && has_write_ops then Disk.write t.store; if not has_commited then Logging.conflict ~tid:(get_id t) ~con else if not !has_coalesced then Logging.commit ~tid:(get_id t) ~con; has_commited xen-4.9.2/tools/ocaml/xenstored/select.ml0000664000175000017500000000527713256712137016547 0ustar smbsmb(* * Copyright (C) 2014 Zheng Li * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) (* The [read], [write], [except] are fields mapped to the POLLIN/OUT/PRI subscription flags used by poll, which have a correspondence to the readfds, writefds, exceptfds concept as in select. *) type event = { mutable read: bool; mutable write: bool; mutable except: bool; } external select_on_poll: (Unix.file_descr * event) array -> int -> int = "stub_select_on_poll" external set_fd_limit: int -> unit = "stub_set_fd_limit" (* The rlim_max given to setrlimit must not go above the system level nr_open, which we can read from /proc/sys. *) let get_sys_fs_nr_open () = try let ch = open_in "/proc/sys/fs/nr_open" in let v = int_of_string (input_line ch) in close_in_noerr ch; v with _ -> 1024 * 1024 let init_event () = {read = false; write = false; except = false} let poll_select in_fds out_fds exc_fds timeout = let h = Hashtbl.create 57 in let add_event event_set fd = let e = try Hashtbl.find h fd with Not_found -> let e = init_event () in Hashtbl.add h fd e; e in event_set e in List.iter (add_event (fun x -> x.read <- true)) in_fds; List.iter (add_event (fun x -> x.write <- true)) out_fds; List.iter (add_event (fun x -> x.except <- true)) exc_fds; (* Unix.stdin and init_event are dummy input as stubs, which will always be overwritten later on. *) let a = Array.make (Hashtbl.length h) (Unix.stdin, init_event ()) in let i = ref (-1) in Hashtbl.iter (fun fd event -> incr i; Array.set a !i (fd, event)) h; let n = select_on_poll a (int_of_float (timeout *. 1000.)) in let r = [], [], [] in if n = 0 then r else Array.fold_right (fun (fd, event) (r, w, x) -> (if event.read then fd :: r else r), (if event.write then fd :: w else w), (if event.except then fd :: x else x)) a r (* If the use_poll function is not called at all, we default to the original Unix.select behavior *) let select_fun = ref Unix.select let use_poll yes = let sel_fun, max_fd = if yes then poll_select, get_sys_fs_nr_open () else Unix.select, 1024 in select_fun := sel_fun; set_fd_limit max_fd let select in_fds out_fds exc_fds timeout = (!select_fun) in_fds out_fds exc_fds timeout xen-4.9.2/tools/ocaml/xenstored/syslog_stubs.c0000664000175000017500000000303213256712137017625 0ustar smbsmb/* * Copyright (C) 2006-2009 Citrix Systems Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include static int __syslog_level_table[] = { LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG }; static int __syslog_facility_table[] = { LOG_AUTH, LOG_AUTHPRIV, LOG_CRON, LOG_DAEMON, LOG_FTP, LOG_KERN, LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_LPR | LOG_MAIL | LOG_NEWS | LOG_SYSLOG | LOG_USER | LOG_UUCP }; value stub_syslog(value facility, value level, value msg) { CAMLparam3(facility, level, msg); const char *c_msg = strdup(String_val(msg)); int c_facility = __syslog_facility_table[Int_val(facility)] | __syslog_level_table[Int_val(level)]; caml_enter_blocking_section(); syslog(c_facility, "%s", c_msg); caml_leave_blocking_section(); free((void*)c_msg); CAMLreturn(Val_unit); } xen-4.9.2/tools/ocaml/xenstored/parse_arg.ml0000664000175000017500000000573513256712137017232 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type config = { domain_init: bool; activate_access_log: bool; daemonize: bool; reraise_top_level: bool; config_file: string option; pidfile: string option; (* old xenstored compatibility *) tracefile: string option; (* old xenstored compatibility *) restart: bool; disable_socket: bool; use_select: bool; } let do_argv = let pidfile = ref "" and tracefile = ref "" (* old xenstored compatibility *) and domain_init = ref true and activate_access_log = ref true and daemonize = ref true and reraise_top_level = ref false and config_file = ref "" and restart = ref false and disable_socket = ref false and use_select = ref false in let speclist = [ ("--no-domain-init", Arg.Unit (fun () -> domain_init := false), "to state that xenstored should not initialise dom0"); ("--config-file", Arg.Set_string config_file, "set an alternative location for the configuration file"); ("--no-fork", Arg.Unit (fun () -> daemonize := false), "to request that the daemon does not fork"); ("--reraise-top-level", Arg.Unit (fun () -> reraise_top_level := true), "reraise exceptions caught at the top level"); ("--no-access-log", Arg.Unit (fun () -> activate_access_log := false), "do not create a xenstore-access.log file"); ("--pid-file", Arg.Set_string pidfile, ""); (* for compatibility *) ("-T", Arg.Set_string tracefile, ""); (* for compatibility *) ("--restart", Arg.Set restart, "Read database on starting"); ("--disable-socket", Arg.Unit (fun () -> disable_socket := true), "Disable socket"); ("--use-select", Arg.Unit (fun () -> use_select := true), "Use select instead of poll"); (* for backward compatibility and testing *) ] in let usage_msg = "usage : xenstored [--config-file ] [--no-domain-init] [--help] [--no-fork] [--reraise-top-level] [--restart] [--disable-socket] [--use-select]" in Arg.parse speclist (fun s -> ()) usage_msg; { domain_init = !domain_init; activate_access_log = !activate_access_log; daemonize = !daemonize; reraise_top_level = !reraise_top_level; config_file = if !config_file <> "" then Some !config_file else None; pidfile = if !pidfile <> "" then Some !pidfile else None; tracefile = if !tracefile <> "" then Some !tracefile else None; restart = !restart; disable_socket = !disable_socket; use_select = !use_select; } xen-4.9.2/tools/ocaml/xenstored/history.ml0000664000175000017500000000615513256712137016765 0ustar smbsmb(* * Copyright (c) 2017 Citrix Systems Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type history_record = { con: Connection.t; (* connection that made a change *) tid: int; (* transaction id of the change (may be Transaction.none) *) before: Store.t; (* the store before the change *) after: Store.t; (* the store after the change *) finish_count: int64; (* the commit-count at which the transaction finished *) } let history : history_record list ref = ref [] (* Called from periodic_ops to ensure we don't discard symbols that are still needed. *) (* There is scope for optimisation here, since in consecutive commits one commit's `after` * is the same thing as the next commit's `before`, but not all commits in history are * consecutive. *) let mark_symbols () = (* There are gaps where dom0's commits are missing. Otherwise we could assume that * each element's `before` is the same thing as the next element's `after` * since the next element is the previous commit *) List.iter (fun hist_rec -> Store.mark_symbols hist_rec.before; Store.mark_symbols hist_rec.after; ) !history (* Keep only enough commit-history to protect the running transactions that we are still tracking *) (* There is scope for optimisation here, replacing List.filter with something more efficient, * probably on a different list-like structure. *) let trim ?txn () = Transaction.trim_short_running_transactions txn; history := match Transaction.oldest_short_running_transaction () with | None -> [] (* We have no open transaction, so no history is needed *) | Some (_, txn) -> ( (* keep records with finish_count recent enough to be relevant *) List.filter (fun r -> r.finish_count > txn.Transaction.start_count) !history ) let end_transaction txn con tid commit = let success = Connection.end_transaction con tid commit in trim ~txn (); success let push (x: history_record) = let dom = x.con.Connection.dom in match dom with | None -> () (* treat socket connections as always free to conflict *) | Some d -> if not (Domain.is_free_to_conflict d) then history := x :: !history (* Find the connections from records since commit-count [since] for which [f record] returns [true] *) let filter_connections ~ignore ~since ~f = (* The "mem" call is an optimisation, to avoid calling f if we have picked con already. *) (* Using a hash table rather than a list is to optimise the "mem" call. *) List.fold_left (fun acc hist_rec -> if hist_rec.finish_count > since && not (hist_rec.con == ignore) && not (Hashtbl.mem acc hist_rec.con) && f hist_rec then Hashtbl.replace acc hist_rec.con (); acc ) (Hashtbl.create 1023) !history xen-4.9.2/tools/ocaml/xenstored/connections.ml0000664000175000017500000001335013256712137017601 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) let debug fmt = Logging.debug "connections" fmt type t = { anonymous: (Unix.file_descr, Connection.t) Hashtbl.t; domains: (int, Connection.t) Hashtbl.t; ports: (Xeneventchn.t, Connection.t) Hashtbl.t; mutable watches: (string, Connection.watch list) Trie.t; } let create () = { anonymous = Hashtbl.create 37; domains = Hashtbl.create 37; ports = Hashtbl.create 37; watches = Trie.create () } let add_anonymous cons fd can_write = let xbcon = Xenbus.Xb.open_fd fd in let con = Connection.create xbcon None in Hashtbl.add cons.anonymous (Xenbus.Xb.get_fd xbcon) con let add_domain cons dom = let xbcon = Xenbus.Xb.open_mmap (Domain.get_interface dom) (fun () -> Domain.notify dom) in let con = Connection.create xbcon (Some dom) in Hashtbl.add cons.domains (Domain.get_id dom) con; match Domain.get_port dom with | Some p -> Hashtbl.add cons.ports p con; | None -> () let select ?(only_if = (fun _ -> true)) cons = Hashtbl.fold (fun _ con (ins, outs) -> if (only_if con) then ( let fd = Connection.get_fd con in (fd :: ins, if Connection.has_output con then fd :: outs else outs) ) else (ins, outs) ) cons.anonymous ([], []) let find cons = Hashtbl.find cons.anonymous let find_domain cons = Hashtbl.find cons.domains let find_domain_by_port cons port = Hashtbl.find cons.ports port let del_watches_of_con con watches = match List.filter (fun w -> Connection.get_con w != con) watches with | [] -> None | ws -> Some ws let del_anonymous cons con = try Hashtbl.remove cons.anonymous (Connection.get_fd con); cons.watches <- Trie.map (del_watches_of_con con) cons.watches; Connection.close con with exn -> debug "del anonymous %s" (Printexc.to_string exn) let del_domain cons id = try let con = find_domain cons id in Hashtbl.remove cons.domains id; (match Connection.get_domain con with | Some d -> (match Domain.get_port d with | Some p -> Hashtbl.remove cons.ports p | None -> ()) | None -> ()); cons.watches <- Trie.map (del_watches_of_con con) cons.watches; Connection.close con with exn -> debug "del domain %u: %s" id (Printexc.to_string exn) let iter_domains cons fct = Hashtbl.iter (fun k c -> fct c) cons.domains let iter_anonymous cons fct = Hashtbl.iter (fun _ c -> fct c) cons.anonymous let iter cons fct = iter_domains cons fct; iter_anonymous cons fct let has_more_work cons = Hashtbl.fold (fun id con acc -> if Connection.has_more_work con then con :: acc else acc) cons.domains [] let key_of_str path = if path.[0] = '@' then [path] else "" :: Store.Path.to_string_list (Store.Path.of_string path) let key_of_path path = "" :: Store.Path.to_string_list path let add_watch cons con path token = let apath, watch = Connection.add_watch con path token in let key = key_of_str apath in let watches = if Trie.mem cons.watches key then Trie.find cons.watches key else [] in cons.watches <- Trie.set cons.watches key (watch :: watches); watch let del_watch cons con path token = let apath, watch = Connection.del_watch con path token in let key = key_of_str apath in let watches = Utils.list_remove watch (Trie.find cons.watches key) in if watches = [] then cons.watches <- Trie.unset cons.watches key else cons.watches <- Trie.set cons.watches key watches; watch (* path is absolute *) let fire_watches cons path recurse = let key = key_of_path path in let path = Store.Path.to_string path in let fire_watch _ = function | None -> () | Some watches -> List.iter (fun w -> Connection.fire_watch w path) watches in let fire_rec x = function | None -> () | Some watches -> List.iter (fun w -> Connection.fire_single_watch w) watches in Trie.iter_path fire_watch cons.watches key; if recurse then Trie.iter fire_rec (Trie.sub cons.watches key) let fire_spec_watches cons specpath = iter cons (fun con -> List.iter (fun w -> Connection.fire_single_watch w) (Connection.get_watches con specpath)) let set_target cons domain target_domain = let con = find_domain cons domain in Connection.set_target con target_domain let number_of_transactions cons = let res = ref 0 in let aux con = res := Connection.number_of_transactions con + !res in iter cons aux; !res let stats cons = let nb_ops_anon = ref 0 and nb_watchs_anon = ref 0 and nb_ops_dom = ref 0 and nb_watchs_dom = ref 0 in iter_anonymous cons (fun con -> let con_watchs, con_ops = Connection.stats con in nb_ops_anon := !nb_ops_anon + con_ops; nb_watchs_anon := !nb_watchs_anon + con_watchs; ); iter_domains cons (fun con -> let con_watchs, con_ops = Connection.stats con in nb_ops_dom := !nb_ops_dom + con_ops; nb_watchs_dom := !nb_watchs_dom + con_watchs; ); (Hashtbl.length cons.anonymous, !nb_ops_anon, !nb_watchs_anon, Hashtbl.length cons.domains, !nb_ops_dom, !nb_watchs_dom) let debug cons = let anonymous = Hashtbl.fold (fun _ con accu -> Connection.debug con :: accu) cons.anonymous [] in let domains = Hashtbl.fold (fun _ con accu -> Connection.debug con :: accu) cons.domains [] in String.concat "" (domains @ anonymous) xen-4.9.2/tools/ocaml/xenstored/systemd.ml0000664000175000017500000000122413256712137016744 0ustar smbsmb(* * Copyright (C) 2014 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) external sd_notify_ready: unit -> unit = "ocaml_sd_notify_ready" xen-4.9.2/tools/ocaml/xenstored/Makefile0000664000175000017500000000455113256712137016370 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. OCAML_TOPLEVEL = $(CURDIR)/.. include $(OCAML_TOPLEVEL)/common.make # Include configure output (config.h) CFLAGS += -include $(XEN_ROOT)/tools/config.h CFLAGS-$(CONFIG_SYSTEMD) += $(SYSTEMD_CFLAGS) LDFLAGS-$(CONFIG_SYSTEMD) += $(SYSTEMD_LIBS) CFLAGS += $(CFLAGS-y) CFLAGS += $(APPEND_CFLAGS) LDFLAGS += $(LDFLAGS-y) LDFLAGS += $(APPEND_LDFLAGS) OCAMLINCLUDE += \ -I $(OCAML_TOPLEVEL)/libs/xb \ -I $(OCAML_TOPLEVEL)/libs/mmap \ -I $(OCAML_TOPLEVEL)/libs/xc \ -I $(OCAML_TOPLEVEL)/libs/eventchn LIBS = syslog.cma syslog.cmxa select.cma select.cmxa syslog_OBJS = syslog syslog_C_OBJS = syslog_stubs select_OBJS = select select_C_OBJS = select_stubs OCAML_LIBRARY = syslog select LIBS += systemd.cma systemd.cmxa systemd_OBJS = systemd systemd_C_OBJS = systemd_stubs OCAML_LIBRARY += systemd $(foreach obj,$(systemd_C_OBJS),$(obj).o): _paths.h LIBS_systemd += $(LDFLAGS-y) OBJS = paths \ define \ stdext \ trie \ config \ packet \ logging \ quota \ perms \ symbol \ utils \ store \ disk \ transaction \ event \ domain \ domains \ connection \ connections \ history \ parse_arg \ process \ xenstored INTF = symbol.cmi trie.cmi syslog.cmi systemd.cmi select.cmi XENSTOREDLIBS = \ unix.cmxa \ -ccopt -L -ccopt . syslog.cmxa \ -ccopt -L -ccopt . systemd.cmxa \ -ccopt -L -ccopt . select.cmxa \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/mmap $(OCAML_TOPLEVEL)/libs/mmap/xenmmap.cmxa \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/eventchn $(OCAML_TOPLEVEL)/libs/eventchn/xeneventchn.cmxa \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xc $(OCAML_TOPLEVEL)/libs/xc/xenctrl.cmxa \ -ccopt -L -ccopt $(OCAML_TOPLEVEL)/libs/xb $(OCAML_TOPLEVEL)/libs/xb/xenbus.cmxa \ -ccopt -L -ccopt $(XEN_ROOT)/tools/libxc PROGRAMS = oxenstored oxenstored_LIBS = $(XENSTOREDLIBS) oxenstored_OBJS = $(OBJS) OCAML_PROGRAM = oxenstored all: $(INTF) $(LIBS) $(PROGRAMS) bins: $(PROGRAMS) libs: $(LIBS) install: all $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_PROG) oxenstored $(DESTDIR)$(sbindir) $(INSTALL_DIR) $(DESTDIR)$(XEN_CONFIG_DIR) $(INSTALL_DATA) oxenstored.conf $(DESTDIR)$(XEN_CONFIG_DIR) include $(OCAML_TOPLEVEL)/Makefile.rules genpath-target = $(call buildmakevars2module,paths.ml) $(eval $(genpath-target)) GENERATED_FILES += paths.ml genpath-target = $(call buildmakevars2header,_paths.h) $(eval $(genpath-target)) GENERATE_FILES += _paths.h xen-4.9.2/tools/ocaml/xenstored/config.ml0000664000175000017500000000655313256712137016533 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type ty = | Set_bool of bool ref | Set_int of int ref | Set_string of string ref | Set_float of float ref | Unit of (unit -> unit) | Bool of (bool -> unit) | Int of (int -> unit) | String of (string -> unit) | Float of (float -> unit) exception Error of (string * string) list let trim_start lc s = let len = String.length s and i = ref 0 in while !i < len && (List.mem s.[!i] lc) do incr i done; if !i < len then String.sub s !i (len - !i) else "" let trim_end lc s = let i = ref (String.length s - 1) in while !i > 0 && (List.mem s.[!i] lc) do decr i done; if !i >= 0 then String.sub s 0 (!i + 1) else "" let rec split ?limit:(limit=(-1)) c s = let i = try String.index s c with Not_found -> -1 in let nlimit = if limit = -1 || limit = 0 then limit else limit - 1 in if i = -1 || nlimit = 0 then [ s ] else let a = String.sub s 0 i and b = String.sub s (i + 1) (String.length s - i - 1) in a :: (split ~limit: nlimit c b) let parse_line stream = let lc = [ ' '; '\t' ] in let trim_spaces s = trim_end lc (trim_start lc s) in let to_config s = match split ~limit:2 '=' s with | k :: v :: [] -> Some (trim_end lc k, trim_start lc v) | _ -> None in let rec read_filter_line () = try let line = trim_spaces (input_line stream) in if String.length line > 0 && line.[0] <> '#' then match to_config line with | None -> read_filter_line () | Some x -> x :: read_filter_line () else read_filter_line () with End_of_file -> [] in read_filter_line () let parse filename = let stream = open_in filename in let cf = parse_line stream in close_in stream; cf let validate cf expected other = let err = ref [] in let append x = err := x :: !err in List.iter (fun (k, v) -> try if not (List.mem_assoc k expected) then other k v else let ty = List.assoc k expected in match ty with | Unit f -> f () | Bool f -> f (bool_of_string v) | String f -> f v | Int f -> f (int_of_string v) | Float f -> f (float_of_string v) | Set_bool r -> r := (bool_of_string v) | Set_string r -> r := v | Set_int r -> r := int_of_string v | Set_float r -> r := (float_of_string v) with | Not_found -> append (k, "unknown key") | Failure "int_of_string" -> append (k, "expect int arg") | Failure "bool_of_string" -> append (k, "expect bool arg") | Failure "float_of_string" -> append (k, "expect float arg") | exn -> append (k, Printexc.to_string exn) ) cf; if !err != [] then raise (Error !err) (** read a filename, parse and validate, and return the errors if any *) let read filename expected other = let cf = parse filename in validate cf expected other xen-4.9.2/tools/ocaml/xenstored/event.ml0000664000175000017500000000256613256712137016407 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) (**************** high level binding ****************) type t = { handle: Xeneventchn.handle; mutable virq_port: Xeneventchn.t option; } let init () = { handle = Xeneventchn.init (); virq_port = None; } let fd eventchn = Xeneventchn.fd eventchn.handle let bind_dom_exc_virq eventchn = eventchn.virq_port <- Some (Xeneventchn.bind_dom_exc_virq eventchn.handle) let bind_interdomain eventchn domid port = Xeneventchn.bind_interdomain eventchn.handle domid port let unbind eventchn port = Xeneventchn.unbind eventchn.handle port let notify eventchn port = Xeneventchn.notify eventchn.handle port let pending eventchn = Xeneventchn.pending eventchn.handle let unmask eventchn port = Xeneventchn.unmask eventchn.handle port xen-4.9.2/tools/ocaml/xenstored/perms.ml0000664000175000017500000001046613256712137016412 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) let info fmt = Logging.info "perms" fmt open Stdext let activate = ref true type permty = READ | WRITE | RDWR | NONE let char_of_permty perm = match perm with | READ -> 'r' | WRITE -> 'w' | RDWR -> 'b' | NONE -> 'n' let permty_of_char c = match c with | 'r' -> READ | 'w' -> WRITE | 'b' -> RDWR | 'n' -> NONE | _ -> invalid_arg "unknown permission type" (* node permissions *) module Node = struct type t = { owner: Xenctrl.domid; other: permty; acl: (Xenctrl.domid * permty) list; } let create owner other acl = { owner = owner; other = other; acl = acl } let get_other perms = perms.other let get_acl perms = perms.acl let get_owner perm = perm.owner let default0 = create 0 NONE [] let perm_of_string s = let ty = permty_of_char s.[0] and id = int_of_string (String.sub s 1 (String.length s - 1)) in (id, ty) let of_strings ls = let vect = List.map (perm_of_string) ls in match vect with | [] -> invalid_arg "permvec empty" | h :: l -> create (fst h) (snd h) l (* [s] must end with '\000' *) let of_string s = let ls = String.split '\000' s in let ls = if ls = [] then ls else List.rev (List.tl (List.rev ls)) in of_strings ls let string_of_perm perm = Printf.sprintf "%c%u" (char_of_permty (snd perm)) (fst perm) let to_string permvec = let l = ((permvec.owner, permvec.other) :: permvec.acl) in String.concat "\000" (List.map string_of_perm l) end (* permission of connections *) module Connection = struct type elt = Xenctrl.domid * (permty list) type t = { main: elt; target: elt option; } let full_rights : t = { main = 0, [READ; WRITE]; target = None } let create ?(perms=[NONE]) domid : t = { main = (domid, perms); target = None } let set_target (connection:t) ?(perms=[NONE]) domid = { connection with target = Some (domid, perms) } let get_owners (connection:t) = match connection.main, connection.target with | c1, Some c2 -> [ fst c1; fst c2 ] | c1, None -> [ fst c1 ] let is_owner (connection:t) id = match connection.target with | Some target -> fst connection.main = id || fst target = id | None -> fst connection.main = id let is_dom0 (connection:t) = is_owner connection 0 let elt_to_string (i,p) = Printf.sprintf "%i%S" i (String.concat "" (List.map String.of_char (List.map char_of_permty p))) let to_string connection = Printf.sprintf "%s%s" (elt_to_string connection.main) (default "" (may elt_to_string connection.target)) end (* check if owner of the current connection and of the current node are the same *) let check_owner (connection:Connection.t) (node:Node.t) = if !activate && not (Connection.is_dom0 connection) then Connection.is_owner connection (Node.get_owner node) else true (* check if the current connection has the requested perm on the current node *) let check (connection:Connection.t) request (node:Node.t) = let check_acl domainid = let perm = if List.mem_assoc domainid (Node.get_acl node) then List.assoc domainid (Node.get_acl node) else Node.get_other node in match perm, request with | NONE, _ -> info "Permission denied: Domain %d has no permission" domainid; false | RDWR, _ -> true | READ, READ -> true | WRITE, WRITE -> true | READ, _ -> info "Permission denied: Domain %d has read only access" domainid; false | WRITE, _ -> info "Permission denied: Domain %d has write only access" domainid; false in if !activate && not (Connection.is_dom0 connection) && not (check_owner connection node) && not (List.exists check_acl (Connection.get_owners connection)) then raise Define.Permission_denied let equiv perm1 perm2 = (Node.to_string perm1) = (Node.to_string perm2) xen-4.9.2/tools/ocaml/xenstored/trie.ml0000664000175000017500000001055413256712137016225 0ustar smbsmb(* * Copyright (C) 2008-2009 Citrix Ltd. * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) module Node = struct type ('a,'b) t = { key: 'a; value: 'b option; children: ('a,'b) t list; } let create key value = { key = key; value = Some value; children = []; } let empty key = { key = key; value = None; children = [] } let get_key node = node.key let get_value node = match node.value with | None -> raise Not_found | Some value -> value let get_children node = node.children let set_value node value = { node with value = Some value } let set_children node children = { node with children = children } let add_child node child = { node with children = child :: node.children } end type ('a,'b) t = ('a,'b) Node.t list let mem_node nodes key = List.exists (fun n -> n.Node.key = key) nodes let find_node nodes key = List.find (fun n -> n.Node.key = key) nodes let replace_node nodes key node = let rec aux = function | [] -> [] | h :: tl when h.Node.key = key -> node :: tl | h :: tl -> h :: aux tl in aux nodes let remove_node nodes key = let rec aux = function | [] -> raise Not_found | h :: tl when h.Node.key = key -> tl | h :: tl -> h :: aux tl in aux nodes let create () = [] let rec iter f tree = let rec aux node = f node.Node.key node.Node.value; iter f node.Node.children in List.iter aux tree let rec map f tree = let rec aux node = let value = match node.Node.value with | None -> None | Some value -> f value in { node with Node.value = value; Node.children = map f node.Node.children } in List.filter (fun n -> n.Node.value <> None || n.Node.children <> []) (List.map aux tree) let rec fold f tree acc = let rec aux accu node = fold f node.Node.children (f node.Node.key node.Node.value accu) in List.fold_left aux acc tree (* return a sub-trie *) let rec sub_node tree = function | [] -> raise Not_found | h::t -> if mem_node tree h then begin let node = find_node tree h in if t = [] then node else sub_node node.Node.children t end else raise Not_found let sub tree path = try (sub_node tree path).Node.children with Not_found -> [] let find tree path = Node.get_value (sub_node tree path) (* return false if the node doesn't exists or if it is not associated to any value *) let rec mem tree = function | [] -> false | h::t -> mem_node tree h && (let node = find_node tree h in if t = [] then node.Node.value <> None else mem node.Node.children t) (* Iterate over the longest valid prefix *) let rec iter_path f tree = function | [] -> () | h::l -> if mem_node tree h then begin let node = find_node tree h in f node.Node.key node.Node.value; iter_path f node.Node.children l end let rec set_node node path value = if path = [] then Node.set_value node value else begin let children = set node.Node.children path value in Node.set_children node children end and set tree path value = match path with | [] -> raise Not_found | h::t -> if mem_node tree h then begin let node = find_node tree h in replace_node tree h (set_node node t value) end else begin let node = Node.empty h in set_node node t value :: tree end let rec unset tree = function | [] -> tree | h::t -> if mem_node tree h then begin let node = find_node tree h in let children = unset node.Node.children t in let new_node = if t = [] then Node.set_children (Node.empty h) children else Node.set_children node children in if children = [] && new_node.Node.value = None then remove_node tree h else replace_node tree h new_node end else raise Not_found xen-4.9.2/tools/ocaml/xenstored/systemd.mli0000664000175000017500000000126513256712137017122 0ustar smbsmb(* * Copyright (C) 2014 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) (** Tells systemd we're ready *) external sd_notify_ready: unit -> unit = "ocaml_sd_notify_ready" xen-4.9.2/tools/ocaml/xenstored/xenstored.ml0000664000175000017500000004143313256712137017275 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) open Printf open Parse_arg open Stdext let error fmt = Logging.error "xenstored" fmt let debug fmt = Logging.debug "xenstored" fmt let info fmt = Logging.info "xenstored" fmt (*------------ event klass processors --------------*) let process_connection_fds store cons domains rset wset = let try_fct fct c = try fct store cons domains c with | Unix.Unix_error(err, "write", _) -> Connections.del_anonymous cons c; error "closing socket connection: write error: %s" (Unix.error_message err) | Unix.Unix_error(err, "read", _) -> Connections.del_anonymous cons c; if err <> Unix.ECONNRESET then error "closing socket connection: read error: %s" (Unix.error_message err) | Xenbus.Xb.End_of_file -> Connections.del_anonymous cons c; debug "closing socket connection" in let process_fdset_with fds fct = List.iter (fun fd -> try try_fct fct (Connections.find cons fd) with Not_found -> () ) fds in process_fdset_with rset Process.do_input; process_fdset_with wset Process.do_output let process_domains store cons domains = let do_io_domain domain = if Domain.is_bad_domain domain || Domain.get_io_credit domain <= 0 || Domain.is_paused_for_conflict domain then () (* nothing to do *) else ( let con = Connections.find_domain cons (Domain.get_id domain) in Process.do_input store cons domains con; Process.do_output store cons domains con; Domain.decr_io_credit domain ) in Domains.iter domains do_io_domain let sigusr1_handler store = try let channel = open_out_gen [ Open_wronly; Open_creat; Open_trunc; ] 0o600 (Paths.xen_run_stored ^ "/db.debug") in finally (fun () -> Store.dump store channel) (fun () -> close_out channel) with _ -> () let sighup_handler _ = maybe (fun logger -> logger.Logging.restart()) !Logging.xenstored_logger; maybe (fun logger -> logger.Logging.restart()) !Logging.access_logger let config_filename cf = match cf.config_file with | Some name -> name | None -> Define.default_config_dir ^ "/oxenstored.conf" let default_pidfile = Paths.xen_run_dir ^ "/xenstored.pid" let ring_scan_interval = ref 20 let parse_config filename = let pidfile = ref default_pidfile in let options = [ ("merge-activate", Config.Set_bool Transaction.do_coalesce); ("conflict-burst-limit", Config.Set_float Define.conflict_burst_limit); ("conflict-max-history-seconds", Config.Set_float Define.conflict_max_history_seconds); ("conflict-rate-limit-is-aggregate", Config.Set_bool Define.conflict_rate_limit_is_aggregate); ("perms-activate", Config.Set_bool Perms.activate); ("quota-activate", Config.Set_bool Quota.activate); ("quota-maxwatch", Config.Set_int Define.maxwatch); ("quota-transaction", Config.Set_int Define.maxtransaction); ("quota-maxentity", Config.Set_int Quota.maxent); ("quota-maxsize", Config.Set_int Quota.maxsize); ("quota-maxrequests", Config.Set_int Define.maxrequests); ("test-eagain", Config.Set_bool Transaction.test_eagain); ("persistent", Config.Set_bool Disk.enable); ("xenstored-log-file", Config.String Logging.set_xenstored_log_destination); ("xenstored-log-level", Config.String (fun s -> Logging.xenstored_log_level := Logging.level_of_string s)); ("xenstored-log-nb-files", Config.Set_int Logging.xenstored_log_nb_files); ("xenstored-log-nb-lines", Config.Set_int Logging.xenstored_log_nb_lines); ("xenstored-log-nb-chars", Config.Set_int Logging.xenstored_log_nb_chars); ("access-log-file", Config.String Logging.set_access_log_destination); ("access-log-nb-files", Config.Set_int Logging.access_log_nb_files); ("access-log-nb-lines", Config.Set_int Logging.access_log_nb_lines); ("access-log-nb-chars", Config.Set_int Logging.access_log_nb_chars); ("access-log-read-ops", Config.Set_bool Logging.access_log_read_ops); ("access-log-transactions-ops", Config.Set_bool Logging.access_log_transaction_ops); ("access-log-special-ops", Config.Set_bool Logging.access_log_special_ops); ("allow-debug", Config.Set_bool Process.allow_debug); ("ring-scan-interval", Config.Set_int ring_scan_interval); ("pid-file", Config.Set_string pidfile); ("xenstored-kva", Config.Set_string Domains.xenstored_kva); ("xenstored-port", Config.Set_string Domains.xenstored_port); ] in begin try Config.read filename options (fun _ _ -> raise Not_found) with | Config.Error err -> List.iter (fun (k, e) -> match e with | "unknown key" -> eprintf "config: unknown key %s\n" k | _ -> eprintf "config: %s: %s\n" k e ) err; | Sys_error m -> eprintf "error: config: %s\n" m; end; !pidfile module DB = struct exception Bad_format of string let dump_format_header = "$xenstored-dump-format" let from_channel_f chan domain_f watch_f store_f = let unhexify s = Utils.unhexify s in let getpath s = Store.Path.of_string (Utils.unhexify s) in let header = input_line chan in if header <> dump_format_header then raise (Bad_format "header"); let quit = ref false in while not !quit do try let line = input_line chan in let l = String.split ',' line in try match l with | "dom" :: domid :: mfn :: port :: []-> domain_f (int_of_string domid) (Nativeint.of_string mfn) (int_of_string port) | "watch" :: domid :: path :: token :: [] -> watch_f (int_of_string domid) (unhexify path) (unhexify token) | "store" :: path :: perms :: value :: [] -> store_f (getpath path) (Perms.Node.of_string (unhexify perms ^ "\000")) (unhexify value) | _ -> info "restoring: ignoring unknown line: %s" line with exn -> info "restoring: ignoring unknown line: %s (exception: %s)" line (Printexc.to_string exn); () with End_of_file -> quit := true done; () let from_channel store cons doms chan = (* don't let the permission get on our way, full perm ! *) let op = Store.get_ops store Perms.Connection.full_rights in let xc = Xenctrl.interface_open () in let domain_f domid mfn port = let ndom = if domid > 0 then Domains.create xc doms domid mfn port else Domains.create0 doms in Connections.add_domain cons ndom; in let watch_f domid path token = let con = Connections.find_domain cons domid in ignore (Connections.add_watch cons con path token) in let store_f path perms value = op.Store.write path value; op.Store.setperms path perms in finally (fun () -> from_channel_f chan domain_f watch_f store_f) (fun () -> Xenctrl.interface_close xc) let from_file store cons doms file = let channel = open_in file in finally (fun () -> from_channel store doms cons channel) (fun () -> close_in channel) let to_channel store cons chan = let hexify s = Utils.hexify s in fprintf chan "%s\n" dump_format_header; (* dump connections related to domains; domid, mfn, eventchn port, watches *) Connections.iter_domains cons (fun con -> Connection.dump con chan); (* dump the store *) Store.dump_fct store (fun path node -> let name, perms, value = Store.Node.unpack node in let fullpath = Store.Path.to_string (Store.Path.of_path_and_name path name) in let permstr = Perms.Node.to_string perms in fprintf chan "store,%s,%s,%s\n" (hexify fullpath) (hexify permstr) (hexify value) ); flush chan; () let to_file store cons file = let channel = open_out_gen [ Open_wronly; Open_creat; Open_trunc; ] 0o600 file in finally (fun () -> to_channel store cons channel) (fun () -> close_out channel) end let _ = let cf = do_argv in let pidfile = if Sys.file_exists (config_filename cf) then parse_config (config_filename cf) else default_pidfile in (try Unixext.mkdir_rec (Filename.dirname pidfile) 0o755 with _ -> () ); let rw_sock, ro_sock = if cf.disable_socket then None, None else Some (Unix.handle_unix_error Utils.create_unix_socket Define.xs_daemon_socket), Some (Unix.handle_unix_error Utils.create_unix_socket Define.xs_daemon_socket_ro) in if cf.daemonize then Unixext.daemonize () else printf "Xen Storage Daemon, version %d.%d\n%!" Define.xenstored_major Define.xenstored_minor; (try Unixext.pidfile_write pidfile with _ -> ()); (* for compatilibity with old xenstored *) begin match cf.pidfile with | Some pidfile -> Unixext.pidfile_write pidfile | None -> () end; let store = Store.create () in let eventchn = Event.init () in let next_frequent_ops = ref 0. in let advance_next_frequent_ops () = next_frequent_ops := (Unix.gettimeofday () +. !Define.conflict_max_history_seconds) in let delay_next_frequent_ops_by duration = next_frequent_ops := !next_frequent_ops +. duration in let domains = Domains.init eventchn advance_next_frequent_ops in (* For things that need to be done periodically but more often * than the periodic_ops function *) let frequent_ops () = if Unix.gettimeofday () > !next_frequent_ops then ( History.trim (); Domains.incr_conflict_credit domains; advance_next_frequent_ops () ) in let cons = Connections.create () in let quit = ref false in Logging.init_xenstored_log(); let filename = Paths.xen_run_stored ^ "/db" in if cf.restart && Sys.file_exists filename then ( DB.from_file store domains cons filename; Event.bind_dom_exc_virq eventchn ) else ( if !Disk.enable then ( info "reading store from disk"; Disk.read store ); let localpath = Store.Path.of_string "/local" in if not (Store.path_exists store localpath) then Store.mkdir store (Perms.Connection.create 0) localpath; if cf.domain_init then ( Connections.add_domain cons (Domains.create0 domains); Event.bind_dom_exc_virq eventchn ); ); Select.use_poll (not cf.use_select); Sys.set_signal Sys.sighup (Sys.Signal_handle sighup_handler); Sys.set_signal Sys.sigterm (Sys.Signal_handle (fun i -> quit := true)); Sys.set_signal Sys.sigusr1 (Sys.Signal_handle (fun i -> sigusr1_handler store)); Sys.set_signal Sys.sigpipe Sys.Signal_ignore; if cf.activate_access_log then begin let post_rotate () = DB.to_file store cons (Paths.xen_run_stored ^ "/db") in Logging.init_access_log post_rotate end; let spec_fds = (match rw_sock with None -> [] | Some x -> [ x ]) @ (match ro_sock with None -> [] | Some x -> [ x ]) @ (if cf.domain_init then [ Event.fd eventchn ] else []) in let xc = Xenctrl.interface_open () in let process_special_fds rset = let accept_connection can_write fd = let (cfd, addr) = Unix.accept fd in debug "new connection through socket"; Connections.add_anonymous cons cfd can_write and handle_eventchn fd = let port = Event.pending eventchn in debug "pending port %d" (Xeneventchn.to_int port); finally (fun () -> if Some port = eventchn.Event.virq_port then ( let (notify, deaddom) = Domains.cleanup xc domains in List.iter (Connections.del_domain cons) deaddom; if deaddom <> [] || notify then Connections.fire_spec_watches cons "@releaseDomain" ) else let c = Connections.find_domain_by_port cons port in match Connection.get_domain c with | Some dom -> Domain.incr_io_credit dom | None -> () ) (fun () -> Event.unmask eventchn port) and do_if_set fd set fct = if List.mem fd set then fct fd in maybe (fun fd -> do_if_set fd rset (accept_connection true)) rw_sock; maybe (fun fd -> do_if_set fd rset (accept_connection false)) ro_sock; do_if_set (Event.fd eventchn) rset (handle_eventchn) in let ring_scan_checker dom = (* no need to scan domains already marked as for processing *) if not (Domain.get_io_credit dom > 0) then let con = Connections.find_domain cons (Domain.get_id dom) in if not (Connection.has_more_work con) then ( Process.do_output store cons domains con; Process.do_input store cons domains con; if Connection.has_more_work con then (* Previously thought as no work, but detect some after scan (as processing a new message involves multiple steps.) It's very likely to be a "lazy" client, bump its credit. It could be false positive though (due to time window), but it's no harm to give a domain extra credit. *) let n = 32 + 2 * (Domains.number domains) in info "found lazy domain %d, credit %d" (Domain.get_id dom) n; Domain.set_io_credit ~n dom ) in let last_stat_time = ref 0. in let last_scan_time = ref 0. in let periodic_ops now = debug "periodic_ops starting"; (* we garbage collect the string->int dictionary after a sizeable amount of operations, * there's no need to be really fast even if we got loose * objects since names are often reuse. *) if Symbol.created () > 1000 || Symbol.used () > 20000 then begin Symbol.mark_all_as_unused (); Store.mark_symbols store; Connections.iter cons Connection.mark_symbols; History.mark_symbols (); Symbol.garbage () end; (* scan all the xs rings as a safenet for ill-behaved clients *) if !ring_scan_interval >= 0 && now > (!last_scan_time +. float !ring_scan_interval) then (last_scan_time := now; Domains.iter domains ring_scan_checker); (* make sure we don't print general stats faster than 2 min *) if now > (!last_stat_time +. 120.) then ( info "Transaction conflict statistics for last %F seconds:" (now -. !last_stat_time); last_stat_time := now; Domains.iter domains (Domain.log_and_reset_conflict_stats (info "Dom%d caused %Ld conflicts")); info "%Ld failed transactions; of these no culprit was found for %Ld" !Transaction.failed_commits !Transaction.failed_commits_no_culprit; Transaction.reset_conflict_stats (); let gc = Gc.stat () in let (lanon, lanon_ops, lanon_watchs, ldom, ldom_ops, ldom_watchs) = Connections.stats cons in let store_nodes, store_abort, store_coalesce = Store.stats store in let symtbl_len = Symbol.stats () in info "store stat: nodes(%d) t-abort(%d) t-coalesce(%d)" store_nodes store_abort store_coalesce; info "sytbl stat: %d" symtbl_len; info " con stat: anonymous(%d, %d o, %d w) domains(%d, %d o, %d w)" lanon lanon_ops lanon_watchs ldom ldom_ops ldom_watchs; info " mem stat: minor(%.0f) promoted(%.0f) major(%.0f) heap(%d w, %d c) live(%d w, %d b) free(%d w, %d b)" gc.Gc.minor_words gc.Gc.promoted_words gc.Gc.major_words gc.Gc.heap_words gc.Gc.heap_chunks gc.Gc.live_words gc.Gc.live_blocks gc.Gc.free_words gc.Gc.free_blocks ); let elapsed = Unix.gettimeofday () -. now in debug "periodic_ops took %F seconds." elapsed; delay_next_frequent_ops_by elapsed in let period_ops_interval = 15. in let period_start = ref 0. in let main_loop () = let is_peaceful c = match Connection.get_domain c with | None -> true (* Treat socket-connections as exempt, and free to conflict. *) | Some dom -> not (Domain.is_paused_for_conflict dom) in frequent_ops (); let mw = Connections.has_more_work cons in let peaceful_mw = List.filter is_peaceful mw in List.iter (fun c -> match Connection.get_domain c with | None -> () | Some d -> Domain.incr_io_credit d) peaceful_mw; let start_time = Unix.gettimeofday () in let timeout = let until_next_activity = if Domains.all_at_max_credit domains then period_ops_interval else min (max 0. (!next_frequent_ops -. start_time)) period_ops_interval in if peaceful_mw <> [] then 0. else until_next_activity in let inset, outset = Connections.select ~only_if:is_peaceful cons in let rset, wset, _ = try Select.select (spec_fds @ inset) outset [] timeout with Unix.Unix_error(Unix.EINTR, _, _) -> [], [], [] in let sfds, cfds = List.partition (fun fd -> List.mem fd spec_fds) rset in if List.length sfds > 0 then process_special_fds sfds; if List.length cfds > 0 || List.length wset > 0 then process_connection_fds store cons domains cfds wset; if timeout <> 0. then ( let now = Unix.gettimeofday () in if now > !period_start +. period_ops_interval then (period_start := now; periodic_ops now) ); process_domains store cons domains in Systemd.sd_notify_ready (); while not !quit do try main_loop () with exc -> error "caught exception %s" (Printexc.to_string exc); if cf.reraise_top_level then raise exc done; info "stopping xenstored"; DB.to_file store cons (Paths.xen_run_stored ^ "/db"); () xen-4.9.2/tools/ocaml/xenstored/trie.mli0000664000175000017500000000540213256712137016372 0ustar smbsmb(* * Copyright (C) 2008-2009 Citrix Ltd. * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) (** Basic Implementation of polymorphic tries (ie. prefix trees) *) type ('a, 'b) t (** The type of tries. ['a list] is the type of keys, ['b] the type of values. Internally, a trie is represented as a labeled tree, where node contains values of type ['a * 'b option]. *) val create : unit -> ('a,'b) t (** Creates an empty trie. *) val mem : ('a,'b) t -> 'a list -> bool (** [mem t k] returns true if a value is associated with the key [k] in the trie [t]. Otherwise, it returns false. *) val find : ('a, 'b) t -> 'a list -> 'b (** [find t k] returns the value associated with the key [k] in the trie [t]. Returns [Not_found] if no values are associated with [k] in [t]. *) val set : ('a, 'b) t -> 'a list -> 'b -> ('a, 'b) t (** [set t k v] associates the value [v] with the key [k] in the trie [t]. *) val unset : ('a, 'b) t -> 'a list -> ('a, 'b) t (** [unset k v] removes the association of value [v] with the key [k] in the trie [t]. Moreover, it automatically clean the trie, ie. it removes recursively every nodes of [t] containing no values and having no chil. *) val iter : ('a -> 'b option -> unit) -> ('a, 'b) t -> unit (** [iter f t] applies the function [f] to every node of the trie [t]. As nodes of the trie [t] do not necessary contains a value, the second argument of [f] is an option type. *) val iter_path : ('a -> 'b option -> unit) -> ('a, 'b) t -> 'a list -> unit (** [iter_path f t p] iterates [f] over nodes associated with the path [p] in the trie [t]. If [p] is not a valid path of [t], it iterates on the longest valid prefix of [p]. *) val fold : ('a -> 'b option -> 'c -> 'c) -> ('a, 'b) t -> 'c -> 'c (** [fold f t x] fold [f] over every nodes of [t], with [x] as initial value. *) val map : ('b -> 'c option) -> ('a,'b) t -> ('a,'c) t (** [map f t] maps [f] over every values stored in [t]. The return value of [f] is of type 'c option as one may wants to remove value associated to a key. This function is not tail-recursive. *) val sub : ('a, 'b) t -> 'a list -> ('a,'b) t (** [sub t p] returns the sub-trie associated with the path [p] in the trie [t]. If [p] is not a valid path of [t], it returns an empty trie. *) xen-4.9.2/tools/ocaml/xenstored/systemd_stubs.c0000664000175000017500000000227713256712137020007 0ustar smbsmb/* * Copyright (C) 2014 Luis R. Rodriguez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_SYSTEMD) #include #include "_paths.h" CAMLprim value ocaml_sd_notify_ready(value ignore) { CAMLparam1(ignore); CAMLlocal1(ret); ret = Val_int(0); sd_notify(1, "READY=1"); CAMLreturn(ret); } #else CAMLprim value ocaml_sd_notify_ready(value ignore) { CAMLparam1(ignore); CAMLlocal1(ret); ret = Val_int(-1U); CAMLreturn(ret); } #endif xen-4.9.2/tools/ocaml/xenstored/domains.ml0000664000175000017500000001726413256712137016721 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) let debug fmt = Logging.debug "domains" fmt let error fmt = Logging.error "domains" fmt let warn fmt = Logging.warn "domains" fmt type domains = { eventchn: Event.t; table: (Xenctrl.domid, Domain.t) Hashtbl.t; (* N.B. the Queue module is not thread-safe but oxenstored is single-threaded. *) (* Domains queue up to regain conflict-credit; we have a queue for domains that are carrying some penalty and so are below the maximum credit, and another queue for domains that have run out of credit and so have had their access paused. *) doms_conflict_paused: (Domain.t option ref) Queue.t; doms_with_conflict_penalty: (Domain.t option ref) Queue.t; (* A callback function to be called when we go from zero to one paused domain. This will be to reset the countdown until the next unit of credit is issued. *) on_first_conflict_pause: unit -> unit; (* If config is set to use individual instead of aggregate conflict-rate-limiting, we use these counts instead of the queues. The second one includes the first. *) mutable n_paused: int; (* Number of domains with zero or negative credit *) mutable n_penalised: int; (* Number of domains with less than maximum credit *) } let init eventchn on_first_conflict_pause = { eventchn = eventchn; table = Hashtbl.create 10; doms_conflict_paused = Queue.create (); doms_with_conflict_penalty = Queue.create (); on_first_conflict_pause = on_first_conflict_pause; n_paused = 0; n_penalised = 0; } let del doms id = Hashtbl.remove doms.table id let exist doms id = Hashtbl.mem doms.table id let find doms id = Hashtbl.find doms.table id let number doms = Hashtbl.length doms.table let iter doms fct = Hashtbl.iter (fun _ b -> fct b) doms.table let rec is_empty_queue q = Queue.is_empty q || if !(Queue.peek q) = None then ( ignore (Queue.pop q); is_empty_queue q ) else false let all_at_max_credit doms = if !Define.conflict_rate_limit_is_aggregate then (* Check both becuase if burst limit is 1.0 then a domain can go straight * from max-credit to paused without getting into the penalty queue. *) is_empty_queue doms.doms_with_conflict_penalty && is_empty_queue doms.doms_conflict_paused else doms.n_penalised = 0 (* Functions to handle queues of domains given that the domain might be deleted while in a queue. *) let push dom queue = Queue.push (ref (Some dom)) queue let rec pop queue = match !(Queue.pop queue) with | None -> pop queue | Some x -> x let remove_from_queue dom queue = Queue.iter (fun d -> match !d with | None -> () | Some x -> if x=dom then d := None) queue let cleanup xc doms = let notify = ref false in let dead_dom = ref [] in Hashtbl.iter (fun id _ -> if id <> 0 then try let info = Xenctrl.domain_getinfo xc id in if info.Xenctrl.shutdown || info.Xenctrl.dying then ( debug "Domain %u died (dying=%b, shutdown %b -- code %d)" id info.Xenctrl.dying info.Xenctrl.shutdown info.Xenctrl.shutdown_code; if info.Xenctrl.dying then dead_dom := id :: !dead_dom else notify := true; ) with Xenctrl.Error _ -> debug "Domain %u died -- no domain info" id; dead_dom := id :: !dead_dom; ) doms.table; List.iter (fun id -> let dom = Hashtbl.find doms.table id in Domain.close dom; Hashtbl.remove doms.table id; if dom.Domain.conflict_credit <= !Define.conflict_burst_limit then ( remove_from_queue dom doms.doms_with_conflict_penalty; if (dom.Domain.conflict_credit <= 0.) then remove_from_queue dom doms.doms_conflict_paused ) ) !dead_dom; !notify, !dead_dom let resume doms domid = () let create xc doms domid mfn port = let interface = Xenctrl.map_foreign_range xc domid (Xenmmap.getpagesize()) mfn in let dom = Domain.make domid mfn port interface doms.eventchn in Hashtbl.add doms.table domid dom; Domain.bind_interdomain dom; dom let xenstored_kva = ref "" let xenstored_port = ref "" let create0 doms = let port, interface = ( let port = Utils.read_file_single_integer !xenstored_port and fd = Unix.openfile !xenstored_kva [ Unix.O_RDWR ] 0o600 in let interface = Xenmmap.mmap fd Xenmmap.RDWR Xenmmap.SHARED (Xenmmap.getpagesize()) 0 in Unix.close fd; port, interface ) in let dom = Domain.make 0 Nativeint.zero port interface doms.eventchn in Hashtbl.add doms.table 0 dom; Domain.bind_interdomain dom; Domain.notify dom; dom let decr_conflict_credit doms dom = dom.Domain.caused_conflicts <- Int64.add 1L dom.Domain.caused_conflicts; let before = dom.Domain.conflict_credit in let after = max (-1.0) (before -. 1.0) in debug "decr_conflict_credit dom%d %F -> %F" (Domain.get_id dom) before after; dom.Domain.conflict_credit <- after; let newly_penalised = before >= !Define.conflict_burst_limit && after < !Define.conflict_burst_limit in let newly_paused = before > 0.0 && after <= 0.0 in if !Define.conflict_rate_limit_is_aggregate then ( if newly_penalised && after > 0.0 then ( push dom doms.doms_with_conflict_penalty ) else if newly_paused then ( let first_pause = Queue.is_empty doms.doms_conflict_paused in push dom doms.doms_conflict_paused; if first_pause then doms.on_first_conflict_pause () ) else ( (* The queues are correct already: no further action needed. *) ) ) else ( if newly_penalised then doms.n_penalised <- doms.n_penalised + 1; if newly_paused then ( doms.n_paused <- doms.n_paused + 1; if doms.n_paused = 1 then doms.on_first_conflict_pause () ) ) (* Give one point of credit to one domain, and update the queues appropriately. *) let incr_conflict_credit_from_queue doms = let process_queue q requeue_test = let d = pop q in let before = d.Domain.conflict_credit in (* just for debug-logging *) d.Domain.conflict_credit <- min (d.Domain.conflict_credit +. 1.0) !Define.conflict_burst_limit; debug "incr_conflict_credit_from_queue: dom%d: %F -> %F" (Domain.get_id d) before d.Domain.conflict_credit; if requeue_test d.Domain.conflict_credit then ( push d q (* Make it queue up again for its next point of credit. *) ) in let paused_queue_test cred = cred <= 0.0 in let penalty_queue_test cred = cred < !Define.conflict_burst_limit in try process_queue doms.doms_conflict_paused paused_queue_test with Queue.Empty -> ( try process_queue doms.doms_with_conflict_penalty penalty_queue_test with Queue.Empty -> () (* Both queues are empty: nothing to do here. *) ) let incr_conflict_credit doms = if !Define.conflict_rate_limit_is_aggregate then incr_conflict_credit_from_queue doms else ( (* Give a point of credit to every domain, subject only to the cap. *) let inc dom = let before = dom.Domain.conflict_credit in let after = min (before +. 1.0) !Define.conflict_burst_limit in dom.Domain.conflict_credit <- after; debug "incr_conflict_credit dom%d: %F -> %F" (Domain.get_id dom) before after; if before <= 0.0 && after > 0.0 then doms.n_paused <- doms.n_paused - 1; if before < !Define.conflict_burst_limit && after >= !Define.conflict_burst_limit then doms.n_penalised <- doms.n_penalised - 1 in if doms.n_penalised > 0 then iter doms inc ) xen-4.9.2/tools/ocaml/xenstored/logging.ml0000664000175000017500000002573613256712137016720 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Thomas Gazagnaire * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) open Stdext open Printf (* Logger common *) type log_destination = | File of string | Syslog of Syslog.facility let log_destination_of_string s = let prefix = "syslog:" in let len_prefix = String.length prefix in let len = String.length s in if String.startswith prefix s then Syslog(Syslog.facility_of_string (String.sub s len_prefix (len - len_prefix))) else File s (* The prefix of a log line depends on the log destination *) let prefix log_destination ?level ?key date = match log_destination with | File _ -> let level = match level with | Some x -> Printf.sprintf "|%5s" x | None -> "" in let key = match key with | Some x -> "|" ^ x | None -> "" in Printf.sprintf "[%s%s%s] " date level key | Syslog _ -> let key = match key with | Some x -> "[" ^ x ^ "] " | None -> "" in (* Syslog handles the date and level internally *) key type level = Debug | Info | Warn | Error | Null type logger = { stop: unit -> unit; restart: unit -> unit; rotate: unit -> unit; write: ?level:level -> string -> unit } let truncate_line nb_chars line = if String.length line > nb_chars - 1 then let len = max (nb_chars - 1) 2 in let dst_line = String.create len in String.blit line 0 dst_line 0 (len - 2); dst_line.[len-2] <- '.'; dst_line.[len-1] <- '.'; dst_line else line let log_rotate ref_ch log_file log_nb_files = let file n = sprintf "%s.%i" log_file n in let log_files = let rec aux accu n = if n >= log_nb_files then accu else if n = 1 && Sys.file_exists log_file then aux [log_file,1] 2 else let file = file (n-1) in if Sys.file_exists file then aux ((file, n) :: accu) (n+1) else accu in aux [] 1 in List.iter (fun (f, n) -> Unix.rename f (file n)) log_files; close_out !ref_ch; ref_ch := open_out log_file let make_file_logger log_file log_nb_files log_nb_lines log_nb_chars post_rotate = let channel = ref (open_out_gen [Open_append; Open_creat] 0o644 log_file) in let counter = ref 0 in let stop() = try flush !channel; close_out !channel with _ -> () in let restart() = stop(); channel := open_out_gen [Open_append; Open_creat] 0o644 log_file in let rotate() = log_rotate channel log_file log_nb_files; (post_rotate (): unit); counter := 0 in let write ?level s = let s = if log_nb_chars > 0 then truncate_line log_nb_chars s else s in let s = s ^ "\n" in output_string !channel s; flush !channel; incr counter; if !counter > log_nb_lines then rotate() in { stop=stop; restart=restart; rotate=rotate; write=write } exception Unknown_level of string let int_of_level = function | Debug -> 0 | Info -> 1 | Warn -> 2 | Error -> 3 | Null -> max_int let string_of_level = function | Debug -> "debug" | Info -> "info" | Warn -> "warn" | Error -> "error" | Null -> "null" let level_of_string = function | "debug" -> Debug | "info" -> Info | "warn" -> Warn | "error" -> Error | "null" -> Null | s -> raise (Unknown_level s) let string_of_date () = let time = Unix.gettimeofday () in let tm = Unix.gmtime time in let msec = time -. (floor time) in sprintf "%d%.2d%.2dT%.2d:%.2d:%.2d.%.3dZ" (1900 + tm.Unix.tm_year) (tm.Unix.tm_mon + 1) tm.Unix.tm_mday tm.Unix.tm_hour tm.Unix.tm_min tm.Unix.tm_sec (int_of_float (1000.0 *. msec)) (* We can defer to syslog for log management *) let make_syslog_logger facility = (* When TZ is unset in the environment, each syslog call will stat the /etc/localtime file at least three times during the process. We'd like to avoid this cost given that we are not a mobile environment and we log almost every xenstore entry update/watch. *) let () = let tz_is_set = try String.length (Unix.getenv "TZ") > 0 with Not_found -> false in if not tz_is_set then Unix.putenv "TZ" "/etc/localtime" in let nothing () = () in let write ?level s = let level = match level with | Some Error -> Syslog.Err | Some Warn -> Syslog.Warning | Some Info -> Syslog.Info | Some Debug -> Syslog.Debug | Some Null -> Syslog.Debug | None -> Syslog.Debug in (* Syslog handles the date and level internally *) Syslog.log facility level s in { stop = nothing; restart = nothing; rotate = nothing; write=write } let xenstored_log_destination = ref (File (Paths.xen_log_dir ^ "/xenstored.log")) let xenstored_log_level = ref Warn let xenstored_log_nb_files = ref 10 let xenstored_log_nb_lines = ref 13215 let xenstored_log_nb_chars = ref (-1) let xenstored_logger = ref (None: logger option) let set_xenstored_log_destination s = xenstored_log_destination := log_destination_of_string s let set_xenstored_logger logger = xenstored_logger := Some logger; logger.write ~level:Info (Printf.sprintf "Xen Storage Daemon, version %d.%d" Define.xenstored_major Define.xenstored_minor) let init_xenstored_log () = match !xenstored_log_destination with | File file -> if !xenstored_log_level <> Null && !xenstored_log_nb_files > 0 then let logger = make_file_logger file !xenstored_log_nb_files !xenstored_log_nb_lines !xenstored_log_nb_chars ignore in set_xenstored_logger logger | Syslog facility -> set_xenstored_logger (make_syslog_logger facility) let xenstored_logging level key (fmt: (_,_,_,_) format4) = match !xenstored_logger with | Some logger when int_of_level level >= int_of_level !xenstored_log_level -> let date = string_of_date() in let level' = string_of_level level in let prefix = prefix !xenstored_log_destination ~level:level' ~key date in Printf.ksprintf (fun s -> logger.write ~level (prefix ^ s)) fmt | _ -> Printf.ksprintf ignore fmt let debug key = xenstored_logging Debug key let info key = xenstored_logging Info key let warn key = xenstored_logging Warn key let error key = xenstored_logging Error key (* Access logger *) type access_type = | Coalesce | Conflict | Commit | Newconn | Endconn | XbOp of Xenbus.Xb.Op.operation let string_of_tid ~con tid = if tid = 0 then sprintf "%-12s" con else sprintf "%-12s" (sprintf "%s.%i" con tid) let string_of_access_type = function | Coalesce -> "coalesce " | Conflict -> "conflict " | Commit -> "commit " | Newconn -> "newconn " | Endconn -> "endconn " | XbOp op -> match op with | Xenbus.Xb.Op.Debug -> "debug " | Xenbus.Xb.Op.Directory -> "directory" | Xenbus.Xb.Op.Read -> "read " | Xenbus.Xb.Op.Getperms -> "getperms " | Xenbus.Xb.Op.Watch -> "watch " | Xenbus.Xb.Op.Unwatch -> "unwatch " | Xenbus.Xb.Op.Transaction_start -> "t start " | Xenbus.Xb.Op.Transaction_end -> "t end " | Xenbus.Xb.Op.Introduce -> "introduce" | Xenbus.Xb.Op.Release -> "release " | Xenbus.Xb.Op.Getdomainpath -> "getdomain" | Xenbus.Xb.Op.Isintroduced -> "is introduced" | Xenbus.Xb.Op.Resume -> "resume " | Xenbus.Xb.Op.Write -> "write " | Xenbus.Xb.Op.Mkdir -> "mkdir " | Xenbus.Xb.Op.Rm -> "rm " | Xenbus.Xb.Op.Setperms -> "setperms " | Xenbus.Xb.Op.Reset_watches -> "reset watches" | Xenbus.Xb.Op.Set_target -> "settarget" | Xenbus.Xb.Op.Error -> "error " | Xenbus.Xb.Op.Watchevent -> "w event " | Xenbus.Xb.Op.Invalid -> "invalid " (* | x -> Xenbus.Xb.Op.to_string x *) let sanitize_data data = let data = String.copy data in for i = 0 to String.length data - 1 do if data.[i] = '\000' then data.[i] <- ' ' done; String.escaped data let activate_access_log = ref true let access_log_destination = ref (File (Paths.xen_log_dir ^ "/xenstored-access.log")) let access_log_nb_files = ref 20 let access_log_nb_lines = ref 13215 let access_log_nb_chars = ref 180 let access_log_read_ops = ref false let access_log_transaction_ops = ref false let access_log_special_ops = ref false let access_logger = ref None let set_access_log_destination s = access_log_destination := log_destination_of_string s let init_access_log post_rotate = match !access_log_destination with | File file -> if !access_log_nb_files > 0 then let logger = make_file_logger file !access_log_nb_files !access_log_nb_lines !access_log_nb_chars post_rotate in access_logger := Some logger | Syslog facility -> access_logger := Some (make_syslog_logger facility) let access_logging ~con ~tid ?(data="") ~level access_type = try maybe (fun logger -> let date = string_of_date() in let tid = string_of_tid ~con tid in let access_type = string_of_access_type access_type in let data = sanitize_data data in let prefix = prefix !access_log_destination date in let msg = Printf.sprintf "%s %s %s %s" prefix tid access_type data in logger.write ~level msg) !access_logger with _ -> () let new_connection = access_logging ~level:Debug Newconn let end_connection = access_logging ~level:Debug Endconn let read_coalesce ~tid ~con data = if !access_log_read_ops then access_logging Coalesce ~tid ~con ~data:("read "^data) ~level:Debug let write_coalesce data = access_logging Coalesce ~data:("write "^data) ~level:Debug let conflict = access_logging Conflict ~level:Debug let commit = access_logging Commit ~level:Debug let xb_op ~tid ~con ~ty data = let print = match ty with | Xenbus.Xb.Op.Read | Xenbus.Xb.Op.Directory | Xenbus.Xb.Op.Getperms -> !access_log_read_ops | Xenbus.Xb.Op.Transaction_start | Xenbus.Xb.Op.Transaction_end -> false (* transactions are managed below *) | Xenbus.Xb.Op.Introduce | Xenbus.Xb.Op.Release | Xenbus.Xb.Op.Getdomainpath | Xenbus.Xb.Op.Isintroduced | Xenbus.Xb.Op.Resume -> !access_log_special_ops | _ -> true in if print then access_logging ~tid ~con ~data (XbOp ty) ~level:Info let start_transaction ~tid ~con = if !access_log_transaction_ops && tid <> 0 then access_logging ~tid ~con (XbOp Xenbus.Xb.Op.Transaction_start) ~level:Debug let end_transaction ~tid ~con = if !access_log_transaction_ops && tid <> 0 then access_logging ~tid ~con (XbOp Xenbus.Xb.Op.Transaction_end) ~level:Debug let xb_answer ~tid ~con ~ty data = let print, level = match ty with | Xenbus.Xb.Op.Error when String.startswith "ENOENT" data -> !access_log_read_ops , Warn | Xenbus.Xb.Op.Error -> true , Warn | Xenbus.Xb.Op.Watchevent -> true , Info | _ -> false, Debug in if print then access_logging ~tid ~con ~data (XbOp ty) ~level xen-4.9.2/tools/ocaml/xenstored/packet.ml0000664000175000017500000000057513256712137016533 0ustar smbsmbtype request = { tid: int; rid: int; ty: Xenbus.Xb.Op.operation; data: string; } type response = | Ack of (unit -> unit) (* function is the action to execute after sending the ack *) | Reply of string | Error of string let response_equal a b = match (a, b) with | (Ack _, Ack _) -> true (* just consider the response, not the post-response action *) | (x, y) -> x = y xen-4.9.2/tools/ocaml/xenstored/domain.ml0000664000175000017500000000640713256712137016533 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) open Printf let debug fmt = Logging.debug "domain" fmt let warn fmt = Logging.warn "domain" fmt type t = { id: Xenctrl.domid; mfn: nativeint; remote_port: int; interface: Xenmmap.mmap_interface; eventchn: Event.t; mutable port: Xeneventchn.t option; mutable bad_client: bool; mutable io_credit: int; (* the rounds of ring process left to do, default is 0, usually set to 1 when there is work detected, could also set to n to give "lazy" clients extra credit *) mutable conflict_credit: float; (* Must be positive to perform writes; a commit that later causes conflict with another domain's transaction costs credit. *) mutable caused_conflicts: int64; } let is_dom0 d = d.id = 0 let get_path dom = "/local/domain/" ^ (sprintf "%u" dom.id) let get_id domain = domain.id let get_interface d = d.interface let get_mfn d = d.mfn let get_remote_port d = d.remote_port let get_port d = d.port let is_bad_domain domain = domain.bad_client let mark_as_bad domain = domain.bad_client <- true let get_io_credit domain = domain.io_credit let set_io_credit ?(n=1) domain = domain.io_credit <- max 0 n let incr_io_credit domain = domain.io_credit <- domain.io_credit + 1 let decr_io_credit domain = domain.io_credit <- max 0 (domain.io_credit - 1) let is_paused_for_conflict dom = dom.conflict_credit <= 0.0 let is_free_to_conflict = is_dom0 let string_of_port = function | None -> "None" | Some x -> string_of_int (Xeneventchn.to_int x) let dump d chan = fprintf chan "dom,%d,%nd,%d\n" d.id d.mfn d.remote_port let notify dom = match dom.port with | None -> warn "domain %d: attempt to notify on unknown port" dom.id | Some port -> Event.notify dom.eventchn port let bind_interdomain dom = dom.port <- Some (Event.bind_interdomain dom.eventchn dom.id dom.remote_port); debug "bound domain %d remote port %d to local port %s" dom.id dom.remote_port (string_of_port dom.port) let close dom = debug "domain %d unbound port %s" dom.id (string_of_port dom.port); begin match dom.port with | None -> () | Some port -> Event.unbind dom.eventchn port end; Xenmmap.unmap dom.interface; () let make id mfn remote_port interface eventchn = { id = id; mfn = mfn; remote_port = remote_port; interface = interface; eventchn = eventchn; port = None; bad_client = false; io_credit = 0; conflict_credit = !Define.conflict_burst_limit; caused_conflicts = 0L; } let log_and_reset_conflict_stats logfn dom = if dom.caused_conflicts > 0L then ( logfn dom.id dom.caused_conflicts; dom.caused_conflicts <- 0L ) xen-4.9.2/tools/ocaml/xenstored/define.ml0000664000175000017500000000257113256712137016514 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) let xenstored_major = 1 let xenstored_minor = 0 let xs_daemon_socket = Paths.xen_run_stored ^ "/socket" let xs_daemon_socket_ro = Paths.xen_run_stored ^ "/socket_ro" let default_config_dir = Paths.xen_config_dir let maxwatch = ref (50) let maxtransaction = ref (20) let maxrequests = ref (-1) (* maximum requests per transaction *) let conflict_burst_limit = ref 5.0 let conflict_max_history_seconds = ref 0.05 let conflict_rate_limit_is_aggregate = ref true let domid_self = 0x7FF0 exception Not_a_directory of string exception Not_a_value of string exception Already_exist exception Doesnt_exist exception Lookup_Doesnt_exist of string exception Invalid_path exception Permission_denied exception Unknown_operation xen-4.9.2/tools/ocaml/xenstored/select_stubs.c0000664000175000017500000000432213256712137017567 0ustar smbsmb/* * Copyright (C) 2014 Zheng Li * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include CAMLprim value stub_select_on_poll(value fd_events, value timeo) { CAMLparam2(fd_events, timeo); CAMLlocal1(events); int i, rc, c_len = Wosize_val(fd_events), c_timeo = Int_val(timeo); struct pollfd c_fds[c_len]; for (i = 0; i < c_len; i++) { events = Field(Field(fd_events, i), 1); c_fds[i].fd = Int_val(Field(Field(fd_events, i), 0)); c_fds[i].events = c_fds[i].revents = 0; c_fds[i].events |= Bool_val(Field(events, 0)) ? POLLIN : 0; c_fds[i].events |= Bool_val(Field(events, 1)) ? POLLOUT: 0; c_fds[i].events |= Bool_val(Field(events, 2)) ? POLLPRI: 0; }; caml_enter_blocking_section(); rc = poll(c_fds, c_len, c_timeo); caml_leave_blocking_section(); if (rc < 0) uerror("poll", Nothing); if (rc > 0) { for (i = 0; i < c_len; i++) { events = Field(Field(fd_events, i), 1); if (c_fds[i].revents & POLLNVAL) unix_error(EBADF, "select", Nothing); Field(events, 0) = Val_bool(c_fds[i].events & POLLIN && c_fds[i].revents & (POLLIN |POLLHUP|POLLERR)); Field(events, 1) = Val_bool(c_fds[i].events & POLLOUT && c_fds[i].revents & (POLLOUT|POLLHUP|POLLERR)); Field(events, 2) = Val_bool(c_fds[i].revents & POLLPRI); } } CAMLreturn(Val_int(rc)); } CAMLprim value stub_set_fd_limit(value limit) { CAMLparam1(limit); struct rlimit rl; rl.rlim_cur = rl.rlim_max = Int_val(limit); if (setrlimit(RLIMIT_NOFILE, &rl) != 0) uerror("setrlimit", Nothing); CAMLreturn(Val_unit); } xen-4.9.2/tools/ocaml/Makefile0000664000175000017500000000065313256712137014354 0ustar smbsmbXEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk SUBDIRS := libs SUBDIRS += xenstored ifeq ($(CONFIG_TESTS),y) SUBDIRS += test endif .NOTPARALLEL: # targets here must be run in order, otherwise we can try # to build programs before the libraries are done .PHONY: all all: subdirs-all .PHONY: install install: subdirs-install .PHONY: clean clean: subdirs-clean .PHONY: distclean distclean: subdirs-distclean xen-4.9.2/tools/ocaml/LICENSE0000664000175000017500000006431513256712137013726 0ustar smbsmbThis repository is distributed under the terms of the GNU Lesser General Public License version 2.1 (included below). As a special exception to the GNU Lesser General Public License, you may link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Lesser General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library as distributed, or a modified version of the Library that is distributed under the conditions defined in clause 3 of the GNU Library General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Lesser General Public License. ------------ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it! xen-4.9.2/tools/ocaml/Makefile.rules0000664000175000017500000000714313256712137015506 0ustar smbsmbifdef V ifeq ("$(origin V)", "command line") BUILD_VERBOSE = $(V) endif endif ifndef BUILD_VERBOSE BUILD_VERBOSE = 0 endif ifeq ($(BUILD_VERBOSE),1) E = @true Q = else E = @echo Q = @ endif .NOTPARALLEL: ALL_OCAML_OBJS ?= $(OBJS) %.cmo: %.ml $(call quiet-command, $(OCAMLC) $(OCAMLCFLAGS) -c -o $@ $<,MLC,$@) %.cmi: %.mli $(call quiet-command, $(OCAMLC) $(OCAMLCFLAGS) -c -o $@ $<,MLI,$@) %.cmx %.o: %.ml $(call quiet-command, $(OCAMLOPT) $(OCAMLOPTFLAGS) -c -o $@ $<,MLOPT,$@) %.ml: %.mll $(call quiet-command, $(OCAMLLEX) -q -o $@ $<,MLLEX,$@) %.ml: %.mly $(call quiet-command, $(OCAMLYACC) -q $<,MLYACC,$@) %.o: %.c $(call quiet-command, $(CC) $(CFLAGS) -c -o $@ $<,CC,$@) META: META.in sed 's/@VERSION@/$(VERSION)/g' < $< $o ALL_OCAML_OBJ_SOURCES=$(addsuffix .ml, $(ALL_OCAML_OBJS)) .ocamldep.make: $(ALL_OCAML_OBJ_SOURCES) Makefile $(TOPLEVEL)/Makefile.rules $(call quiet-command, $(OCAMLDEP) $(ALL_OCAML_OBJ_SOURCES) *.mli $o,MLDEP,) clean: $(CLEAN_HOOKS) $(Q)rm -f .*.d *.o *.so *.a *.cmo *.cmi *.cma *.cmx *.cmxa *.annot *.spot *.spit $(LIBS) $(PROGRAMS) $(GENERATED_FILES) .ocamldep.make META distclean: clean quiet-command = $(if $(V),$1,@printf " %-8s %s\n" "$2" "$3" && $1) mk-caml-lib-native = $(call quiet-command, $(OCAMLOPT) $(OCAMLOPTFLAGS) -a -o $1 $2 $3,MLA,$1) mk-caml-lib-bytecode = $(call quiet-command, $(OCAMLC) $(OCAMLCFLAGS) -a -o $1 $2 $3,MLA,$1) mk-caml-stubs = $(call quiet-command, $(OCAMLMKLIB) -o `basename $1 .a` $2,MKLIB,$1) mk-caml-lib-stubs = \ $(call quiet-command, $(AR) rcs $1 $2 && $(OCAMLMKLIB) -o `basename $1 .a | sed -e 's/^lib//'` $2,MKLIB,$1) # define a library target .cmxa and .cma define OCAML_LIBRARY_template $(1).cmxa: lib$(1)_stubs.a $(foreach obj,$($(1)_OBJS),$(obj).cmx) $(call mk-caml-lib-native,$$@, -cclib -l$(1)_stubs $(foreach lib,$(LIBS_$(1)),-cclib $(lib)), $(foreach obj,$($(1)_OBJS),$(obj).cmx)) $(1).cma: $(foreach obj,$($(1)_OBJS),$(obj).cmo) $(call mk-caml-lib-bytecode,$$@, -dllib dll$(1)_stubs.so -cclib -l$(1)_stubs, $$+) $(1)_stubs.a: $(foreach obj,$$($(1)_C_OBJS),$(obj).o) $(call mk-caml-stubs,$$@, $$+) lib$(1)_stubs.a: $(foreach obj,$($(1)_C_OBJS),$(obj).o) $(call mk-caml-lib-stubs,$$@, $$+) endef define OCAML_NOC_LIBRARY_template $(1).cmxa: $(foreach obj,$($(1)_OBJS),$(obj).cmx) $(call mk-caml-lib-native,$$@, , $(foreach obj,$($(1)_OBJS),$(obj).cmx)) $(1).cma: $(foreach obj,$($(1)_OBJS),$(obj).cmo) $(call mk-caml-lib-bytecode,$$@, , $$+) endef define OCAML_PROGRAM_template $(1): $(foreach obj,$($(1)_OBJS),$(obj).cmx) $($(1)_EXTRA_DEPS) $(call quiet-command, $(OCAMLOPT) $(OCAMLOPTFLAGS) -o $$@ $($(1)_LIBS) $$+,MLBIN,$$@) $(1).byte: $(foreach obj,$($(1)_OBJS),$(obj).cmo) $(call quiet-command, $(OCAMLC) $(OCAMLCFLAGS) -o $$@ $($(1)_BYTE_LIBS) $$+,MLBIN,$$@) endef define C_PROGRAM_template $(1): $(foreach obj,$($(1)_OBJS),$(obj).o) $(call quiet-command, $(CC) $(LDFLAGS) -o $$@ $$+,BIN,$$@) endef -include .ocamldep.make $(foreach lib,$(OCAML_LIBRARY),$(eval $(call OCAML_LIBRARY_template,$(lib)))) $(foreach lib,$(OCAML_NOC_LIBRARY),$(eval $(call OCAML_NOC_LIBRARY_template,$(lib)))) $(foreach p,$(OCAML_PROGRAM),$(eval $(call OCAML_PROGRAM_template,$(p)))) $(foreach p,$(C_PROGRAM),$(eval $(call C_PROGRAM_template,$(p)))) buildmakevars2module = $(eval $(call buildmakevars2module-closure,$(1))) define buildmakevars2module-closure $(1): .phony rm -f $(1).tmp; \ $(foreach var, $(BUILD_MAKE_VARS), \ printf "let %s = \"%s\";;\n" \ $(shell echo $(var) | tr '[:upper:]' '[:lower:]') \ $($(var)) >>$(1).tmp;) \ $(call move-if-changed,$(1).tmp,$(1)) endef xen-4.9.2/tools/ocaml/common.make0000664000175000017500000000113113256712137015033 0ustar smbsmbinclude $(XEN_ROOT)/tools/Rules.mk CC ?= gcc OCAMLOPT ?= ocamlopt OCAMLC ?= ocamlc OCAMLMKLIB ?= ocamlmklib OCAMLDEP ?= ocamldep OCAMLLEX ?= ocamllex OCAMLYACC ?= ocamlyacc OCAMLFIND ?= ocamlfind CFLAGS += -fPIC -Werror -I$(shell ocamlc -where) OCAMLOPTFLAG_G := $(shell $(OCAMLOPT) -h 2>&1 | sed -n 's/^ *\(-g\) .*/\1/p') OCAMLOPTFLAGS = $(OCAMLOPTFLAG_G) -ccopt "$(LDFLAGS)" -dtypes $(OCAMLINCLUDE) -cc $(CC) -w F -warn-error F OCAMLCFLAGS += -g $(OCAMLINCLUDE) -w F -warn-error F VERSION := 4.1 OCAMLDESTDIR ?= $(DESTDIR)$(shell $(OCAMLFIND) printconf destdir) o= >$@.new && mv -f $@.new $@ xen-4.9.2/tools/ocaml/libs/0000775000175000017500000000000013256712137013641 5ustar smbsmbxen-4.9.2/tools/ocaml/libs/xc/0000775000175000017500000000000013256712137014253 5ustar smbsmbxen-4.9.2/tools/ocaml/libs/xc/xenctrl.mli0000664000175000017500000001670413256712137016445 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type domid = int type vcpuinfo = { online : bool; blocked : bool; running : bool; cputime : int64; cpumap : int32; } type domaininfo = { domid : domid; dying : bool; shutdown : bool; paused : bool; blocked : bool; running : bool; hvm_guest : bool; shutdown_code : int; total_memory_pages : nativeint; max_memory_pages : nativeint; shared_info_frame : int64; cpu_time : int64; nr_online_vcpus : int; max_vcpu_id : int; ssidref : int32; handle : int array; } type sched_control = { weight : int; cap : int; } type physinfo_cap_flag = CAP_HVM | CAP_DirectIO type physinfo = { threads_per_core : int; cores_per_socket : int; nr_cpus : int; max_node_id : int; cpu_khz : int; total_pages : nativeint; free_pages : nativeint; scrub_pages : nativeint; capabilities : physinfo_cap_flag list; max_nr_cpus : int; (** compile-time max possible number of nr_cpus *) } type version = { major : int; minor : int; extra : string; } type compile_info = { compiler : string; compile_by : string; compile_domain : string; compile_date : string; } type shutdown_reason = Poweroff | Reboot | Suspend | Crash | Watchdog | Soft_reset type domain_create_flag = CDF_HVM | CDF_HAP exception Error of string type handle external sizeof_core_header : unit -> int = "stub_sizeof_core_header" external sizeof_vcpu_guest_context : unit -> int = "stub_sizeof_vcpu_guest_context" external sizeof_xen_pfn : unit -> int = "stub_sizeof_xen_pfn" external interface_open : unit -> handle = "stub_xc_interface_open" external interface_close : handle -> unit = "stub_xc_interface_close" val with_intf : (handle -> 'a) -> 'a val domain_create : handle -> int32 -> domain_create_flag list -> string -> domid val domain_sethandle : handle -> domid -> string -> unit external domain_max_vcpus : handle -> domid -> int -> unit = "stub_xc_domain_max_vcpus" external domain_pause : handle -> domid -> unit = "stub_xc_domain_pause" external domain_unpause : handle -> domid -> unit = "stub_xc_domain_unpause" external domain_resume_fast : handle -> domid -> unit = "stub_xc_domain_resume_fast" external domain_destroy : handle -> domid -> unit = "stub_xc_domain_destroy" external domain_shutdown : handle -> domid -> shutdown_reason -> unit = "stub_xc_domain_shutdown" external _domain_getinfolist : handle -> domid -> int -> domaininfo list = "stub_xc_domain_getinfolist" val domain_getinfolist : handle -> domid -> domaininfo list external domain_getinfo : handle -> domid -> domaininfo = "stub_xc_domain_getinfo" external domain_get_vcpuinfo : handle -> int -> int -> vcpuinfo = "stub_xc_vcpu_getinfo" external domain_ioport_permission: handle -> domid -> int -> int -> bool -> unit = "stub_xc_domain_ioport_permission" external domain_iomem_permission: handle -> domid -> nativeint -> nativeint -> bool -> unit = "stub_xc_domain_iomem_permission" external domain_irq_permission: handle -> domid -> int -> bool -> unit = "stub_xc_domain_irq_permission" external vcpu_affinity_set : handle -> domid -> int -> bool array -> unit = "stub_xc_vcpu_setaffinity" external vcpu_affinity_get : handle -> domid -> int -> bool array = "stub_xc_vcpu_getaffinity" external vcpu_context_get : handle -> domid -> int -> string = "stub_xc_vcpu_context_get" external sched_id : handle -> int = "stub_xc_sched_id" external sched_credit_domain_set : handle -> domid -> sched_control -> unit = "stub_sched_credit_domain_set" external sched_credit_domain_get : handle -> domid -> sched_control = "stub_sched_credit_domain_get" external shadow_allocation_set : handle -> domid -> int -> unit = "stub_shadow_allocation_set" external shadow_allocation_get : handle -> domid -> int = "stub_shadow_allocation_get" external evtchn_alloc_unbound : handle -> domid -> domid -> int = "stub_xc_evtchn_alloc_unbound" external evtchn_reset : handle -> domid -> unit = "stub_xc_evtchn_reset" external readconsolering : handle -> string = "stub_xc_readconsolering" external send_debug_keys : handle -> string -> unit = "stub_xc_send_debug_keys" external physinfo : handle -> physinfo = "stub_xc_physinfo" external pcpu_info: handle -> int -> int64 array = "stub_xc_pcpu_info" external domain_setmaxmem : handle -> domid -> int64 -> unit = "stub_xc_domain_setmaxmem" external domain_set_memmap_limit : handle -> domid -> int64 -> unit = "stub_xc_domain_set_memmap_limit" external domain_memory_increase_reservation : handle -> domid -> int64 -> unit = "stub_xc_domain_memory_increase_reservation" external map_foreign_range : handle -> domid -> int -> nativeint -> Xenmmap.mmap_interface = "stub_map_foreign_range" external domain_get_pfn_list : handle -> domid -> nativeint -> nativeint array = "stub_xc_domain_get_pfn_list" external domain_assign_device: handle -> domid -> (int * int * int * int) -> unit = "stub_xc_domain_assign_device" external domain_deassign_device: handle -> domid -> (int * int * int * int) -> unit = "stub_xc_domain_deassign_device" external domain_test_assign_device: handle -> domid -> (int * int * int * int) -> bool = "stub_xc_domain_test_assign_device" external version : handle -> version = "stub_xc_version_version" external version_compile_info : handle -> compile_info = "stub_xc_version_compile_info" external version_changeset : handle -> string = "stub_xc_version_changeset" external version_capabilities : handle -> string = "stub_xc_version_capabilities" type featureset_index = Featureset_raw | Featureset_host | Featureset_pv | Featureset_hvm external get_cpu_featureset : handle -> featureset_index -> int64 array = "stub_xc_get_cpu_featureset" type core_magic = Magic_hvm | Magic_pv type core_header = { xch_magic : core_magic; xch_nr_vcpus : int; xch_nr_pages : nativeint; xch_index_offset : int64; xch_ctxt_offset : int64; xch_pages_offset : int64; } external marshall_core_header : core_header -> string = "stub_marshall_core_header" val coredump : handle -> domid -> Unix.file_descr -> unit external pages_to_kib : int64 -> int64 = "stub_pages_to_kib" val pages_to_mib : int64 -> int64 external watchdog : handle -> int -> int32 -> int = "stub_xc_watchdog" external domain_set_machine_address_size: handle -> domid -> int -> unit = "stub_xc_domain_set_machine_address_size" external domain_get_machine_address_size: handle -> domid -> int = "stub_xc_domain_get_machine_address_size" external domain_cpuid_set: handle -> domid -> (int64 * (int64 option)) -> string option array -> string option array = "stub_xc_domain_cpuid_set" external domain_cpuid_apply_policy: handle -> domid -> unit = "stub_xc_domain_cpuid_apply_policy" external cpuid_check: handle -> (int64 * (int64 option)) -> string option array -> (bool * string option array) = "stub_xc_cpuid_check" xen-4.9.2/tools/ocaml/libs/xc/Makefile0000664000175000017500000000142613256712137015716 0ustar smbsmbTOPLEVEL=$(CURDIR)/../.. XEN_ROOT=$(TOPLEVEL)/../.. include $(TOPLEVEL)/common.make CFLAGS += -I../mmap $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest) CFLAGS += $(APPEND_CFLAGS) OCAMLINCLUDE += -I ../mmap OBJS = xenctrl INTF = xenctrl.cmi LIBS = xenctrl.cma xenctrl.cmxa LIBS_xenctrl = $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) xenctrl_OBJS = $(OBJS) xenctrl_C_OBJS = xenctrl_stubs OCAML_LIBRARY = xenctrl all: $(INTF) $(LIBS) libs: $(LIBS) .PHONY: install install: $(LIBS) META mkdir -p $(OCAMLDESTDIR) $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenctrl $(OCAMLFIND) install -destdir $(OCAMLDESTDIR) -ldconf ignore xenctrl META $(INTF) $(LIBS) *.a *.so *.cmx .PHONY: uninstall uninstall: $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenctrl include $(TOPLEVEL)/Makefile.rules xen-4.9.2/tools/ocaml/libs/xc/META.in0000664000175000017500000000022513256712137015330 0ustar smbsmbversion = "@VERSION@" description = "Xen Control Interface" requires = "unix,xenmmap" archive(byte) = "xenctrl.cma" archive(native) = "xenctrl.cmxa" xen-4.9.2/tools/ocaml/libs/xc/xenctrl.ml0000664000175000017500000002463613256712137016277 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) (** *) type domid = int (* ** xenctrl.h ** *) type vcpuinfo = { online: bool; blocked: bool; running: bool; cputime: int64; cpumap: int32; } type domaininfo = { domid : domid; dying : bool; shutdown : bool; paused : bool; blocked : bool; running : bool; hvm_guest : bool; shutdown_code : int; total_memory_pages: nativeint; max_memory_pages : nativeint; shared_info_frame : int64; cpu_time : int64; nr_online_vcpus : int; max_vcpu_id : int; ssidref : int32; handle : int array; } type sched_control = { weight : int; cap : int; } type physinfo_cap_flag = | CAP_HVM | CAP_DirectIO type physinfo = { threads_per_core : int; cores_per_socket : int; nr_cpus : int; max_node_id : int; cpu_khz : int; total_pages : nativeint; free_pages : nativeint; scrub_pages : nativeint; (* XXX hw_cap *) capabilities : physinfo_cap_flag list; max_nr_cpus : int; } type version = { major : int; minor : int; extra : string; } type compile_info = { compiler : string; compile_by : string; compile_domain : string; compile_date : string; } type shutdown_reason = Poweroff | Reboot | Suspend | Crash | Watchdog | Soft_reset type domain_create_flag = CDF_HVM | CDF_HAP exception Error of string type handle (* this is only use by coredumping *) external sizeof_core_header: unit -> int = "stub_sizeof_core_header" external sizeof_vcpu_guest_context: unit -> int = "stub_sizeof_vcpu_guest_context" external sizeof_xen_pfn: unit -> int = "stub_sizeof_xen_pfn" (* end of use *) external interface_open: unit -> handle = "stub_xc_interface_open" external interface_close: handle -> unit = "stub_xc_interface_close" let with_intf f = let xc = interface_open () in let r = try f xc with exn -> interface_close xc; raise exn in interface_close xc; r external _domain_create: handle -> int32 -> domain_create_flag list -> int array -> domid = "stub_xc_domain_create" let int_array_of_uuid_string s = try Scanf.sscanf s "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" (fun a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 -> [| a0; a1; a2; a3; a4; a5; a6; a7; a8; a9; a10; a11; a12; a13; a14; a15 |]) with _ -> invalid_arg ("Xc.int_array_of_uuid_string: " ^ s) let domain_create handle n flags uuid = _domain_create handle n flags (int_array_of_uuid_string uuid) external _domain_sethandle: handle -> domid -> int array -> unit = "stub_xc_domain_sethandle" let domain_sethandle handle n uuid = _domain_sethandle handle n (int_array_of_uuid_string uuid) external domain_max_vcpus: handle -> domid -> int -> unit = "stub_xc_domain_max_vcpus" external domain_pause: handle -> domid -> unit = "stub_xc_domain_pause" external domain_unpause: handle -> domid -> unit = "stub_xc_domain_unpause" external domain_resume_fast: handle -> domid -> unit = "stub_xc_domain_resume_fast" external domain_destroy: handle -> domid -> unit = "stub_xc_domain_destroy" external domain_shutdown: handle -> domid -> shutdown_reason -> unit = "stub_xc_domain_shutdown" external _domain_getinfolist: handle -> domid -> int -> domaininfo list = "stub_xc_domain_getinfolist" let domain_getinfolist handle first_domain = let nb = 2 in let last_domid l = (List.hd l).domid + 1 in let rec __getlist from = let l = _domain_getinfolist handle from nb in (if List.length l = nb then __getlist (last_domid l) else []) @ l in List.rev (__getlist first_domain) external domain_getinfo: handle -> domid -> domaininfo= "stub_xc_domain_getinfo" external domain_get_vcpuinfo: handle -> int -> int -> vcpuinfo = "stub_xc_vcpu_getinfo" external domain_ioport_permission: handle -> domid -> int -> int -> bool -> unit = "stub_xc_domain_ioport_permission" external domain_iomem_permission: handle -> domid -> nativeint -> nativeint -> bool -> unit = "stub_xc_domain_iomem_permission" external domain_irq_permission: handle -> domid -> int -> bool -> unit = "stub_xc_domain_irq_permission" external vcpu_affinity_set: handle -> domid -> int -> bool array -> unit = "stub_xc_vcpu_setaffinity" external vcpu_affinity_get: handle -> domid -> int -> bool array = "stub_xc_vcpu_getaffinity" external vcpu_context_get: handle -> domid -> int -> string = "stub_xc_vcpu_context_get" external sched_id: handle -> int = "stub_xc_sched_id" external sched_credit_domain_set: handle -> domid -> sched_control -> unit = "stub_sched_credit_domain_set" external sched_credit_domain_get: handle -> domid -> sched_control = "stub_sched_credit_domain_get" external shadow_allocation_set: handle -> domid -> int -> unit = "stub_shadow_allocation_set" external shadow_allocation_get: handle -> domid -> int = "stub_shadow_allocation_get" external evtchn_alloc_unbound: handle -> domid -> domid -> int = "stub_xc_evtchn_alloc_unbound" external evtchn_reset: handle -> domid -> unit = "stub_xc_evtchn_reset" external readconsolering: handle -> string = "stub_xc_readconsolering" external send_debug_keys: handle -> string -> unit = "stub_xc_send_debug_keys" external physinfo: handle -> physinfo = "stub_xc_physinfo" external pcpu_info: handle -> int -> int64 array = "stub_xc_pcpu_info" external domain_setmaxmem: handle -> domid -> int64 -> unit = "stub_xc_domain_setmaxmem" external domain_set_memmap_limit: handle -> domid -> int64 -> unit = "stub_xc_domain_set_memmap_limit" external domain_memory_increase_reservation: handle -> domid -> int64 -> unit = "stub_xc_domain_memory_increase_reservation" external domain_set_machine_address_size: handle -> domid -> int -> unit = "stub_xc_domain_set_machine_address_size" external domain_get_machine_address_size: handle -> domid -> int = "stub_xc_domain_get_machine_address_size" external domain_cpuid_set: handle -> domid -> (int64 * (int64 option)) -> string option array -> string option array = "stub_xc_domain_cpuid_set" external domain_cpuid_apply_policy: handle -> domid -> unit = "stub_xc_domain_cpuid_apply_policy" external cpuid_check: handle -> (int64 * (int64 option)) -> string option array -> (bool * string option array) = "stub_xc_cpuid_check" external map_foreign_range: handle -> domid -> int -> nativeint -> Xenmmap.mmap_interface = "stub_map_foreign_range" external domain_get_pfn_list: handle -> domid -> nativeint -> nativeint array = "stub_xc_domain_get_pfn_list" external domain_assign_device: handle -> domid -> (int * int * int * int) -> unit = "stub_xc_domain_assign_device" external domain_deassign_device: handle -> domid -> (int * int * int * int) -> unit = "stub_xc_domain_deassign_device" external domain_test_assign_device: handle -> domid -> (int * int * int * int) -> bool = "stub_xc_domain_test_assign_device" external version: handle -> version = "stub_xc_version_version" external version_compile_info: handle -> compile_info = "stub_xc_version_compile_info" external version_changeset: handle -> string = "stub_xc_version_changeset" external version_capabilities: handle -> string = "stub_xc_version_capabilities" type featureset_index = Featureset_raw | Featureset_host | Featureset_pv | Featureset_hvm external get_cpu_featureset : handle -> featureset_index -> int64 array = "stub_xc_get_cpu_featureset" external watchdog : handle -> int -> int32 -> int = "stub_xc_watchdog" (* core dump structure *) type core_magic = Magic_hvm | Magic_pv type core_header = { xch_magic: core_magic; xch_nr_vcpus: int; xch_nr_pages: nativeint; xch_index_offset: int64; xch_ctxt_offset: int64; xch_pages_offset: int64; } external marshall_core_header: core_header -> string = "stub_marshall_core_header" (* coredump *) let coredump xch domid fd = let dump s = let wd = Unix.write fd s 0 (String.length s) in if wd <> String.length s then failwith "error while writing"; in let info = domain_getinfo xch domid in let nrpages = info.total_memory_pages in let ctxt = Array.make info.max_vcpu_id None in let nr_vcpus = ref 0 in for i = 0 to info.max_vcpu_id - 1 do ctxt.(i) <- try let v = vcpu_context_get xch domid i in incr nr_vcpus; Some v with _ -> None done; (* FIXME page offset if not rounded to sup *) let page_offset = Int64.add (Int64.of_int (sizeof_core_header () + (sizeof_vcpu_guest_context () * !nr_vcpus))) (Int64.of_nativeint ( Nativeint.mul (Nativeint.of_int (sizeof_xen_pfn ())) nrpages) ) in let header = { xch_magic = if info.hvm_guest then Magic_hvm else Magic_pv; xch_nr_vcpus = !nr_vcpus; xch_nr_pages = nrpages; xch_ctxt_offset = Int64.of_int (sizeof_core_header ()); xch_index_offset = Int64.of_int (sizeof_core_header () + sizeof_vcpu_guest_context ()); xch_pages_offset = page_offset; } in dump (marshall_core_header header); for i = 0 to info.max_vcpu_id - 1 do match ctxt.(i) with | None -> () | Some ctxt_i -> dump ctxt_i done; let pfns = domain_get_pfn_list xch domid nrpages in if Array.length pfns <> Nativeint.to_int nrpages then failwith "could not get the page frame list"; let page_size = Xenmmap.getpagesize () in for i = 0 to Nativeint.to_int nrpages - 1 do let page = map_foreign_range xch domid page_size pfns.(i) in let data = Xenmmap.read page 0 page_size in Xenmmap.unmap page; dump data done (* ** Misc ** *) (** Convert the given number of pages to an amount in KiB, rounded up. *) external pages_to_kib : int64 -> int64 = "stub_pages_to_kib" let pages_to_mib pages = Int64.div (pages_to_kib pages) 1024L let _ = Callback.register_exception "xc.error" (Error "register_callback") xen-4.9.2/tools/ocaml/libs/xc/xenctrl_stubs.c0000664000175000017500000007456413256712137017336 0ustar smbsmb/* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #define _XOPEN_SOURCE 600 #include #include #define CAML_NAME_SPACE #include #include #include #include #include #include #include #include #define XC_WANT_COMPAT_MAP_FOREIGN_API #include #include "mmap_stubs.h" #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) #define _H(__h) ((xc_interface *)(__h)) #define _D(__d) ((uint32_t)Int_val(__d)) #define Val_none (Val_int(0)) #define string_of_option_array(array, index) \ ((Field(array, index) == Val_none) ? NULL : String_val(Field(Field(array, index), 0))) /* maybe here we should check the range of the input instead of blindly * casting it to uint32 */ #define cpuid_input_of_val(i1, i2, input) \ i1 = (uint32_t) Int64_val(Field(input, 0)); \ i2 = ((Field(input, 1) == Val_none) ? 0xffffffff : (uint32_t) Int64_val(Field(Field(input, 1), 0))); static void Noreturn failwith_xc(xc_interface *xch) { char error_str[1028]; if (xch) { const xc_error *error = xc_get_last_error(xch); if (error->code == XC_ERROR_NONE) snprintf(error_str, sizeof(error_str), "%d: %s", errno, strerror(errno)); else snprintf(error_str, sizeof(error_str), "%d: %s: %s", error->code, xc_error_code_to_desc(error->code), error->message); } else { snprintf(error_str, sizeof(error_str), "Unable to open XC interface"); } caml_raise_with_string(*caml_named_value("xc.error"), error_str); } CAMLprim value stub_sizeof_core_header(value unit) { CAMLparam1(unit); CAMLreturn(Val_int(sizeof(struct xc_core_header))); } CAMLprim value stub_sizeof_vcpu_guest_context(value unit) { CAMLparam1(unit); CAMLreturn(Val_int(sizeof(struct vcpu_guest_context))); } CAMLprim value stub_sizeof_xen_pfn(value unit) { CAMLparam1(unit); CAMLreturn(Val_int(sizeof(xen_pfn_t))); } #define XC_CORE_MAGIC 0xF00FEBED #define XC_CORE_MAGIC_HVM 0xF00FEBEE CAMLprim value stub_marshall_core_header(value header) { CAMLparam1(header); CAMLlocal1(s); struct xc_core_header c_header; c_header.xch_magic = (Field(header, 0)) ? XC_CORE_MAGIC : XC_CORE_MAGIC_HVM; c_header.xch_nr_vcpus = Int_val(Field(header, 1)); c_header.xch_nr_pages = Nativeint_val(Field(header, 2)); c_header.xch_ctxt_offset = Int64_val(Field(header, 3)); c_header.xch_index_offset = Int64_val(Field(header, 4)); c_header.xch_pages_offset = Int64_val(Field(header, 5)); s = caml_alloc_string(sizeof(c_header)); memcpy(String_val(s), (char *) &c_header, sizeof(c_header)); CAMLreturn(s); } CAMLprim value stub_xc_interface_open(void) { CAMLparam0(); xc_interface *xch; /* Don't assert XC_OPENFLAG_NON_REENTRANT because these bindings * do not prevent re-entrancy to libxc */ xch = xc_interface_open(NULL, NULL, 0); if (xch == NULL) failwith_xc(NULL); CAMLreturn((value)xch); } CAMLprim value stub_xc_interface_close(value xch) { CAMLparam1(xch); caml_enter_blocking_section(); xc_interface_close(_H(xch)); caml_leave_blocking_section(); CAMLreturn(Val_unit); } static int domain_create_flag_table[] = { XEN_DOMCTL_CDF_hvm_guest, XEN_DOMCTL_CDF_hap, }; CAMLprim value stub_xc_domain_create(value xch, value ssidref, value flags, value handle) { CAMLparam4(xch, ssidref, flags, handle); uint32_t domid = 0; xen_domain_handle_t h = { 0 }; int result; int i; uint32_t c_ssidref = Int32_val(ssidref); unsigned int c_flags = 0; value l; if (Wosize_val(handle) != 16) caml_invalid_argument("Handle not a 16-integer array"); for (i = 0; i < sizeof(h); i++) { h[i] = Int_val(Field(handle, i)) & 0xff; } for (l = flags; l != Val_none; l = Field(l, 1)) { int v = Int_val(Field(l, 0)); c_flags |= domain_create_flag_table[v]; } caml_enter_blocking_section(); result = xc_domain_create(_H(xch), c_ssidref, h, c_flags, &domid, NULL); caml_leave_blocking_section(); if (result < 0) failwith_xc(_H(xch)); CAMLreturn(Val_int(domid)); } CAMLprim value stub_xc_domain_max_vcpus(value xch, value domid, value max_vcpus) { CAMLparam3(xch, domid, max_vcpus); int r; r = xc_domain_max_vcpus(_H(xch), _D(domid), Int_val(max_vcpus)); if (r) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } value stub_xc_domain_sethandle(value xch, value domid, value handle) { CAMLparam3(xch, domid, handle); xen_domain_handle_t h = { 0 }; int i; if (Wosize_val(handle) != 16) caml_invalid_argument("Handle not a 16-integer array"); for (i = 0; i < sizeof(h); i++) { h[i] = Int_val(Field(handle, i)) & 0xff; } i = xc_domain_sethandle(_H(xch), _D(domid), h); if (i) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } static value dom_op(value xch, value domid, int (*fn)(xc_interface *, uint32_t)) { CAMLparam2(xch, domid); int result; uint32_t c_domid = _D(domid); caml_enter_blocking_section(); result = fn(_H(xch), c_domid); caml_leave_blocking_section(); if (result) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_pause(value xch, value domid) { return dom_op(xch, domid, xc_domain_pause); } CAMLprim value stub_xc_domain_unpause(value xch, value domid) { return dom_op(xch, domid, xc_domain_unpause); } CAMLprim value stub_xc_domain_destroy(value xch, value domid) { return dom_op(xch, domid, xc_domain_destroy); } CAMLprim value stub_xc_domain_resume_fast(value xch, value domid) { CAMLparam2(xch, domid); int result; uint32_t c_domid = _D(domid); caml_enter_blocking_section(); result = xc_domain_resume(_H(xch), c_domid, 1); caml_leave_blocking_section(); if (result) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_shutdown(value xch, value domid, value reason) { CAMLparam3(xch, domid, reason); int ret; ret = xc_domain_shutdown(_H(xch), _D(domid), Int_val(reason)); if (ret < 0) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } static value alloc_domaininfo(xc_domaininfo_t * info) { CAMLparam0(); CAMLlocal2(result, tmp); int i; result = caml_alloc_tuple(16); Store_field(result, 0, Val_int(info->domain)); Store_field(result, 1, Val_bool(info->flags & XEN_DOMINF_dying)); Store_field(result, 2, Val_bool(info->flags & XEN_DOMINF_shutdown)); Store_field(result, 3, Val_bool(info->flags & XEN_DOMINF_paused)); Store_field(result, 4, Val_bool(info->flags & XEN_DOMINF_blocked)); Store_field(result, 5, Val_bool(info->flags & XEN_DOMINF_running)); Store_field(result, 6, Val_bool(info->flags & XEN_DOMINF_hvm_guest)); Store_field(result, 7, Val_int((info->flags >> XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask)); Store_field(result, 8, caml_copy_nativeint(info->tot_pages)); Store_field(result, 9, caml_copy_nativeint(info->max_pages)); Store_field(result, 10, caml_copy_int64(info->shared_info_frame)); Store_field(result, 11, caml_copy_int64(info->cpu_time)); Store_field(result, 12, Val_int(info->nr_online_vcpus)); Store_field(result, 13, Val_int(info->max_vcpu_id)); Store_field(result, 14, caml_copy_int32(info->ssidref)); tmp = caml_alloc_small(16, 0); for (i = 0; i < 16; i++) { Field(tmp, i) = Val_int(info->handle[i]); } Store_field(result, 15, tmp); CAMLreturn(result); } CAMLprim value stub_xc_domain_getinfolist(value xch, value first_domain, value nb) { CAMLparam3(xch, first_domain, nb); CAMLlocal2(result, temp); xc_domaininfo_t * info; int i, ret, toalloc, retval; unsigned int c_max_domains; uint32_t c_first_domain; /* get the minimum number of allocate byte we need and bump it up to page boundary */ toalloc = (sizeof(xc_domaininfo_t) * Int_val(nb)) | 0xfff; ret = posix_memalign((void **) ((void *) &info), 4096, toalloc); if (ret) caml_raise_out_of_memory(); result = temp = Val_emptylist; c_first_domain = _D(first_domain); c_max_domains = Int_val(nb); caml_enter_blocking_section(); retval = xc_domain_getinfolist(_H(xch), c_first_domain, c_max_domains, info); caml_leave_blocking_section(); if (retval < 0) { free(info); failwith_xc(_H(xch)); } for (i = 0; i < retval; i++) { result = caml_alloc_small(2, Tag_cons); Field(result, 0) = Val_int(0); Field(result, 1) = temp; temp = result; Store_field(result, 0, alloc_domaininfo(info + i)); } free(info); CAMLreturn(result); } CAMLprim value stub_xc_domain_getinfo(value xch, value domid) { CAMLparam2(xch, domid); CAMLlocal1(result); xc_domaininfo_t info; int ret; ret = xc_domain_getinfolist(_H(xch), _D(domid), 1, &info); if (ret != 1) failwith_xc(_H(xch)); if (info.domain != _D(domid)) failwith_xc(_H(xch)); result = alloc_domaininfo(&info); CAMLreturn(result); } CAMLprim value stub_xc_vcpu_getinfo(value xch, value domid, value vcpu) { CAMLparam3(xch, domid, vcpu); CAMLlocal1(result); xc_vcpuinfo_t info; int retval; uint32_t c_domid = _D(domid); uint32_t c_vcpu = Int_val(vcpu); caml_enter_blocking_section(); retval = xc_vcpu_getinfo(_H(xch), c_domid, c_vcpu, &info); caml_leave_blocking_section(); if (retval < 0) failwith_xc(_H(xch)); result = caml_alloc_tuple(5); Store_field(result, 0, Val_bool(info.online)); Store_field(result, 1, Val_bool(info.blocked)); Store_field(result, 2, Val_bool(info.running)); Store_field(result, 3, caml_copy_int64(info.cpu_time)); Store_field(result, 4, caml_copy_int32(info.cpu)); CAMLreturn(result); } CAMLprim value stub_xc_vcpu_context_get(value xch, value domid, value cpu) { CAMLparam3(xch, domid, cpu); CAMLlocal1(context); int ret; vcpu_guest_context_any_t ctxt; ret = xc_vcpu_getcontext(_H(xch), _D(domid), Int_val(cpu), &ctxt); context = caml_alloc_string(sizeof(ctxt)); memcpy(String_val(context), (char *) &ctxt.c, sizeof(ctxt.c)); CAMLreturn(context); } static int get_cpumap_len(value xch, value cpumap) { int ml_len = Wosize_val(cpumap); int xc_len = xc_get_max_cpus(_H(xch)); if (ml_len < xc_len) return ml_len; else return xc_len; } CAMLprim value stub_xc_vcpu_setaffinity(value xch, value domid, value vcpu, value cpumap) { CAMLparam4(xch, domid, vcpu, cpumap); int i, len = get_cpumap_len(xch, cpumap); xc_cpumap_t c_cpumap; int retval; c_cpumap = xc_cpumap_alloc(_H(xch)); if (c_cpumap == NULL) failwith_xc(_H(xch)); for (i=0; i= 0) { size += count - 1; if (size < count) break; ptr = realloc(str, size); if (!ptr) break; str = ptr + count; count = size - count; caml_enter_blocking_section(); ret = xc_readconsolering(_H(xch), str, &count, 0, 1, &index); caml_leave_blocking_section(); count += str - ptr; str = ptr; } /* * If we didn't break because of an overflow with size, and we have * needed to realloc() ourself more space, update our tracking of the * real console ring size. */ if (size > conring_size) conring_size = size; ring = caml_alloc_string(count); memcpy(String_val(ring), str, count); free(str); CAMLreturn(ring); } CAMLprim value stub_xc_send_debug_keys(value xch, value keys) { CAMLparam2(xch, keys); int r; r = xc_send_debug_keys(_H(xch), String_val(keys)); if (r) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_physinfo(value xch) { CAMLparam1(xch); CAMLlocal3(physinfo, cap_list, tmp); xc_physinfo_t c_physinfo; int r; caml_enter_blocking_section(); r = xc_physinfo(_H(xch), &c_physinfo); caml_leave_blocking_section(); if (r) failwith_xc(_H(xch)); tmp = cap_list = Val_emptylist; for (r = 0; r < 2; r++) { if ((c_physinfo.capabilities >> r) & 1) { tmp = caml_alloc_small(2, Tag_cons); Field(tmp, 0) = Val_int(r); Field(tmp, 1) = cap_list; cap_list = tmp; } } physinfo = caml_alloc_tuple(10); Store_field(physinfo, 0, Val_int(c_physinfo.threads_per_core)); Store_field(physinfo, 1, Val_int(c_physinfo.cores_per_socket)); Store_field(physinfo, 2, Val_int(c_physinfo.nr_cpus)); Store_field(physinfo, 3, Val_int(c_physinfo.max_node_id)); Store_field(physinfo, 4, Val_int(c_physinfo.cpu_khz)); Store_field(physinfo, 5, caml_copy_nativeint(c_physinfo.total_pages)); Store_field(physinfo, 6, caml_copy_nativeint(c_physinfo.free_pages)); Store_field(physinfo, 7, caml_copy_nativeint(c_physinfo.scrub_pages)); Store_field(physinfo, 8, cap_list); Store_field(physinfo, 9, Val_int(c_physinfo.max_cpu_id + 1)); CAMLreturn(physinfo); } CAMLprim value stub_xc_pcpu_info(value xch, value nr_cpus) { CAMLparam2(xch, nr_cpus); CAMLlocal2(pcpus, v); xc_cpuinfo_t *info; int r, size; if (Int_val(nr_cpus) < 1) caml_invalid_argument("nr_cpus"); info = calloc(Int_val(nr_cpus) + 1, sizeof(*info)); if (!info) caml_raise_out_of_memory(); caml_enter_blocking_section(); r = xc_getcpuinfo(_H(xch), Int_val(nr_cpus), info, &size); caml_leave_blocking_section(); if (r) { free(info); failwith_xc(_H(xch)); } if (size > 0) { int i; pcpus = caml_alloc(size, 0); for (i = 0; i < size; i++) { v = caml_copy_int64(info[i].idletime); caml_modify(&Field(pcpus, i), v); } } else pcpus = Atom(0); free(info); CAMLreturn(pcpus); } CAMLprim value stub_xc_domain_setmaxmem(value xch, value domid, value max_memkb) { CAMLparam3(xch, domid, max_memkb); int retval; uint32_t c_domid = _D(domid); unsigned int c_max_memkb = Int64_val(max_memkb); caml_enter_blocking_section(); retval = xc_domain_setmaxmem(_H(xch), c_domid, c_max_memkb); caml_leave_blocking_section(); if (retval) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_set_memmap_limit(value xch, value domid, value map_limitkb) { CAMLparam3(xch, domid, map_limitkb); unsigned long v; int retval; v = Int64_val(map_limitkb); retval = xc_domain_set_memmap_limit(_H(xch), _D(domid), v); if (retval) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_memory_increase_reservation(value xch, value domid, value mem_kb) { CAMLparam3(xch, domid, mem_kb); int retval; unsigned long nr_extents = ((unsigned long)(Int64_val(mem_kb))) >> (PAGE_SHIFT - 10); uint32_t c_domid = _D(domid); caml_enter_blocking_section(); retval = xc_domain_increase_reservation_exact(_H(xch), c_domid, nr_extents, 0, 0, NULL); caml_leave_blocking_section(); if (retval) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_set_machine_address_size(value xch, value domid, value width) { CAMLparam3(xch, domid, width); uint32_t c_domid = _D(domid); int c_width = Int_val(width); int retval = xc_domain_set_machine_address_size(_H(xch), c_domid, c_width); if (retval) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_get_machine_address_size(value xch, value domid) { CAMLparam2(xch, domid); int retval; retval = xc_domain_get_machine_address_size(_H(xch), _D(domid)); if (retval < 0) failwith_xc(_H(xch)); CAMLreturn(Val_int(retval)); } CAMLprim value stub_xc_domain_cpuid_set(value xch, value domid, value input, value config) { CAMLparam4(xch, domid, input, config); CAMLlocal2(array, tmp); #if defined(__i386__) || defined(__x86_64__) int r; unsigned int c_input[2]; char *c_config[4], *out_config[4]; c_config[0] = string_of_option_array(config, 0); c_config[1] = string_of_option_array(config, 1); c_config[2] = string_of_option_array(config, 2); c_config[3] = string_of_option_array(config, 3); cpuid_input_of_val(c_input[0], c_input[1], input); array = caml_alloc(4, 0); for (r = 0; r < 4; r++) { tmp = Val_none; if (c_config[r]) { tmp = caml_alloc_small(1, 0); Field(tmp, 0) = caml_alloc_string(32); } Store_field(array, r, tmp); } for (r = 0; r < 4; r++) out_config[r] = (c_config[r]) ? String_val(Field(Field(array, r), 0)) : NULL; r = xc_cpuid_set(_H(xch), _D(domid), c_input, (const char **)c_config, out_config); if (r < 0) failwith_xc(_H(xch)); #else caml_failwith("xc_domain_cpuid_set: not implemented"); #endif CAMLreturn(array); } CAMLprim value stub_xc_domain_cpuid_apply_policy(value xch, value domid) { CAMLparam2(xch, domid); #if defined(__i386__) || defined(__x86_64__) int r; r = xc_cpuid_apply_policy(_H(xch), _D(domid), NULL, 0); if (r < 0) failwith_xc(_H(xch)); #else caml_failwith("xc_domain_cpuid_apply_policy: not implemented"); #endif CAMLreturn(Val_unit); } CAMLprim value stub_xc_cpuid_check(value xch, value input, value config) { CAMLparam3(xch, input, config); CAMLlocal3(ret, array, tmp); #if defined(__i386__) || defined(__x86_64__) int r; unsigned int c_input[2]; char *c_config[4], *out_config[4]; c_config[0] = string_of_option_array(config, 0); c_config[1] = string_of_option_array(config, 1); c_config[2] = string_of_option_array(config, 2); c_config[3] = string_of_option_array(config, 3); cpuid_input_of_val(c_input[0], c_input[1], input); array = caml_alloc(4, 0); for (r = 0; r < 4; r++) { tmp = Val_none; if (c_config[r]) { tmp = caml_alloc_small(1, 0); Field(tmp, 0) = caml_alloc_string(32); } Store_field(array, r, tmp); } for (r = 0; r < 4; r++) out_config[r] = (c_config[r]) ? String_val(Field(Field(array, r), 0)) : NULL; r = xc_cpuid_check(_H(xch), c_input, (const char **)c_config, out_config); if (r < 0) failwith_xc(_H(xch)); ret = caml_alloc_tuple(2); Store_field(ret, 0, Val_bool(r)); Store_field(ret, 1, array); #else caml_failwith("xc_domain_cpuid_check: not implemented"); #endif CAMLreturn(ret); } CAMLprim value stub_xc_version_version(value xch) { CAMLparam1(xch); CAMLlocal1(result); xen_extraversion_t extra; long packed; int retval; caml_enter_blocking_section(); packed = xc_version(_H(xch), XENVER_version, NULL); caml_leave_blocking_section(); if (packed < 0) failwith_xc(_H(xch)); caml_enter_blocking_section(); retval = xc_version(_H(xch), XENVER_extraversion, &extra); caml_leave_blocking_section(); if (retval) failwith_xc(_H(xch)); result = caml_alloc_tuple(3); Store_field(result, 0, Val_int(packed >> 16)); Store_field(result, 1, Val_int(packed & 0xffff)); Store_field(result, 2, caml_copy_string(extra)); CAMLreturn(result); } CAMLprim value stub_xc_version_compile_info(value xch) { CAMLparam1(xch); CAMLlocal1(result); xen_compile_info_t ci; int retval; caml_enter_blocking_section(); retval = xc_version(_H(xch), XENVER_compile_info, &ci); caml_leave_blocking_section(); if (retval) failwith_xc(_H(xch)); result = caml_alloc_tuple(4); Store_field(result, 0, caml_copy_string(ci.compiler)); Store_field(result, 1, caml_copy_string(ci.compile_by)); Store_field(result, 2, caml_copy_string(ci.compile_domain)); Store_field(result, 3, caml_copy_string(ci.compile_date)); CAMLreturn(result); } static value xc_version_single_string(value xch, int code, void *info) { CAMLparam1(xch); int retval; caml_enter_blocking_section(); retval = xc_version(_H(xch), code, info); caml_leave_blocking_section(); if (retval) failwith_xc(_H(xch)); CAMLreturn(caml_copy_string((char *)info)); } CAMLprim value stub_xc_version_changeset(value xch) { xen_changeset_info_t ci; return xc_version_single_string(xch, XENVER_changeset, &ci); } CAMLprim value stub_xc_version_capabilities(value xch) { xen_capabilities_info_t ci; return xc_version_single_string(xch, XENVER_capabilities, &ci); } CAMLprim value stub_pages_to_kib(value pages) { CAMLparam1(pages); CAMLreturn(caml_copy_int64(Int64_val(pages) << (PAGE_SHIFT - 10))); } CAMLprim value stub_map_foreign_range(value xch, value dom, value size, value mfn) { CAMLparam4(xch, dom, size, mfn); CAMLlocal1(result); struct mmap_interface *intf; uint32_t c_dom; unsigned long c_mfn; result = caml_alloc(sizeof(struct mmap_interface), Abstract_tag); intf = (struct mmap_interface *) result; intf->len = Int_val(size); c_dom = _D(dom); c_mfn = Nativeint_val(mfn); caml_enter_blocking_section(); intf->addr = xc_map_foreign_range(_H(xch), c_dom, intf->len, PROT_READ|PROT_WRITE, c_mfn); caml_leave_blocking_section(); if (!intf->addr) caml_failwith("xc_map_foreign_range error"); CAMLreturn(result); } CAMLprim value stub_sched_credit_domain_get(value xch, value domid) { CAMLparam2(xch, domid); CAMLlocal1(sdom); struct xen_domctl_sched_credit c_sdom; int ret; caml_enter_blocking_section(); ret = xc_sched_credit_domain_get(_H(xch), _D(domid), &c_sdom); caml_leave_blocking_section(); if (ret != 0) failwith_xc(_H(xch)); sdom = caml_alloc_tuple(2); Store_field(sdom, 0, Val_int(c_sdom.weight)); Store_field(sdom, 1, Val_int(c_sdom.cap)); CAMLreturn(sdom); } CAMLprim value stub_sched_credit_domain_set(value xch, value domid, value sdom) { CAMLparam3(xch, domid, sdom); struct xen_domctl_sched_credit c_sdom; int ret; c_sdom.weight = Int_val(Field(sdom, 0)); c_sdom.cap = Int_val(Field(sdom, 1)); caml_enter_blocking_section(); ret = xc_sched_credit_domain_set(_H(xch), _D(domid), &c_sdom); caml_leave_blocking_section(); if (ret != 0) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_shadow_allocation_get(value xch, value domid) { CAMLparam2(xch, domid); CAMLlocal1(mb); unsigned long c_mb; int ret; caml_enter_blocking_section(); ret = xc_shadow_control(_H(xch), _D(domid), XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION, NULL, 0, &c_mb, 0, NULL); caml_leave_blocking_section(); if (ret != 0) failwith_xc(_H(xch)); mb = Val_int(c_mb); CAMLreturn(mb); } CAMLprim value stub_shadow_allocation_set(value xch, value domid, value mb) { CAMLparam3(xch, domid, mb); unsigned long c_mb; int ret; c_mb = Int_val(mb); caml_enter_blocking_section(); ret = xc_shadow_control(_H(xch), _D(domid), XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION, NULL, 0, &c_mb, 0, NULL); caml_leave_blocking_section(); if (ret != 0) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_get_pfn_list(value xch, value domid, value nr_pfns) { CAMLparam3(xch, domid, nr_pfns); CAMLlocal2(array, v); unsigned long c_nr_pfns; long ret, i; uint64_t *c_array; c_nr_pfns = Nativeint_val(nr_pfns); c_array = malloc(sizeof(uint64_t) * c_nr_pfns); if (!c_array) caml_raise_out_of_memory(); ret = xc_get_pfn_list(_H(xch), _D(domid), c_array, c_nr_pfns); if (ret < 0) { free(c_array); failwith_xc(_H(xch)); } array = caml_alloc(ret, 0); for (i = 0; i < ret; i++) { v = caml_copy_nativeint(c_array[i]); Store_field(array, i, v); } free(c_array); CAMLreturn(array); } CAMLprim value stub_xc_domain_ioport_permission(value xch, value domid, value start_port, value nr_ports, value allow) { CAMLparam5(xch, domid, start_port, nr_ports, allow); uint32_t c_start_port, c_nr_ports; uint8_t c_allow; int ret; c_start_port = Int_val(start_port); c_nr_ports = Int_val(nr_ports); c_allow = Bool_val(allow); ret = xc_domain_ioport_permission(_H(xch), _D(domid), c_start_port, c_nr_ports, c_allow); if (ret < 0) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_iomem_permission(value xch, value domid, value start_pfn, value nr_pfns, value allow) { CAMLparam5(xch, domid, start_pfn, nr_pfns, allow); unsigned long c_start_pfn, c_nr_pfns; uint8_t c_allow; int ret; c_start_pfn = Nativeint_val(start_pfn); c_nr_pfns = Nativeint_val(nr_pfns); c_allow = Bool_val(allow); ret = xc_domain_iomem_permission(_H(xch), _D(domid), c_start_pfn, c_nr_pfns, c_allow); if (ret < 0) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_irq_permission(value xch, value domid, value pirq, value allow) { CAMLparam4(xch, domid, pirq, allow); uint8_t c_pirq; uint8_t c_allow; int ret; c_pirq = Int_val(pirq); c_allow = Bool_val(allow); ret = xc_domain_irq_permission(_H(xch), _D(domid), c_pirq, c_allow); if (ret < 0) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } static uint32_t encode_sbdf(int domain, int bus, int dev, int func) { return ((uint32_t)domain & 0xffff) << 16 | ((uint32_t)bus & 0xff) << 8 | ((uint32_t)dev & 0x1f) << 3 | ((uint32_t)func & 0x7); } CAMLprim value stub_xc_domain_test_assign_device(value xch, value domid, value desc) { CAMLparam3(xch, domid, desc); int ret; int domain, bus, dev, func; uint32_t sbdf; domain = Int_val(Field(desc, 0)); bus = Int_val(Field(desc, 1)); dev = Int_val(Field(desc, 2)); func = Int_val(Field(desc, 3)); sbdf = encode_sbdf(domain, bus, dev, func); ret = xc_test_assign_device(_H(xch), _D(domid), sbdf); CAMLreturn(Val_bool(ret == 0)); } static int domain_assign_device_rdm_flag_table[] = { XEN_DOMCTL_DEV_RDM_RELAXED, }; CAMLprim value stub_xc_domain_assign_device(value xch, value domid, value desc, value rflag) { CAMLparam4(xch, domid, desc, rflag); int ret; int domain, bus, dev, func; uint32_t sbdf, flag; domain = Int_val(Field(desc, 0)); bus = Int_val(Field(desc, 1)); dev = Int_val(Field(desc, 2)); func = Int_val(Field(desc, 3)); sbdf = encode_sbdf(domain, bus, dev, func); ret = Int_val(Field(rflag, 0)); flag = domain_assign_device_rdm_flag_table[ret]; ret = xc_assign_device(_H(xch), _D(domid), sbdf, flag); if (ret < 0) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_domain_deassign_device(value xch, value domid, value desc) { CAMLparam3(xch, domid, desc); int ret; int domain, bus, dev, func; uint32_t sbdf; domain = Int_val(Field(desc, 0)); bus = Int_val(Field(desc, 1)); dev = Int_val(Field(desc, 2)); func = Int_val(Field(desc, 3)); sbdf = encode_sbdf(domain, bus, dev, func); ret = xc_deassign_device(_H(xch), _D(domid), sbdf); if (ret < 0) failwith_xc(_H(xch)); CAMLreturn(Val_unit); } CAMLprim value stub_xc_get_cpu_featureset(value xch, value idx) { CAMLparam2(xch, idx); CAMLlocal1(bitmap_val); #if defined(__i386__) || defined(__x86_64__) /* Safe, because of the global ocaml lock. */ static uint32_t fs_len; if (fs_len == 0) { int ret = xc_get_cpu_featureset(_H(xch), 0, &fs_len, NULL); if (ret || (fs_len == 0)) failwith_xc(_H(xch)); } { /* To/from hypervisor to retrieve actual featureset */ uint32_t fs[fs_len], len = fs_len; unsigned int i; int ret = xc_get_cpu_featureset(_H(xch), Int_val(idx), &len, fs); if (ret) failwith_xc(_H(xch)); bitmap_val = caml_alloc(len, 0); for (i = 0; i < len; ++i) Store_field(bitmap_val, i, caml_copy_int64(fs[i])); } #else caml_failwith("xc_get_cpu_featureset: not implemented"); #endif CAMLreturn(bitmap_val); } CAMLprim value stub_xc_watchdog(value xch, value domid, value timeout) { CAMLparam3(xch, domid, timeout); int ret; unsigned int c_timeout = Int32_val(timeout); ret = xc_watchdog(_H(xch), _D(domid), c_timeout); if (ret < 0) failwith_xc(_H(xch)); CAMLreturn(Val_int(ret)); } /* * Local variables: * indent-tabs-mode: t * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/ocaml/libs/xb/0000775000175000017500000000000013256712137014252 5ustar smbsmbxen-4.9.2/tools/ocaml/libs/xb/xs_ring.ml0000664000175000017500000000331413256712137016256 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) module Server_feature = struct type t = | Reconnection end module Server_features = Set.Make(struct type t = Server_feature.t let compare = compare end) external read: Xenmmap.mmap_interface -> string -> int -> int = "ml_interface_read" external write: Xenmmap.mmap_interface -> string -> int -> int = "ml_interface_write" external _internal_set_server_features: Xenmmap.mmap_interface -> int -> unit = "ml_interface_set_server_features" "noalloc" external _internal_get_server_features: Xenmmap.mmap_interface -> int = "ml_interface_get_server_features" "noalloc" let get_server_features mmap = (* NB only one feature currently defined above *) let x = _internal_get_server_features mmap in if x = 0 then Server_features.empty else Server_features.singleton Server_feature.Reconnection let set_server_features mmap set = (* NB only one feature currently defined above *) let x = if set = Server_features.empty then 0 else 1 in _internal_set_server_features mmap x external close: Xenmmap.mmap_interface -> unit = "ml_interface_close" "noalloc" xen-4.9.2/tools/ocaml/libs/xb/xs_ring_stubs.c0000664000175000017500000001237513256712137017317 0ustar smbsmb/* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmap_stubs.h" #define GET_C_STRUCT(a) ((struct mmap_interface *) a) CAMLprim value ml_interface_read(value ml_interface, value ml_buffer, value ml_len) { CAMLparam3(ml_interface, ml_buffer, ml_len); CAMLlocal1(ml_result); struct mmap_interface *interface = GET_C_STRUCT(ml_interface); char *buffer = String_val(ml_buffer); int len = Int_val(ml_len); int result; struct xenstore_domain_interface *intf = interface->addr; XENSTORE_RING_IDX cons, prod; /* offsets only */ int total_data, data; uint32_t connection; cons = *(volatile uint32_t*)&intf->req_cons; prod = *(volatile uint32_t*)&intf->req_prod; connection = *(volatile uint32_t*)&intf->connection; if (connection != XENSTORE_CONNECTED) caml_raise_constant(*caml_named_value("Xb.Reconnect")); xen_mb(); if ((prod - cons) > XENSTORE_RING_SIZE) caml_failwith("bad connection"); /* Check for any pending data at all. */ total_data = prod - cons; if (total_data == 0) { /* No pending data at all. */ result = 0; goto exit; } else if (total_data < len) /* Some data - make a partial read. */ len = total_data; /* Check whether data crosses the end of the ring. */ data = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons); if (len < data) /* Data within the remaining part of the ring. */ memcpy(buffer, intf->req + MASK_XENSTORE_IDX(cons), len); else { /* Data crosses the ring boundary. Read both halves. */ memcpy(buffer, intf->req + MASK_XENSTORE_IDX(cons), data); memcpy(buffer + data, intf->req, len - data); } xen_mb(); intf->req_cons += len; result = len; exit: ml_result = Val_int(result); CAMLreturn(ml_result); } CAMLprim value ml_interface_write(value ml_interface, value ml_buffer, value ml_len) { CAMLparam3(ml_interface, ml_buffer, ml_len); CAMLlocal1(ml_result); struct mmap_interface *interface = GET_C_STRUCT(ml_interface); char *buffer = String_val(ml_buffer); int len = Int_val(ml_len); int result; struct xenstore_domain_interface *intf = interface->addr; XENSTORE_RING_IDX cons, prod; int total_space, space; uint32_t connection; cons = *(volatile uint32_t*)&intf->rsp_cons; prod = *(volatile uint32_t*)&intf->rsp_prod; connection = *(volatile uint32_t*)&intf->connection; if (connection != XENSTORE_CONNECTED) caml_raise_constant(*caml_named_value("Xb.Reconnect")); xen_mb(); if ((prod - cons) > XENSTORE_RING_SIZE) caml_failwith("bad connection"); /* Check for space to write the full message. */ total_space = XENSTORE_RING_SIZE - (prod - cons); if (total_space == 0) { /* No space at all - exit having done nothing. */ result = 0; goto exit; } else if (total_space < len) /* Some space - make a partial write. */ len = total_space; /* Check for space until the ring wraps. */ space = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod); if (len < space) /* Message fits inside the remaining part of the ring. */ memcpy(intf->rsp + MASK_XENSTORE_IDX(prod), buffer, len); else { /* Message wraps around the end of the ring. Write both halves. */ memcpy(intf->rsp + MASK_XENSTORE_IDX(prod), buffer, space); memcpy(intf->rsp, buffer + space, len - space); } xen_mb(); intf->rsp_prod += len; result = len; exit: ml_result = Val_int(result); CAMLreturn(ml_result); } CAMLprim value ml_interface_set_server_features(value interface, value v) { CAMLparam2(interface, v); struct xenstore_domain_interface *intf = GET_C_STRUCT(interface)->addr; intf->server_features = Int_val(v); CAMLreturn(Val_unit); } CAMLprim value ml_interface_get_server_features(value interface) { CAMLparam1(interface); struct xenstore_domain_interface *intf = GET_C_STRUCT(interface)->addr; CAMLreturn(Val_int (intf->server_features)); } CAMLprim value ml_interface_close(value interface) { CAMLparam1(interface); struct xenstore_domain_interface *intf = GET_C_STRUCT(interface)->addr; int i; intf->req_cons = intf->req_prod = intf->rsp_cons = intf->rsp_prod = 0; /* Ensure the unused space is full of invalid xenstore packets. */ for (i = 0; i < XENSTORE_RING_SIZE; i++) { intf->req[i] = 0xff; /* XS_INVALID = 0xffff */ intf->rsp[i] = 0xff; } xen_mb (); intf->connection = XENSTORE_CONNECTED; CAMLreturn(Val_unit); } xen-4.9.2/tools/ocaml/libs/xb/xb.ml0000664000175000017500000001300713256712137015216 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) module Op = struct include Op end module Packet = struct include Packet end exception End_of_file exception Eagain exception Noent exception Invalid exception Reconnect let _ = Callback.register_exception "Xb.Reconnect" Reconnect type backend_mmap = { mmap: Xenmmap.mmap_interface; (* mmaped interface = xs_ring *) eventchn_notify: unit -> unit; (* function to notify through eventchn *) mutable work_again: bool; } type backend_fd = { fd: Unix.file_descr; } type backend = Fd of backend_fd | Xenmmap of backend_mmap type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * string type t = { backend: backend; pkt_in: Packet.t Queue.t; pkt_out: Packet.t Queue.t; mutable partial_in: partial_buf; mutable partial_out: string; } let init_partial_in () = NoHdr (Partial.header_size (), String.make (Partial.header_size()) '\000') let reconnect t = match t.backend with | Fd _ -> (* should never happen, so close the connection *) raise End_of_file | Xenmmap backend -> Xs_ring.close backend.mmap; backend.eventchn_notify (); (* Clear our old connection state *) Queue.clear t.pkt_in; Queue.clear t.pkt_out; t.partial_in <- init_partial_in (); t.partial_out <- "" let queue con pkt = Queue.push pkt con.pkt_out let read_fd back con s len = let rd = Unix.read back.fd s 0 len in if rd = 0 then raise End_of_file; rd let read_mmap back con s len = let rd = Xs_ring.read back.mmap s len in back.work_again <- (rd > 0); if rd > 0 then back.eventchn_notify (); rd let read con s len = match con.backend with | Fd backfd -> read_fd backfd con s len | Xenmmap backmmap -> read_mmap backmmap con s len let write_fd back con s len = Unix.write back.fd s 0 len let write_mmap back con s len = let ws = Xs_ring.write back.mmap s len in if ws > 0 then back.eventchn_notify (); ws let write con s len = match con.backend with | Fd backfd -> write_fd backfd con s len | Xenmmap backmmap -> write_mmap backmmap con s len (* NB: can throw Reconnect *) let output con = (* get the output string from a string_of(packet) or partial_out *) let s = if String.length con.partial_out > 0 then con.partial_out else if Queue.length con.pkt_out > 0 then Packet.to_string (Queue.pop con.pkt_out) else "" in (* send data from s, and save the unsent data to partial_out *) if s <> "" then ( let len = String.length s in let sz = write con s len in let left = String.sub s sz (len - sz) in con.partial_out <- left ); (* after sending one packet, partial is empty *) con.partial_out = "" (* NB: can throw Reconnect *) let input con = let newpacket = ref false in let to_read = match con.partial_in with | HaveHdr partial_pkt -> Partial.to_complete partial_pkt | NoHdr (i, buf) -> i in (* try to get more data from input stream *) let s = String.make to_read '\000' in let sz = if to_read > 0 then read con s to_read else 0 in ( match con.partial_in with | HaveHdr partial_pkt -> (* we complete the data *) if sz > 0 then Partial.append partial_pkt s sz; if Partial.to_complete partial_pkt = 0 then ( let pkt = Packet.of_partialpkt partial_pkt in con.partial_in <- init_partial_in (); Queue.push pkt con.pkt_in; newpacket := true ) | NoHdr (i, buf) -> (* we complete the partial header *) if sz > 0 then String.blit s 0 buf (Partial.header_size () - i) sz; con.partial_in <- if sz = i then HaveHdr (Partial.of_string buf) else NoHdr (i - sz, buf) ); !newpacket let newcon backend = { backend = backend; pkt_in = Queue.create (); pkt_out = Queue.create (); partial_in = init_partial_in (); partial_out = ""; } let open_fd fd = newcon (Fd { fd = fd; }) let open_mmap mmap notifyfct = (* Advertise XENSTORE_SERVER_FEATURE_RECONNECTION *) Xs_ring.set_server_features mmap (Xs_ring.Server_features.singleton Xs_ring.Server_feature.Reconnection); newcon (Xenmmap { mmap = mmap; eventchn_notify = notifyfct; work_again = false; }) let close con = match con.backend with | Fd backend -> Unix.close backend.fd | Xenmmap backend -> Xenmmap.unmap backend.mmap let is_fd con = match con.backend with | Fd _ -> true | Xenmmap _ -> false let is_mmap con = not (is_fd con) let output_len con = Queue.length con.pkt_out let has_new_output con = Queue.length con.pkt_out > 0 let has_old_output con = String.length con.partial_out > 0 let has_output con = has_new_output con || has_old_output con let peek_output con = Queue.peek con.pkt_out let input_len con = Queue.length con.pkt_in let has_in_packet con = Queue.length con.pkt_in > 0 let get_in_packet con = Queue.pop con.pkt_in let has_more_input con = match con.backend with | Fd _ -> false | Xenmmap backend -> backend.work_again let is_selectable con = match con.backend with | Fd _ -> true | Xenmmap _ -> false let get_fd con = match con.backend with | Fd backend -> backend.fd | Xenmmap _ -> raise (Failure "get_fd") xen-4.9.2/tools/ocaml/libs/xb/Makefile0000664000175000017500000000260713256712137015717 0ustar smbsmbTOPLEVEL=$(CURDIR)/../.. XEN_ROOT=$(TOPLEVEL)/../.. include $(TOPLEVEL)/common.make CFLAGS += -I../mmap CFLAGS += $(CFLAGS_libxenctrl) # For xen_mb() CFLAGS += $(CFLAGS_xeninclude) CFLAGS += $(APPEND_CFLAGS) OCAMLINCLUDE += -I ../mmap OCAMLOPTFLAGS += -for-pack Xenbus .NOTPARALLEL: # Ocaml is such a PITA! PREINTF = op.cmi partial.cmi packet.cmi PREOBJS = op partial packet xs_ring PRELIBS = $(foreach obj, $(PREOBJS),$(obj).cmo) $(foreach obj,$(PREOJBS),$(obj).cmx) OBJS = op partial packet xs_ring xb INTF = op.cmi packet.cmi xb.cmi LIBS = xenbus.cma xenbus.cmxa ALL_OCAML_OBJS = $(OBJS) $(PREOJBS) all: $(PREINTF) $(PRELIBS) $(INTF) $(LIBS) $(PROGRAMS) bins: $(PROGRAMS) libs: $(LIBS) xenbus_OBJS = xenbus xenbus_C_OBJS = xs_ring_stubs xenbus_stubs OCAML_LIBRARY = xenbus xenbus.cmx : $(foreach obj, $(OBJS), $(obj).cmx) $(E) " CMX $@" $(OCAMLOPT) -pack -o $@ $^ xenbus.cmo : $(foreach obj, $(OBJS), $(obj).cmo) $(E) " CMO $@" $(OCAMLC) -pack -o $@ $^ %.mli: %.ml $(E) " MLI $@" $(Q)$(OCAMLC) $(OCAMLINCLUDE) -i $< $o .PHONY: install install: $(LIBS) META mkdir -p $(OCAMLDESTDIR) $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenbus $(OCAMLFIND) install -destdir $(OCAMLDESTDIR) -ldconf ignore xenbus META $(LIBS) xenbus.cmo xenbus.cmi xenbus.cmx *.a *.so .PHONY: uninstall uninstall: $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenbus include $(TOPLEVEL)/Makefile.rules xen-4.9.2/tools/ocaml/libs/xb/META.in0000664000175000017500000000021613256712137015327 0ustar smbsmbversion = "@VERSION@" description = "XenBus Interface" requires = "unix,xenmmap" archive(byte) = "xenbus.cma" archive(native) = "xenbus.cmxa" xen-4.9.2/tools/ocaml/libs/xb/xenbus_stubs.c0000664000175000017500000000364513256712137017152 0ustar smbsmb/* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include CAMLprim value stub_header_size(void) { CAMLparam0(); CAMLreturn(Val_int(sizeof(struct xsd_sockmsg))); } CAMLprim value stub_header_of_string(value s) { CAMLparam1(s); CAMLlocal1(ret); struct xsd_sockmsg *hdr; if (caml_string_length(s) != sizeof(struct xsd_sockmsg)) caml_failwith("xb header incomplete"); ret = caml_alloc_tuple(4); hdr = (struct xsd_sockmsg *) String_val(s); Store_field(ret, 0, Val_int(hdr->tx_id)); Store_field(ret, 1, Val_int(hdr->req_id)); Store_field(ret, 2, Val_int(hdr->type)); Store_field(ret, 3, Val_int(hdr->len)); CAMLreturn(ret); } CAMLprim value stub_string_of_header(value tid, value rid, value ty, value len) { CAMLparam4(tid, rid, ty, len); CAMLlocal1(ret); struct xsd_sockmsg xsd = { .type = Int_val(ty), .tx_id = Int_val(tid), .req_id = Int_val(rid), .len = Int_val(len), }; ret = caml_alloc_string(sizeof(struct xsd_sockmsg)); memcpy(String_val(ret), &xsd, sizeof(struct xsd_sockmsg)); CAMLreturn(ret); } xen-4.9.2/tools/ocaml/libs/xb/op.ml0000664000175000017500000000446113256712137015227 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type operation = Debug | Directory | Read | Getperms | Watch | Unwatch | Transaction_start | Transaction_end | Introduce | Release | Getdomainpath | Write | Mkdir | Rm | Setperms | Watchevent | Error | Isintroduced | Resume | Set_target | Reset_watches | Invalid let operation_c_mapping = [| Debug; Directory; Read; Getperms; Watch; Unwatch; Transaction_start; Transaction_end; Introduce; Release; Getdomainpath; Write; Mkdir; Rm; Setperms; Watchevent; Error; Isintroduced; Resume; Set_target; Reset_watches |] let size = Array.length operation_c_mapping let array_search el a = let len = Array.length a in let rec search i = if i > len then raise Not_found; if a.(i) = el then i else search (i + 1) in search 0 let of_cval i = if i >= 0 && i < size then operation_c_mapping.(i) else Invalid let to_cval op = array_search op operation_c_mapping let to_string ty = match ty with | Debug -> "DEBUG" | Directory -> "DIRECTORY" | Read -> "READ" | Getperms -> "GET_PERMS" | Watch -> "WATCH" | Unwatch -> "UNWATCH" | Transaction_start -> "TRANSACTION_START" | Transaction_end -> "TRANSACTION_END" | Introduce -> "INTRODUCE" | Release -> "RELEASE" | Getdomainpath -> "GET_DOMAIN_PATH" | Write -> "WRITE" | Mkdir -> "MKDIR" | Rm -> "RM" | Setperms -> "SET_PERMS" | Watchevent -> "WATCH_EVENT" | Error -> "ERROR" | Isintroduced -> "IS_INTRODUCED" | Resume -> "RESUME" | Set_target -> "SET_TARGET" | Reset_watches -> "RESET_WATCHES" | Invalid -> "INVALID" xen-4.9.2/tools/ocaml/libs/xb/xb.mli0000664000175000017500000000535713256712137015400 0ustar smbsmbmodule Op : sig type operation = Op.operation = Debug | Directory | Read | Getperms | Watch | Unwatch | Transaction_start | Transaction_end | Introduce | Release | Getdomainpath | Write | Mkdir | Rm | Setperms | Watchevent | Error | Isintroduced | Resume | Set_target | Reset_watches | Invalid val operation_c_mapping : operation array val size : int val array_search : 'a -> 'a array -> int val of_cval : int -> operation val to_cval : operation -> int val to_string : operation -> string end module Packet : sig type t = Packet.t = { tid : int; rid : int; ty : Op.operation; data : string; } exception Error of string exception DataError of string external string_of_header : int -> int -> int -> int -> string = "stub_string_of_header" val create : int -> int -> Op.operation -> string -> t val of_partialpkt : Partial.pkt -> t val to_string : t -> string val unpack : t -> int * int * Op.operation * string val get_tid : t -> int val get_ty : t -> Op.operation val get_data : t -> string val get_rid : t -> int end exception End_of_file exception Eagain exception Noent exception Invalid exception Reconnect type backend_mmap = { mmap : Xenmmap.mmap_interface; eventchn_notify : unit -> unit; mutable work_again : bool; } type backend_fd = { fd : Unix.file_descr; } type backend = Fd of backend_fd | Xenmmap of backend_mmap type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * string type t = { backend : backend; pkt_in : Packet.t Queue.t; pkt_out : Packet.t Queue.t; mutable partial_in : partial_buf; mutable partial_out : string; } val init_partial_in : unit -> partial_buf val reconnect : t -> unit val queue : t -> Packet.t -> unit val read_fd : backend_fd -> 'a -> string -> int -> int val read_mmap : backend_mmap -> 'a -> string -> int -> int val read : t -> string -> int -> int val write_fd : backend_fd -> 'a -> string -> int -> int val write_mmap : backend_mmap -> 'a -> string -> int -> int val write : t -> string -> int -> int val output : t -> bool val input : t -> bool val newcon : backend -> t val open_fd : Unix.file_descr -> t val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> t val close : t -> unit val is_fd : t -> bool val is_mmap : t -> bool val output_len : t -> int val has_new_output : t -> bool val has_old_output : t -> bool val has_output : t -> bool val peek_output : t -> Packet.t val input_len : t -> int val has_in_packet : t -> bool val get_in_packet : t -> Packet.t val has_more_input : t -> bool val is_selectable : t -> bool val get_fd : t -> Unix.file_descr xen-4.9.2/tools/ocaml/libs/xb/packet.ml0000664000175000017500000000273413256712137016061 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type t = { tid: int; rid: int; ty: Op.operation; data: string; } exception Error of string exception DataError of string external string_of_header: int -> int -> int -> int -> string = "stub_string_of_header" let create tid rid ty data = { tid = tid; rid = rid; ty = ty; data = data; } let of_partialpkt ppkt = create ppkt.Partial.tid ppkt.Partial.rid ppkt.Partial.ty (Buffer.contents ppkt.Partial.buf) let to_string pkt = let header = string_of_header pkt.tid pkt.rid (Op.to_cval pkt.ty) (String.length pkt.data) in header ^ pkt.data let unpack pkt = pkt.tid, pkt.rid, pkt.ty, pkt.data let get_tid pkt = pkt.tid let get_ty pkt = pkt.ty let get_data pkt = let l = String.length pkt.data in if l > 0 && pkt.data.[l - 1] = '\000' then String.sub pkt.data 0 (l - 1) else pkt.data let get_rid pkt = pkt.ridxen-4.9.2/tools/ocaml/libs/xb/partial.ml0000664000175000017500000000314313256712137016241 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type pkt = { tid: int; rid: int; ty: Op.operation; len: int; buf: Buffer.t; } external header_size: unit -> int = "stub_header_size" external header_of_string_internal: string -> int * int * int * int = "stub_header_of_string" let xenstore_payload_max = 4096 (* xen/include/public/io/xs_wire.h *) let of_string s = let tid, rid, opint, dlen = header_of_string_internal s in (* A packet which is bigger than xenstore_payload_max is illegal. This will leave the guest connection is a bad state and will be hard to recover from without restarting the connection (ie rebooting the guest) *) let dlen = min xenstore_payload_max dlen in { tid = tid; rid = rid; ty = (Op.of_cval opint); len = dlen; buf = Buffer.create dlen; } let append pkt s sz = if pkt.len > 4096 then failwith "Buffer.add: cannot grow buffer"; Buffer.add_string pkt.buf (String.sub s 0 sz) let to_complete pkt = pkt.len - (Buffer.length pkt.buf) xen-4.9.2/tools/ocaml/libs/Makefile0000664000175000017500000000043013256712137015276 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk SUBDIRS= \ mmap \ xentoollog \ xc eventchn \ xb xs xl .PHONY: all all: subdirs-all .PHONY: install install: subdirs-install .PHONY: clean clean: subdirs-clean .PHONY: distclean distclean: subdirs-distclean xen-4.9.2/tools/ocaml/libs/xs/0000775000175000017500000000000013256712137014273 5ustar smbsmbxen-4.9.2/tools/ocaml/libs/xs/xst.ml0000664000175000017500000000413313256712137015444 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type ops = { directory: string -> string list; read: string -> string; readv: string -> string list -> string list; write: string -> string -> unit; writev: string -> (string * string) list -> unit; mkdir: string -> unit; rm: string -> unit; getperms: string -> Xsraw.perms; setperms: string -> Xsraw.perms -> unit; setpermsv: string -> string list -> Xsraw.perms -> unit; } let get_operations tid xsh = { directory = (fun path -> Xsraw.directory tid path xsh); read = (fun path -> Xsraw.read tid path xsh); readv = (fun dir vec -> Xsraw.readv tid dir vec xsh); write = (fun path value -> Xsraw.write tid path value xsh); writev = (fun dir vec -> Xsraw.writev tid dir vec xsh); mkdir = (fun path -> Xsraw.mkdir tid path xsh); rm = (fun path -> Xsraw.rm tid path xsh); getperms = (fun path -> Xsraw.getperms tid path xsh); setperms = (fun path perms -> Xsraw.setperms tid path perms xsh); setpermsv = (fun dir vec perms -> Xsraw.setpermsv tid dir vec perms xsh); } let transaction xsh (f: ops -> 'a) : 'a = let commited = ref false and result = ref None in while not !commited do let tid = Xsraw.transaction_start xsh in let t = get_operations tid xsh in begin try result := Some (f t) with exn -> ignore (Xsraw.transaction_end tid false xsh); raise exn end; commited := Xsraw.transaction_end tid true xsh done; match !result with | None -> failwith "internal error in transaction" | Some result -> result xen-4.9.2/tools/ocaml/libs/xs/xsraw.mli0000664000175000017500000000532213256712137016144 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) exception Partial_not_empty exception Unexpected_packet of string exception Invalid_path of string val unexpected_packet : Xenbus.Xb.Op.operation -> Xenbus.Xb.Op.operation -> 'a type con = { xb : Xenbus.Xb.t; watchevents : (string * string) Queue.t; } val close : con -> unit val open_fd : Unix.file_descr -> con val split_string : ?limit:int -> char -> string -> string list type perm = PERM_NONE | PERM_READ | PERM_WRITE | PERM_RDWR type perms = int * perm * (int * perm) list val string_of_perms : int * perm * (int * perm) list -> string val perms_of_string : string -> int * perm * (int * perm) list val pkt_send : con -> unit val pkt_recv : con -> Xenbus.Xb.Packet.t val pkt_recv_timeout : con -> float -> bool * Xenbus.Xb.Packet.t option val queue_watchevent : con -> string -> unit val has_watchevents : con -> bool val get_watchevent : con -> string * string val read_watchevent : con -> string * string val sync_recv : Xenbus.Xb.Op.operation -> con -> string val sync : (Xenbus.Xb.t -> 'a) -> con -> string val ack : string -> unit val validate_path : string -> unit val validate_watch_path : string -> unit val directory : int -> string -> con -> string list val debug : string list -> con -> string val read : int -> string -> con -> string val readv : int -> string -> string list -> con -> string list val getperms : int -> string -> con -> int * perm * (int * perm) list val watch : string -> string -> con -> unit val unwatch : string -> string -> con -> unit val transaction_start : con -> int val transaction_end : int -> bool -> con -> bool val introduce : int -> nativeint -> int -> con -> unit val release : int -> con -> unit val resume : int -> con -> unit val getdomainpath : int -> con -> string val write : int -> string -> string -> con -> unit val writev : int -> string -> (string * string) list -> con -> unit val mkdir : int -> string -> con -> unit val rm : int -> string -> con -> unit val setperms : int -> string -> int * perm * (int * perm) list -> con -> unit val setpermsv : int -> string -> string list -> int * perm * (int * perm) list -> con -> unit xen-4.9.2/tools/ocaml/libs/xs/queueop.ml0000664000175000017500000000535413256712137016317 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) open Xenbus let data_concat ls = (String.concat "\000" ls) ^ "\000" let queue_path ty (tid: int) (path: string) con = let data = data_concat [ path; ] in Xb.queue con (Xb.Packet.create tid 0 ty data) (* operations *) let directory tid path con = queue_path Xb.Op.Directory tid path con let read tid path con = queue_path Xb.Op.Read tid path con let getperms tid path con = queue_path Xb.Op.Getperms tid path con let debug commands con = Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Debug (data_concat commands)) let watch path data con = let data = data_concat [ path; data; ] in Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Watch data) let unwatch path data con = let data = data_concat [ path; data; ] in Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Unwatch data) let transaction_start con = Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Transaction_start (data_concat [])) let transaction_end tid commit con = let data = data_concat [ (if commit then "T" else "F"); ] in Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Transaction_end data) let introduce domid mfn port con = let data = data_concat [ Printf.sprintf "%u" domid; Printf.sprintf "%nu" mfn; string_of_int port; ] in Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Introduce data) let release domid con = let data = data_concat [ Printf.sprintf "%u" domid; ] in Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Release data) let resume domid con = let data = data_concat [ Printf.sprintf "%u" domid; ] in Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Resume data) let getdomainpath domid con = let data = data_concat [ Printf.sprintf "%u" domid; ] in Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Getdomainpath data) let write tid path value con = let data = path ^ "\000" ^ value (* no NULL at the end *) in Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Write data) let mkdir tid path con = queue_path Xb.Op.Mkdir tid path con let rm tid path con = queue_path Xb.Op.Rm tid path con let setperms tid path perms con = let data = data_concat [ path; perms ] in Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Setperms data) xen-4.9.2/tools/ocaml/libs/xs/xsraw.ml0000664000175000017500000001724113256712137015776 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) open Xenbus exception Partial_not_empty exception Unexpected_packet of string (** Thrown when a path looks invalid e.g. if it contains "//" *) exception Invalid_path of string let unexpected_packet expected received = let s = Printf.sprintf "expecting %s received %s" (Xb.Op.to_string expected) (Xb.Op.to_string received) in raise (Unexpected_packet s) type con = { xb: Xenbus.Xb.t; watchevents: (string * string) Queue.t; } let close con = Xb.close con.xb let open_fd fd = { xb = Xb.open_fd fd; watchevents = Queue.create (); } let rec split_string ?limit:(limit=(-1)) c s = let i = try String.index s c with Not_found -> -1 in let nlimit = if limit = -1 || limit = 0 then limit else limit - 1 in if i = -1 || nlimit = 0 then [ s ] else let a = String.sub s 0 i and b = String.sub s (i + 1) (String.length s - i - 1) in a :: (split_string ~limit: nlimit c b) type perm = PERM_NONE | PERM_READ | PERM_WRITE | PERM_RDWR type perms = int * perm * (int * perm) list let string_of_perms perms = let owner, other, acl = perms in let char_of_perm perm = match perm with PERM_NONE -> 'n' | PERM_READ -> 'r' | PERM_WRITE -> 'w' | PERM_RDWR -> 'b' in let string_of_perm (id, perm) = Printf.sprintf "%c%u" (char_of_perm perm) id in String.concat "\000" (List.map string_of_perm ((owner,other) :: acl)) let perms_of_string s = let perm_of_char c = match c with 'n' -> PERM_NONE | 'r' -> PERM_READ | 'w' -> PERM_WRITE | 'b' -> PERM_RDWR | c -> invalid_arg (Printf.sprintf "unknown permission type: %c" c) in let perm_of_string s = if String.length s < 2 then invalid_arg (Printf.sprintf "perm of string: length = %d; contents=\"%s\"" (String.length s) s) else begin int_of_string (String.sub s 1 (String.length s - 1)), perm_of_char s.[0] end in let rec split s = try let i = String.index s '\000' in String.sub s 0 i :: split (String.sub s (i + 1) (String.length s - 1 - i)) with Not_found -> if s = "" then [] else [ s ] in let l = List.map perm_of_string (split s) in match l with h :: l -> (fst h, snd h, l) | [] -> (0, PERM_NONE, []) (* send one packet - can sleep *) let pkt_send con = if Xb.has_old_output con.xb then raise Partial_not_empty; let workdone = ref false in while not !workdone do workdone := Xb.output con.xb done (* receive one packet - can sleep *) let pkt_recv con = let workdone = ref false in while not !workdone do workdone := Xb.input con.xb done; Xb.get_in_packet con.xb let pkt_recv_timeout con timeout = let fd = Xb.get_fd con.xb in let r, _, _ = Unix.select [ fd ] [] [] timeout in if r = [] then true, None else ( let workdone = Xb.input con.xb in if workdone then false, (Some (Xb.get_in_packet con.xb)) else false, None ) let queue_watchevent con data = let ls = split_string ~limit:2 '\000' data in if List.length ls != 2 then raise (Xb.Packet.DataError "arguments number mismatch"); let event = List.nth ls 0 and event_data = List.nth ls 1 in Queue.push (event, event_data) con.watchevents let has_watchevents con = Queue.length con.watchevents > 0 let get_watchevent con = Queue.pop con.watchevents let read_watchevent con = let pkt = pkt_recv con in match Xb.Packet.get_ty pkt with | Xb.Op.Watchevent -> queue_watchevent con (Xb.Packet.get_data pkt); Queue.pop con.watchevents | ty -> unexpected_packet Xb.Op.Watchevent ty (* send one packet in the queue, and wait for reply *) let rec sync_recv ty con = let pkt = pkt_recv con in match Xb.Packet.get_ty pkt with | Xb.Op.Error -> ( match Xb.Packet.get_data pkt with | "ENOENT" -> raise Xb.Noent | "EAGAIN" -> raise Xb.Eagain | "EINVAL" -> raise Xb.Invalid | s -> raise (Xb.Packet.Error s)) | Xb.Op.Watchevent -> queue_watchevent con (Xb.Packet.get_data pkt); sync_recv ty con | rty when rty = ty -> Xb.Packet.get_data pkt | rty -> unexpected_packet ty rty let sync f con = (* queue a query using function f *) f con.xb; if Xb.output_len con.xb = 0 then Printf.printf "output len = 0\n%!"; let ty = Xb.Packet.get_ty (Xb.peek_output con.xb) in pkt_send con; sync_recv ty con let ack s = if s = "OK" then () else raise (Xb.Packet.DataError s) (** Check paths are suitable for read/write/mkdir/rm/directory etc (NOT watches) *) let validate_path path = (* Paths shouldn't have a "//" in the middle *) let bad = "//" in for offset = 0 to String.length path - (String.length bad) do if String.sub path offset (String.length bad) = bad then raise (Invalid_path path) done; (* Paths shouldn't have a "/" at the end, except for the root *) if path <> "/" && path <> "" && path.[String.length path - 1] = '/' then raise (Invalid_path path) (** Check to see if a path is suitable for watches *) let validate_watch_path path = (* Check for stuff like @releaseDomain etc first *) if path <> "" && path.[0] = '@' then () else validate_path path let debug command con = sync (Queueop.debug command) con let directory tid path con = validate_path path; let data = sync (Queueop.directory tid path) con in split_string '\000' data let read tid path con = validate_path path; sync (Queueop.read tid path) con let readv tid dir vec con = List.map (fun path -> validate_path path; read tid path con) (if dir <> "" then (List.map (fun v -> dir ^ "/" ^ v) vec) else vec) let getperms tid path con = validate_path path; perms_of_string (sync (Queueop.getperms tid path) con) let watch path data con = validate_watch_path path; ack (sync (Queueop.watch path data) con) let unwatch path data con = validate_watch_path path; ack (sync (Queueop.unwatch path data) con) let transaction_start con = let data = sync (Queueop.transaction_start) con in try int_of_string data with _ -> raise (Packet.DataError (Printf.sprintf "int expected; got '%s'" data)) let transaction_end tid commit con = try ack (sync (Queueop.transaction_end tid commit) con); true with Xb.Eagain -> false let introduce domid mfn port con = ack (sync (Queueop.introduce domid mfn port) con) let release domid con = ack (sync (Queueop.release domid) con) let resume domid con = ack (sync (Queueop.resume domid) con) let getdomainpath domid con = sync (Queueop.getdomainpath domid) con let write tid path value con = validate_path path; ack (sync (Queueop.write tid path value) con) let writev tid dir vec con = List.iter (fun (entry, value) -> let path = (if dir <> "" then dir ^ "/" ^ entry else entry) in validate_path path; write tid path value con) vec let mkdir tid path con = validate_path path; ack (sync (Queueop.mkdir tid path) con) let rm tid path con = validate_path path; try ack (sync (Queueop.rm tid path) con) with Xb.Noent -> () let setperms tid path perms con = validate_path path; ack (sync (Queueop.setperms tid path (string_of_perms perms)) con) let setpermsv tid dir vec perms con = List.iter (fun entry -> let path = (if dir <> "" then dir ^ "/" ^ entry else entry) in validate_path path; setperms tid path perms con) vec xen-4.9.2/tools/ocaml/libs/xs/xs.mli0000664000175000017500000000573313256712137015440 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) exception Timeout (** Throws this rather than a miscellaneous Unix.connect failed *) exception Failed_to_connect (** perms contains 3 things: - owner domid. - other perm: applied to domain that is not owner or in ACL. - ACL: list of per-domain permission *) type perms = Xsraw.perms type domid = int type con type xsh = { con : con; debug: string list -> string; directory : string -> string list; read : string -> string; readv : string -> string list -> string list; write : string -> string -> unit; writev : string -> (string * string) list -> unit; mkdir : string -> unit; rm : string -> unit; getperms : string -> perms; setperms : string -> perms -> unit; setpermsv : string -> string list -> perms -> unit; introduce : domid -> nativeint -> int -> unit; release : domid -> unit; resume : domid -> unit; getdomainpath : domid -> string; watch : string -> string -> unit; unwatch : string -> string -> unit; } (** get operations provide a vector of xenstore function that apply to one connection *) val get_operations : con -> xsh (** create a transaction with a vector of function that can be applied into the transaction. *) val transaction : xsh -> (Xst.ops -> 'a) -> 'a (** watch manipulation on a connection *) val has_watchevents : xsh -> bool val get_watchevent : xsh -> string * string val read_watchevent : xsh -> string * string (** get_fd return the fd of the connection to be able to select on it. NOTE: it works only for socket-based connection *) val get_fd : xsh -> Unix.file_descr (** wait for watchevent with a timeout. Until the callback return true, every watch during the time specified, will be pass to the callback. NOTE: it works only when use with a socket-based connection *) val read_watchevent_timeout : xsh -> float -> (string * string -> bool) -> unit (** register a set of watches, then wait for watchevent. remove all watches previously set before giving back the hand. *) val monitor_paths : xsh -> (string * string) list -> float -> (string * string -> bool) -> unit (** open a socket-based xenstored connection *) val daemon_open : unit -> xsh (** open a mmap-based xenstored connection *) val domain_open : unit -> xsh (** close any xenstored connection *) val close : xsh -> unit xen-4.9.2/tools/ocaml/libs/xs/Makefile0000664000175000017500000000235513256712137015740 0ustar smbsmbTOPLEVEL=$(CURDIR)/../.. XEN_ROOT=$(TOPLEVEL)/../.. include $(TOPLEVEL)/common.make OCAMLINCLUDE += -I ../xb/ OCAMLOPTFLAGS += -for-pack Xenstore .NOTPARALLEL: # Ocaml is such a PITA! PREINTF = xsraw.cmi xst.cmi PREOBJS = queueop xsraw xst PRELIBS = $(foreach obj, $(PREOBJS),$(obj).cmo) $(foreach obj,$(PREOJBS),$(obj).cmx) OBJS = paths queueop xsraw xst xs INTF = xsraw.cmi xst.cmi xs.cmi LIBS = xenstore.cma xenstore.cmxa all: $(PREINTF) $(PRELIBS) $(INTF) $(LIBS) $(PROGRAMS) bins: $(PROGRAMS) libs: $(LIBS) xenstore_OBJS = xenstore OCAML_NOC_LIBRARY = xenstore xenstore.cmx : $(foreach obj, $(OBJS), $(obj).cmx) $(E) " CMX $@" $(Q)$(OCAMLOPT) -pack -o $@ $^ xenstore.cmo : $(foreach obj, $(OBJS), $(obj).cmo) $(E) " CMO $@" $(Q)$(OCAMLC) -pack -o $@ $^ .PHONY: install install: $(LIBS) META mkdir -p $(OCAMLDESTDIR) $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenstore $(OCAMLFIND) install -destdir $(OCAMLDESTDIR) -ldconf ignore xenstore META $(LIBS) xenstore.cmo xenstore.cmi xenstore.cmx *.a .PHONY: uninstall uninstall: $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenstore include $(TOPLEVEL)/Makefile.rules genpath-target = $(call buildmakevars2module,paths.ml) $(eval $(genpath-target)) GENERATED_FILES += paths.ml xen-4.9.2/tools/ocaml/libs/xs/META.in0000664000175000017500000000022313256712137015346 0ustar smbsmbversion = "@VERSION@" description = "XenStore Interface" requires = "unix,xenbus" archive(byte) = "xenstore.cma" archive(native) = "xenstore.cmxa" xen-4.9.2/tools/ocaml/libs/xs/xst.mli0000664000175000017500000000221513256712137015614 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type ops = { directory : string -> string list; read : string -> string; readv : string -> string list -> string list; write : string -> string -> unit; writev : string -> (string * string) list -> unit; mkdir : string -> unit; rm : string -> unit; getperms : string -> Xsraw.perms; setperms : string -> Xsraw.perms -> unit; setpermsv : string -> string list -> Xsraw.perms -> unit; } val get_operations : int -> Xsraw.con -> ops val transaction : Xsraw.con -> (ops -> 'a) -> 'a xen-4.9.2/tools/ocaml/libs/xs/xs.ml0000664000175000017500000001321413256712137015260 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type perms = Xsraw.perms type con = Xsraw.con type domid = int type xsh = { con: con; debug: string list -> string; directory: string -> string list; read: string -> string; readv: string -> string list -> string list; write: string -> string -> unit; writev: string -> (string * string) list -> unit; mkdir: string -> unit; rm: string -> unit; getperms: string -> perms; setperms: string -> perms -> unit; setpermsv: string -> string list -> perms -> unit; introduce: domid -> nativeint -> int -> unit; release: domid -> unit; resume: domid -> unit; getdomainpath: domid -> string; watch: string -> string -> unit; unwatch: string -> string -> unit; } let get_operations con = { con = con; debug = (fun commands -> Xsraw.debug commands con); directory = (fun path -> Xsraw.directory 0 path con); read = (fun path -> Xsraw.read 0 path con); readv = (fun dir vec -> Xsraw.readv 0 dir vec con); write = (fun path value -> Xsraw.write 0 path value con); writev = (fun dir vec -> Xsraw.writev 0 dir vec con); mkdir = (fun path -> Xsraw.mkdir 0 path con); rm = (fun path -> Xsraw.rm 0 path con); getperms = (fun path -> Xsraw.getperms 0 path con); setperms = (fun path perms -> Xsraw.setperms 0 path perms con); setpermsv = (fun dir vec perms -> Xsraw.setpermsv 0 dir vec perms con); introduce = (fun id mfn port -> Xsraw.introduce id mfn port con); release = (fun id -> Xsraw.release id con); resume = (fun id -> Xsraw.resume id con); getdomainpath = (fun id -> Xsraw.getdomainpath id con); watch = (fun path data -> Xsraw.watch path data con); unwatch = (fun path data -> Xsraw.unwatch path data con); } let transaction xsh = Xst.transaction xsh.con let has_watchevents xsh = Xsraw.has_watchevents xsh.con let get_watchevent xsh = Xsraw.get_watchevent xsh.con let read_watchevent xsh = Xsraw.read_watchevent xsh.con let make fd = get_operations (Xsraw.open_fd fd) let get_fd xsh = Xenbus.Xb.get_fd xsh.con.Xsraw.xb exception Timeout (* Should never be thrown, indicates a bug in the read_watchevent_timetout function *) exception Timeout_with_nonempty_queue (* Just in case we screw up: poll the callback every couple of seconds rather than wait for the whole timeout period *) let max_blocking_time = 5. (* seconds *) let read_watchevent_timeout xsh timeout callback = let start_time = Unix.gettimeofday () in let end_time = start_time +. timeout in let left = ref timeout in (* Returns true if a watch event in the queue satisfied us *) let process_queued_events () = let success = ref false in while Xsraw.has_watchevents xsh.con && not(!success) do success := callback (Xsraw.get_watchevent xsh.con) done; !success in (* Returns true if a watch event read from the socket satisfied us *) let process_incoming_event () = let fd = get_fd xsh in let r, _, _ = Unix.select [ fd ] [] [] (min max_blocking_time !left) in (* If data is available for reading then read it *) if r = [] then false (* timeout, either a max_blocking_time or global *) else callback (Xsraw.read_watchevent xsh.con) in let success = ref false in while !left > 0. && not(!success) do (* NB the 'callback' might call back into Xs functions and as a side-effect, watches might be queued. Hence we must process the queue on every loop iteration *) (* First process all queued watch events *) if not(!success) then success := process_queued_events (); (* Then block for one more watch event *) if not(!success) then success := process_incoming_event (); (* Just in case our callback caused events to be queued and this is our last time round the loop: this prevents us throwing the Timeout_with_nonempty_queue spuriously *) if not(!success) then success := process_queued_events (); (* Update the time left *) let current_time = Unix.gettimeofday () in left := end_time -. current_time done; if not(!success) then begin (* Sanity check: it should be impossible for any events to be queued here *) if Xsraw.has_watchevents xsh.con then raise Timeout_with_nonempty_queue else raise Timeout end let monitor_paths xsh l time callback = let unwatch () = List.iter (fun (w,v) -> try xsh.unwatch w v with _ -> ()) l in List.iter (fun (w,v) -> xsh.watch w v) l; begin try read_watchevent_timeout xsh time callback; with exn -> unwatch (); raise exn; end; unwatch () let daemon_socket = Paths.xen_run_stored ^ "/socket" (** Throws this rather than a miscellaneous Unix.connect failed *) exception Failed_to_connect let daemon_open () = try let sockaddr = Unix.ADDR_UNIX(daemon_socket) in let sock = Unix.socket Unix.PF_UNIX Unix.SOCK_STREAM 0 in Unix.connect sock sockaddr; Unix.set_close_on_exec sock; make sock with _ -> raise Failed_to_connect let domain_open () = let path = try let devpath = "/dev/xen/xenbus" in Unix.access devpath [ Unix.F_OK ]; devpath with Unix.Unix_error(_, _, _) -> "/proc/xen/xenbus" in let fd = Unix.openfile path [ Unix.O_RDWR ] 0o550 in Unix.set_close_on_exec fd; make fd let close xsh = Xsraw.close xsh.con xen-4.9.2/tools/ocaml/libs/mmap/0000775000175000017500000000000013256712137014573 5ustar smbsmbxen-4.9.2/tools/ocaml/libs/mmap/mmap_stubs.h0000664000175000017500000000163113256712137017117 0ustar smbsmb/* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef C_MMAP_H #define C_MMAP_H #include #include #include #include #include #include struct mmap_interface { void *addr; int len; }; #endif xen-4.9.2/tools/ocaml/libs/mmap/xenmmap_stubs.c0000664000175000017500000000662013256712137017630 0ustar smbsmb/* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include "mmap_stubs.h" #include #include #include #include #include #include #define Intf_val(a) ((struct mmap_interface *) a) static int mmap_interface_init(struct mmap_interface *intf, int fd, int pflag, int mflag, int len, int offset) { intf->len = len; intf->addr = mmap(NULL, len, pflag, mflag, fd, offset); return (intf->addr == MAP_FAILED) ? errno : 0; } CAMLprim value stub_mmap_init(value fd, value pflag, value mflag, value len, value offset) { CAMLparam5(fd, pflag, mflag, len, offset); CAMLlocal1(result); int c_pflag, c_mflag; switch (Int_val(pflag)) { case 0: c_pflag = PROT_READ; break; case 1: c_pflag = PROT_WRITE; break; case 2: c_pflag = PROT_READ|PROT_WRITE; break; default: caml_invalid_argument("protectiontype"); } switch (Int_val(mflag)) { case 0: c_mflag = MAP_SHARED; break; case 1: c_mflag = MAP_PRIVATE; break; default: caml_invalid_argument("maptype"); } result = caml_alloc(sizeof(struct mmap_interface), Abstract_tag); if (mmap_interface_init(Intf_val(result), Int_val(fd), c_pflag, c_mflag, Int_val(len), Int_val(offset))) caml_failwith("mmap"); CAMLreturn(result); } CAMLprim value stub_mmap_final(value intf) { CAMLparam1(intf); if (Intf_val(intf)->addr != MAP_FAILED) munmap(Intf_val(intf)->addr, Intf_val(intf)->len); Intf_val(intf)->addr = MAP_FAILED; CAMLreturn(Val_unit); } CAMLprim value stub_mmap_read(value intf, value start, value len) { CAMLparam3(intf, start, len); CAMLlocal1(data); int c_start; int c_len; c_start = Int_val(start); c_len = Int_val(len); if (c_start > Intf_val(intf)->len) caml_invalid_argument("start invalid"); if (c_start + c_len > Intf_val(intf)->len) caml_invalid_argument("len invalid"); data = caml_alloc_string(c_len); memcpy((char *) data, Intf_val(intf)->addr + c_start, c_len); CAMLreturn(data); } CAMLprim value stub_mmap_write(value intf, value data, value start, value len) { CAMLparam4(intf, data, start, len); int c_start; int c_len; c_start = Int_val(start); c_len = Int_val(len); if (c_start > Intf_val(intf)->len) caml_invalid_argument("start invalid"); if (c_start + c_len > Intf_val(intf)->len) caml_invalid_argument("len invalid"); memcpy(Intf_val(intf)->addr + c_start, (char *) data, c_len); CAMLreturn(Val_unit); } CAMLprim value stub_mmap_getpagesize(value unit) { CAMLparam1(unit); CAMLlocal1(data); data = Val_int(getpagesize()); CAMLreturn(data); } xen-4.9.2/tools/ocaml/libs/mmap/Makefile0000664000175000017500000000123313256712137016232 0ustar smbsmbTOPLEVEL=$(CURDIR)/../.. XEN_ROOT=$(TOPLEVEL)/../.. include $(TOPLEVEL)/common.make OBJS = xenmmap INTF = $(foreach obj, $(OBJS),$(obj).cmi) LIBS = xenmmap.cma xenmmap.cmxa all: $(INTF) $(LIBS) $(PROGRAMS) bins: $(PROGRAMS) libs: $(LIBS) xenmmap_OBJS = $(OBJS) xenmmap_C_OBJS = xenmmap_stubs OCAML_LIBRARY = xenmmap .PHONY: install install: $(LIBS) META mkdir -p $(OCAMLDESTDIR) $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenmmap $(OCAMLFIND) install -destdir $(OCAMLDESTDIR) -ldconf ignore xenmmap META $(INTF) $(LIBS) *.a *.so *.cmx .PHONY: uninstall uninstall: $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenmmap include $(TOPLEVEL)/Makefile.rules xen-4.9.2/tools/ocaml/libs/mmap/META.in0000664000175000017500000000017613256712137015655 0ustar smbsmbversion = "@VERSION@" description = "Mmap interface extension" archive(byte) = "xenmmap.cma" archive(native) = "xenmmap.cmxa" xen-4.9.2/tools/ocaml/libs/mmap/xenmmap.mli0000664000175000017500000000226613256712137016751 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type mmap_interface type mmap_prot_flag = RDONLY | WRONLY | RDWR type mmap_map_flag = SHARED | PRIVATE external mmap : Unix.file_descr -> mmap_prot_flag -> mmap_map_flag -> int -> int -> mmap_interface = "stub_mmap_init" external unmap : mmap_interface -> unit = "stub_mmap_final" external read : mmap_interface -> int -> int -> string = "stub_mmap_read" external write : mmap_interface -> string -> int -> int -> unit = "stub_mmap_write" external getpagesize : unit -> int = "stub_mmap_getpagesize" xen-4.9.2/tools/ocaml/libs/mmap/xenmmap.ml0000664000175000017500000000256313256712137016600 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type mmap_interface type mmap_prot_flag = RDONLY | WRONLY | RDWR type mmap_map_flag = SHARED | PRIVATE (* mmap: fd -> prot_flag -> map_flag -> length -> offset -> interface *) external mmap: Unix.file_descr -> mmap_prot_flag -> mmap_map_flag -> int -> int -> mmap_interface = "stub_mmap_init" external unmap: mmap_interface -> unit = "stub_mmap_final" (* read: interface -> start -> length -> data *) external read: mmap_interface -> int -> int -> string = "stub_mmap_read" (* write: interface -> data -> start -> length -> unit *) external write: mmap_interface -> string -> int -> int -> unit = "stub_mmap_write" (* getpagesize: unit -> size of page *) external getpagesize: unit -> int = "stub_mmap_getpagesize" xen-4.9.2/tools/ocaml/libs/xl/0000775000175000017500000000000013256712137014264 5ustar smbsmbxen-4.9.2/tools/ocaml/libs/xl/xenlight.ml.in0000664000175000017500000001020513256712137017043 0ustar smbsmb(* * Copyright (C) 2009-2011 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type ctx type domid = int type devid = int (* @@LIBXL_TYPES@@ *) exception Error of (error * string) external ctx_alloc: Xentoollog.handle -> ctx = "stub_libxl_ctx_alloc" external test_raise_exception: unit -> unit = "stub_raise_exception" type event = | POLLIN (* There is data to read *) | POLLPRI (* There is urgent data to read *) | POLLOUT (* Writing now will not block *) | POLLERR (* Error condition (revents only) *) | POLLHUP (* Device has been disconnected (revents only) *) | POLLNVAL (* Invalid request: fd not open (revents only). *) module Domain = struct external create_new : ctx -> Domain_config.t -> ?async:'a -> unit -> domid = "stub_libxl_domain_create_new" external create_restore : ctx -> Domain_config.t -> (Unix.file_descr * Domain_restore_params.t) -> ?async:'a -> unit -> domid = "stub_libxl_domain_create_restore" external shutdown : ctx -> domid -> unit = "stub_libxl_domain_shutdown" external reboot : ctx -> domid -> unit = "stub_libxl_domain_reboot" external destroy : ctx -> domid -> ?async:'a -> unit -> unit = "stub_libxl_domain_destroy" external suspend : ctx -> domid -> Unix.file_descr -> ?async:'a -> unit -> unit = "stub_libxl_domain_suspend" external pause : ctx -> domid -> unit = "stub_libxl_domain_pause" external unpause : ctx -> domid -> unit = "stub_libxl_domain_unpause" external send_trigger : ctx -> domid -> trigger -> int -> unit = "stub_xl_send_trigger" external send_sysrq : ctx -> domid -> char -> unit = "stub_xl_send_sysrq" end module Host = struct type console_reader exception End_of_file external xen_console_read_start : ctx -> int -> console_reader = "stub_libxl_xen_console_read_start" external xen_console_read_line : ctx -> console_reader -> string = "stub_libxl_xen_console_read_line" external xen_console_read_finish : ctx -> console_reader -> unit = "stub_libxl_xen_console_read_finish" external send_debug_keys : ctx -> string -> unit = "stub_xl_send_debug_keys" end module Async = struct type for_libxl type event_hooks type osevent_hooks external osevent_register_hooks' : ctx -> 'a -> osevent_hooks = "stub_libxl_osevent_register_hooks" external osevent_occurred_fd : ctx -> for_libxl -> Unix.file_descr -> event list -> event list -> unit = "stub_libxl_osevent_occurred_fd" external osevent_occurred_timeout : ctx -> for_libxl -> unit = "stub_libxl_osevent_occurred_timeout" let osevent_register_hooks ctx ~user ~fd_register ~fd_modify ~fd_deregister ~timeout_register ~timeout_fire_now = Callback.register "libxl_fd_register" fd_register; Callback.register "libxl_fd_modify" fd_modify; Callback.register "libxl_fd_deregister" fd_deregister; Callback.register "libxl_timeout_register" timeout_register; Callback.register "libxl_timeout_fire_now" timeout_fire_now; osevent_register_hooks' ctx user let async_register_callback ~async_callback = Callback.register "libxl_async_callback" async_callback external evenable_domain_death : ctx -> domid -> int -> unit = "stub_libxl_evenable_domain_death" external event_register_callbacks' : ctx -> 'a -> event_hooks = "stub_libxl_event_register_callbacks" let event_register_callbacks ctx ~user ~event_occurs_callback ~event_disaster_callback = Callback.register "libxl_event_occurs_callback" event_occurs_callback; Callback.register "libxl_event_disaster_callback" event_disaster_callback; event_register_callbacks' ctx user end let register_exceptions () = Callback.register_exception "Xenlight.Error" (Error(ERROR_FAIL, "")); Callback.register_exception "Xenlight.Host.End_of_file" (Host.End_of_file) xen-4.9.2/tools/ocaml/libs/xl/Makefile0000664000175000017500000000375613256712137015737 0ustar smbsmbTOPLEVEL=$(CURDIR)/../.. XEN_ROOT=$(TOPLEVEL)/../.. include $(TOPLEVEL)/common.make # ignore unused generated functions and allow mixed declarations and code CFLAGS += -Wno-unused -Wno-declaration-after-statement CFLAGS += $(CFLAGS_libxenlight) CFLAGS += -I ../xentoollog CFLAGS += $(APPEND_CFLAGS) OBJS = xenlight INTF = xenlight.cmi LIBS = xenlight.cma xenlight.cmxa OCAMLINCLUDE += -I ../xentoollog LIBS_xenlight = $(LDLIBS_libxenlight) xenlight_OBJS = $(OBJS) xenlight_C_OBJS = xenlight_stubs OCAML_LIBRARY = xenlight GENERATED_FILES += xenlight.ml xenlight.ml.tmp xenlight.mli xenlight.mli.tmp GENERATED_FILES += _libxl_types.ml.in _libxl_types.mli.in GENERATED_FILES += _libxl_types.inc META all: $(INTF) $(LIBS) xenlight.ml: xenlight.ml.in _libxl_types.ml.in $(Q)sed -e '1i\ (*\ * AUTO-GENERATED FILE DO NOT EDIT\ * Generated from xenlight.ml.in and _libxl_types.ml.in\ *)\ ' \ -e '/^(\* @@LIBXL_TYPES@@ \*)$$/r_libxl_types.ml.in' \ < xenlight.ml.in > xenlight.ml.tmp $(Q)mv xenlight.ml.tmp xenlight.ml xenlight.mli: xenlight.mli.in _libxl_types.mli.in $(Q)sed -e '1i\ (*\ * AUTO-GENERATED FILE DO NOT EDIT\ * Generated from xenlight.mli.in and _libxl_types.mli.in\ *)\ ' \ -e '/^(\* @@LIBXL_TYPES@@ \*)$$/r_libxl_types.mli.in' \ < xenlight.mli.in > xenlight.mli.tmp $(Q)mv xenlight.mli.tmp xenlight.mli _libxl_types.ml.in _libxl_types.mli.in _libxl_types.inc: genwrap.py $(XEN_ROOT)/tools/libxl/libxl_types.idl \ $(XEN_ROOT)/tools/libxl/idl.py PYTHONPATH=$(XEN_ROOT)/tools/libxl $(PYTHON) genwrap.py \ $(XEN_ROOT)/tools/libxl/libxl_types.idl \ _libxl_types.mli.in _libxl_types.ml.in _libxl_types.inc libs: $(LIBS) .PHONY: install install: $(LIBS) META mkdir -p $(OCAMLDESTDIR) $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenlight $(OCAMLFIND) install -destdir $(OCAMLDESTDIR) -ldconf ignore xenlight META $(INTF) $(LIBS) *.a *.so *.cmx .PHONY: uninstall uninstall: $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xenlight include $(TOPLEVEL)/Makefile.rules xen-4.9.2/tools/ocaml/libs/xl/META.in0000664000175000017500000000022513256712137015341 0ustar smbsmbversion = "@VERSION@" description = "Xen Toolstack Library" requires = "xentoollog" archive(byte) = "xenlight.cma" archive(native) = "xenlight.cmxa" xen-4.9.2/tools/ocaml/libs/xl/xenlight.mli.in0000664000175000017500000000720313256712137017220 0ustar smbsmb(* * Copyright (C) 2009-2011 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type ctx type domid = int type devid = int (* @@LIBXL_TYPES@@ *) exception Error of (error * string) val register_exceptions: unit -> unit external ctx_alloc: Xentoollog.handle -> ctx = "stub_libxl_ctx_alloc" external test_raise_exception: unit -> unit = "stub_raise_exception" type event = | POLLIN (* There is data to read *) | POLLPRI (* There is urgent data to read *) | POLLOUT (* Writing now will not block *) | POLLERR (* Error condition (revents only) *) | POLLHUP (* Device has been disconnected (revents only) *) | POLLNVAL (* Invalid request: fd not open (revents only). *) module Domain : sig external create_new : ctx -> Domain_config.t -> ?async:'a -> unit -> domid = "stub_libxl_domain_create_new" external create_restore : ctx -> Domain_config.t -> (Unix.file_descr * Domain_restore_params.t) -> ?async:'a -> unit -> domid = "stub_libxl_domain_create_restore" external shutdown : ctx -> domid -> unit = "stub_libxl_domain_shutdown" external reboot : ctx -> domid -> unit = "stub_libxl_domain_reboot" external destroy : ctx -> domid -> ?async:'a -> unit -> unit = "stub_libxl_domain_destroy" external suspend : ctx -> domid -> Unix.file_descr -> ?async:'a -> unit -> unit = "stub_libxl_domain_suspend" external pause : ctx -> domid -> unit = "stub_libxl_domain_pause" external unpause : ctx -> domid -> unit = "stub_libxl_domain_unpause" external send_trigger : ctx -> domid -> trigger -> int -> unit = "stub_xl_send_trigger" external send_sysrq : ctx -> domid -> char -> unit = "stub_xl_send_sysrq" end module Host : sig type console_reader exception End_of_file external xen_console_read_start : ctx -> int -> console_reader = "stub_libxl_xen_console_read_start" external xen_console_read_line : ctx -> console_reader -> string = "stub_libxl_xen_console_read_line" external xen_console_read_finish : ctx -> console_reader -> unit = "stub_libxl_xen_console_read_finish" external send_debug_keys : ctx -> string -> unit = "stub_xl_send_debug_keys" end module Async : sig type for_libxl type event_hooks type osevent_hooks val osevent_register_hooks : ctx -> user:'a -> fd_register:('a -> Unix.file_descr -> event list -> for_libxl -> 'b) -> fd_modify:('a -> Unix.file_descr -> 'b -> event list -> 'b) -> fd_deregister:('a -> Unix.file_descr -> 'b -> unit) -> timeout_register:('a -> int64 -> int64 -> for_libxl -> 'c) -> timeout_fire_now:('a -> 'c -> 'c) -> osevent_hooks external osevent_occurred_fd : ctx -> for_libxl -> Unix.file_descr -> event list -> event list -> unit = "stub_libxl_osevent_occurred_fd" external osevent_occurred_timeout : ctx -> for_libxl -> unit = "stub_libxl_osevent_occurred_timeout" val async_register_callback : async_callback:(result:error option -> user:'a -> unit) -> unit external evenable_domain_death : ctx -> domid -> int -> unit = "stub_libxl_evenable_domain_death" val event_register_callbacks : ctx -> user:'a -> event_occurs_callback:('a -> Event.t -> unit) -> event_disaster_callback:('a -> event_type -> string -> int -> unit) -> event_hooks end xen-4.9.2/tools/ocaml/libs/xl/genwrap.py0000664000175000017500000005373013256712137016311 0ustar smbsmb#!/usr/bin/python import sys,os import idl # typename -> ( ocaml_type, c_from_ocaml, ocaml_from_c ) builtins = { "bool": ("bool", "%(c)s = Bool_val(%(o)s)", "Val_bool(%(c)s)" ), "int": ("int", "%(c)s = Int_val(%(o)s)", "Val_int(%(c)s)" ), "char *": ("string option", "%(c)s = String_option_val(%(o)s)", "Val_string_option(%(c)s)"), "libxl_domid": ("domid", "%(c)s = Int_val(%(o)s)", "Val_int(%(c)s)" ), "libxl_devid": ("devid", "%(c)s = Int_val(%(o)s)", "Val_int(%(c)s)" ), "libxl_defbool": ("bool option", "%(c)s = Defbool_val(%(o)s)", "Val_defbool(%(c)s)" ), "libxl_uuid": ("int array", "Uuid_val(&%(c)s, %(o)s)", "Val_uuid(&%(c)s)"), "libxl_bitmap": ("bool array", "Bitmap_val(ctx, &%(c)s, %(o)s)", "Val_bitmap(&%(c)s)"), "libxl_key_value_list": ("(string * string) list", "libxl_key_value_list_val(&%(c)s, %(o)s)", "Val_key_value_list(&%(c)s)"), "libxl_string_list": ("string list", "libxl_string_list_val(&%(c)s, %(o)s)", "Val_string_list(&%(c)s)"), "libxl_mac": ("int array", "Mac_val(&%(c)s, %(o)s)", "Val_mac(&%(c)s)"), "libxl_hwcap": ("int32 array", None, "Val_hwcap(&%(c)s)"), "libxl_ms_vm_genid": ("int array", "Ms_vm_genid_val(&%(c)s, %(o)s)", "Val_ms_vm_genid(&%(c)s)"), # The following needs to be sorted out later "libxl_cpuid_policy_list": ("unit", "%(c)s = 0", "Val_unit"), } DEVICE_FUNCTIONS = [ ("add", ["ctx", "t", "domid", "?async:'a", "unit", "unit"]), ("remove", ["ctx", "t", "domid", "?async:'a", "unit", "unit"]), ("destroy", ["ctx", "t", "domid", "?async:'a", "unit", "unit"]), ] DEVICE_LIST = [ ("list", ["ctx", "domid", "t list"]), ] functions = { # ( name , [type1,type2,....] ) "device_vfb": DEVICE_FUNCTIONS, "device_vkb": DEVICE_FUNCTIONS, "device_disk": DEVICE_FUNCTIONS + DEVICE_LIST + [ ("insert", ["ctx", "t", "domid", "?async:'a", "unit", "unit"]), ("of_vdev", ["ctx", "domid", "string", "t"]), ], "device_nic": DEVICE_FUNCTIONS + DEVICE_LIST + [ ("of_devid", ["ctx", "domid", "int", "t"]), ], "device_pci": DEVICE_FUNCTIONS + DEVICE_LIST + [ ("assignable_add", ["ctx", "t", "bool", "unit"]), ("assignable_remove", ["ctx", "t", "bool", "unit"]), ("assignable_list", ["ctx", "t list"]), ], "dominfo": [ ("list", ["ctx", "t list"]), ("get", ["ctx", "domid", "t"]), ], "physinfo": [ ("get", ["ctx", "t"]), ], "cputopology": [ ("get", ["ctx", "t array"]), ], "domain_sched_params": [ ("get", ["ctx", "domid", "t"]), ("set", ["ctx", "domid", "t", "unit"]), ], } def stub_fn_name(ty, name): return "stub_xl_%s_%s" % (ty.rawname,name) def ocaml_type_of(ty): if ty.rawname in ["domid","devid"]: return ty.rawname elif isinstance(ty,idl.UInt): if ty.width in [8, 16]: # handle as ints width = None elif ty.width in [32, 64]: width = ty.width else: raise NotImplementedError("Cannot handle %d-bit int" % ty.width) if width: return "int%d" % ty.width else: return "int" elif isinstance(ty,idl.Array): return "%s array" % ocaml_type_of(ty.elem_type) elif isinstance(ty,idl.Builtin): if not builtins.has_key(ty.typename): raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) typename,_,_ = builtins[ty.typename] if not typename: raise NotImplementedError("No typename for Builtin %s (%s)" % (ty.typename, type(ty))) return typename elif isinstance(ty,idl.KeyedUnion): return ty.union_name elif isinstance(ty,idl.Aggregate): if ty.rawname is None: return ty.anon_struct else: return ty.rawname.capitalize() + ".t" else: return ty.rawname ocaml_keywords = ['and', 'as', 'assert', 'begin', 'end', 'class', 'constraint', 'do', 'done', 'downto', 'else', 'if', 'end', 'exception', 'external', 'false', 'for', 'fun', 'function', 'functor', 'if', 'in', 'include', 'inherit', 'initializer', 'lazy', 'let', 'match', 'method', 'module', 'mutable', 'new', 'object', 'of', 'open', 'or', 'private', 'rec', 'sig', 'struct', 'then', 'to', 'true', 'try', 'type', 'val', 'virtual', 'when', 'while', 'with'] def munge_name(name): if name in ocaml_keywords: return "xl_" + name else: return name def ocaml_instance_of_field(f): if isinstance(f.type, idl.KeyedUnion): name = f.type.keyvar.name else: name = f.name return "%s : %s" % (munge_name(name), ocaml_type_of(f.type)) def gen_struct(ty, indent): s = "" for f in ty.fields: if f.type.private: continue x = ocaml_instance_of_field(f) x = x.replace("\n", "\n"+indent) s += indent + x + ";\n" return s def gen_ocaml_keyedunions(ty, interface, indent, parent = None): s = "" union_type = "" if ty.rawname is not None: # Non-anonymous types need no special handling pass elif isinstance(ty, idl.KeyedUnion): if parent is None: nparent = ty.keyvar.name else: nparent = parent + "_" + ty.keyvar.name for f in ty.fields: if f.type is None: continue if f.type.rawname is not None: continue if isinstance(f.type, idl.Struct) and not f.type.has_fields(): continue s += "\ntype %s_%s =\n" % (nparent,f.name) s += "{\n" s += gen_struct(f.type, indent + "\t") s += "}\n" name = "%s__union" % ty.keyvar.name s += "\n" s += "type %s = " % name u = [] for f in ty.fields: if f.type is None: u.append("%s" % (f.name.capitalize())) elif isinstance(f.type, idl.Struct): if f.type.rawname is not None: u.append("%s of %s.t" % (f.name.capitalize(), f.type.rawname.capitalize())) elif f.type.has_fields(): u.append("%s of %s_%s" % (f.name.capitalize(), nparent, f.name)) else: u.append("%s" % (f.name.capitalize())) else: raise NotImplementedError("Cannot handle KeyedUnion fields which are not Structs") s += " | ".join(u) + "\n" ty.union_name = name union_type = "?%s:%s" % (munge_name(nparent), ty.keyvar.type.rawname) if s == "": return None, None return s.replace("\n", "\n%s" % indent), union_type def gen_ocaml_anonstruct(ty, interface, indent, parent = None): s= "" if ty.rawname is not None: # Non-anonymous types need no special handling pass elif isinstance(ty, idl.Struct): name = "%s__anon" % parent s += "type %s = {\n" % name s += gen_struct(ty, indent) s += "}\n" ty.anon_struct = name if s == "": return None s = indent + s return s.replace("\n", "\n%s" % indent) def gen_ocaml_ml(ty, interface, indent=""): if interface: s = ("""(* %s interface *)\n""" % ty.typename) else: s = ("""(* %s implementation *)\n""" % ty.typename) if isinstance(ty, idl.Enumeration): s += "type %s = \n" % ty.rawname for v in ty.values: s += "\t | %s\n" % v.rawname if interface: s += "\nval string_of_%s : %s -> string\n" % (ty.rawname, ty.rawname) else: s += "\nlet string_of_%s = function\n" % ty.rawname for v in ty.values: s += '\t| %s -> "%s"\n' % (v.rawname, v.valuename) elif isinstance(ty, idl.Aggregate): s += "" if ty.typename is None: raise NotImplementedError("%s has no typename" % type(ty)) else: module_name = ty.rawname[0].upper() + ty.rawname[1:] if interface: s += "module %s : sig\n" % module_name else: s += "module %s = struct\n" % module_name # Handle KeyedUnions... union_types = [] for f in ty.fields: ku, union_type = gen_ocaml_keyedunions(f.type, interface, "\t") if ku is not None: s += ku s += "\n" if union_type is not None: union_types.append(union_type) # Handle anonymous structs... for f in ty.fields: anon = gen_ocaml_anonstruct(f.type, interface, "\t", f.name) if anon is not None: s += anon s += "\n" s += "\ttype t =\n" s += "\t{\n" s += gen_struct(ty, "\t\t") s += "\t}\n" if ty.init_fn is not None: union_args = "".join([u + " -> " for u in union_types]) if interface: s += "\tval default : ctx -> %sunit -> t\n" % union_args else: s += "\texternal default : ctx -> %sunit -> t = \"stub_libxl_%s_init\"\n" % (union_args, ty.rawname) if functions.has_key(ty.rawname): for name,args in functions[ty.rawname]: s += "\texternal %s : " % name s += " -> ".join(args) s += " = \"%s\"\n" % stub_fn_name(ty,name) s += "end\n" else: raise NotImplementedError("%s" % type(ty)) return s.replace("\n", "\n%s" % indent) def c_val(ty, c, o, indent="", parent = None): s = indent if isinstance(ty,idl.UInt): if ty.width in [8, 16]: # handle as ints width = None elif ty.width in [32, 64]: width = ty.width else: raise NotImplementedError("Cannot handle %d-bit int" % ty.width) if width: s += "%s = Int%d_val(%s);" % (c, width, o) else: s += "%s = Int_val(%s);" % (c, o) elif isinstance(ty,idl.Builtin): if not builtins.has_key(ty.typename): raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) _,fn,_ = builtins[ty.typename] if not fn: raise NotImplementedError("No c_val fn for Builtin %s (%s)" % (ty.typename, type(ty))) s += "%s;" % (fn % { "o": o, "c": c }) elif isinstance (ty,idl.Array): s += "{\n" s += "\tint i;\n" s += "\t%s = Wosize_val(%s);\n" % (parent + ty.lenvar.name, o) s += "\t%s = (%s) calloc(%s, sizeof(*%s));\n" % (c, ty.typename, parent + ty.lenvar.name, c) s += "\tfor(i=0; i<%s; i++) {\n" % (parent + ty.lenvar.name) s += c_val(ty.elem_type, c+"[i]", "Field(%s, i)" % o, indent="\t\t", parent=parent) + "\n" s += "\t}\n" s += "}\n" elif isinstance(ty,idl.Enumeration) and (parent is None): n = 0 s += "switch(Int_val(%s)) {\n" % o for e in ty.values: s += " case %d: *%s = %s; break;\n" % (n, c, e.name) n += 1 s += " default: failwith_xl(ERROR_FAIL, \"cannot convert value to %s\"); break;\n" % ty.typename s += "}" elif isinstance(ty, idl.KeyedUnion): s += "{\n" s += "\tif(Is_long(%s)) {\n" % o n = 0 s += "\t\tswitch(Int_val(%s)) {\n" % o for f in ty.fields: if f.type is None or not f.type.has_fields(): s += "\t\t case %d: %s = %s; break;\n" % (n, parent + ty.keyvar.name, f.enumname) n += 1 s += "\t\t default: failwith_xl(ERROR_FAIL, \"variant handling bug %s%s (long)\"); break;\n" % (parent, ty.keyvar.name) s += "\t\t}\n" s += "\t} else {\n" s += "\t\t/* Is block... */\n" s += "\t\tswitch(Tag_val(%s)) {\n" % o n = 0 for f in ty.fields: if f.type is not None and f.type.has_fields(): if f.type.private: continue s += "\t\t case %d:\n" % (n) s += "\t\t %s = %s;\n" % (parent + ty.keyvar.name, f.enumname) (nparent,fexpr) = ty.member(c, f, False) s += "%s" % c_val(f.type, fexpr, "Field(%s, 0)" % o, parent=nparent, indent=indent+"\t\t ") s += "break;\n" n += 1 s += "\t\t default: failwith_xl(ERROR_FAIL, \"variant handling bug %s%s (block)\"); break;\n" % (parent, ty.keyvar.name) s += "\t\t}\n" s += "\t}\n" s += "}" elif isinstance(ty, idl.Aggregate) and (parent is None or ty.rawname is None): n = 0 for f in ty.fields: if f.type.private: continue (nparent,fexpr) = ty.member(c, f, ty.rawname is not None) s += "%s\n" % c_val(f.type, fexpr, "Field(%s, %d)" % (o,n), parent=nparent) n = n + 1 else: s += "%s_val(ctx, %s, %s);" % (ty.rawname, ty.pass_arg(c, parent is None, passby=idl.PASS_BY_REFERENCE), o) return s.replace("\n", "\n%s" % indent) def gen_c_val(ty, indent=""): s = "/* Convert caml value to %s */\n" % ty.rawname s += "static int %s_val (libxl_ctx *ctx, %s, value v)\n" % (ty.rawname, ty.make_arg("c_val", passby=idl.PASS_BY_REFERENCE)) s += "{\n" s += "\tCAMLparam1(v);\n" s += "\n" s += c_val(ty, "c_val", "v", indent="\t") + "\n" s += "\tCAMLreturn(0);\n" s += "}\n" return s.replace("\n", "\n%s" % indent) def ocaml_Val(ty, o, c, indent="", parent = None): s = indent if isinstance(ty,idl.UInt): if ty.width in [8, 16]: # handle as ints width = None elif ty.width in [32, 64]: width = ty.width else: raise NotImplementedError("Cannot handle %d-bit int" % ty.width) if width: s += "%s = caml_copy_int%d(%s);" % (o, width, c) else: s += "%s = Val_int(%s);" % (o, c) elif isinstance(ty,idl.Builtin): if not builtins.has_key(ty.typename): raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) _,_,fn = builtins[ty.typename] if not fn: raise NotImplementedError("No ocaml Val fn for Builtin %s (%s)" % (ty.typename, type(ty))) s += "%s = %s;" % (o, fn % { "c": c }) elif isinstance(ty, idl.Array): s += "{\n" s += "\t int i;\n" s += "\t CAMLlocal1(array_elem);\n" s += "\t %s = caml_alloc(%s,0);\n" % (o, parent + ty.lenvar.name) s += "\t for(i=0; i<%s; i++) {\n" % (parent + ty.lenvar.name) s += "\t %s\n" % ocaml_Val(ty.elem_type, "array_elem", c + "[i]", "", parent=parent) s += "\t Store_field(%s, i, array_elem);\n" % o s += "\t }\n" s += "\t}" elif isinstance(ty,idl.Enumeration) and (parent is None): n = 0 s += "switch(%s) {\n" % c for e in ty.values: s += " case %s: %s = Val_int(%d); break;\n" % (e.name, o, n) n += 1 s += " default: failwith_xl(ERROR_FAIL, \"cannot convert value from %s\"); break;\n" % ty.typename s += "}" elif isinstance(ty, idl.KeyedUnion): n = 0 m = 0 s += "switch(%s) {\n" % (parent + ty.keyvar.name) for f in ty.fields: s += "\t case %s:\n" % f.enumname if f.type is None: s += "\t /* %d: None */\n" % n s += "\t %s = Val_long(%d);\n" % (o,n) n += 1 elif not f.type.has_fields(): s += "\t /* %d: Long */\n" % n s += "\t %s = Val_long(%d);\n" % (o,n) n += 1 else: s += "\t /* %d: Block */\n" % m (nparent,fexpr) = ty.member(c, f, parent is None) s += "\t {\n" s += "\t\t CAMLlocal1(tmp);\n" s += "\t\t %s = caml_alloc(%d,%d);\n" % (o, 1, m) s += ocaml_Val(f.type, 'tmp', fexpr, indent="\t\t ", parent=nparent) s += "\n" s += "\t\t Store_field(%s, 0, tmp);\n" % o s += "\t }\n" m += 1 #s += "\t %s = caml_alloc(%d,%d);\n" % (o,len(f.type.fields),n) s += "\t break;\n" s += "\t default: failwith_xl(ERROR_FAIL, \"cannot convert value from %s\"); break;\n" % ty.typename s += "\t}" elif isinstance(ty,idl.Aggregate) and (parent is None or ty.rawname is None): s += "{\n" if ty.rawname is None: fn = "anon_field" else: fn = "%s_field" % ty.rawname s += "\tCAMLlocal1(%s);\n" % fn s += "\n" s += "\t%s = caml_alloc_tuple(%d);\n" % (o, len(ty.fields)) n = 0 for f in ty.fields: if f.type.private: continue (nparent,fexpr) = ty.member(c, f, parent is None) s += "\n" s += "\t%s\n" % ocaml_Val(f.type, fn, ty.pass_arg(fexpr, c), parent=nparent) s += "\tStore_field(%s, %d, %s);\n" % (o, n, fn) n = n + 1 s += "}" else: s += "%s = Val_%s(%s);" % (o, ty.rawname, ty.pass_arg(c, parent is None)) return s.replace("\n", "\n%s" % indent).rstrip(indent) def gen_Val_ocaml(ty, indent=""): s = "/* Convert %s to a caml value */\n" % ty.rawname s += "static value Val_%s (%s)\n" % (ty.rawname, ty.make_arg(ty.rawname+"_c")) s += "{\n" s += "\tCAMLparam0();\n" s += "\tCAMLlocal1(%s_ocaml);\n" % ty.rawname s += ocaml_Val(ty, "%s_ocaml" % ty.rawname, "%s_c" % ty.rawname, indent="\t") + "\n" s += "\tCAMLreturn(%s_ocaml);\n" % ty.rawname s += "}\n" return s.replace("\n", "\n%s" % indent) def gen_c_stub_prototype(ty, fns): s = "/* Stubs for %s */\n" % ty.rawname for name,args in fns: # For N args we return one value and take N-1 values as parameters s += "value %s(" % stub_fn_name(ty, name) s += ", ".join(["value v%d" % v for v in range(1,len(args))]) s += ");\n" return s def gen_c_default(ty): s = "/* Get the defaults for %s */\n" % ty.rawname # Handle KeyedUnions... union_types = [] for f in ty.fields: if isinstance(f.type, idl.KeyedUnion): union_types.append(f.type.keyvar) s += "value stub_libxl_%s_init(value ctx, %svalue unit)\n" % (ty.rawname, "".join(["value " + u.name + ", " for u in union_types])) s += "{\n" s += "\tCAMLparam%d(ctx, %sunit);\n" % (len(union_types) + 2, "".join([u.name + ", " for u in union_types])) s += "\tCAMLlocal1(val);\n" s += "\tlibxl_%s c_val;\n" % ty.rawname s += "\tlibxl_%s_init(&c_val);\n" % ty.rawname for u in union_types: s += "\tif (%s != Val_none) {\n" % u.name s += "\t\t%s c = 0;\n" % u.type.typename s += "\t\t%s_val(CTX, &c, Some_val(%s));\n" % (u.type.rawname, u.name) s += "\t\tlibxl_%s_init_%s(&c_val, c);\n" % (ty.rawname, u.name) s += "\t}\n" s += "\tval = Val_%s(&c_val);\n" % ty.rawname if ty.dispose_fn: s += "\tlibxl_%s_dispose(&c_val);\n" % ty.rawname s += "\tCAMLreturn(val);\n" s += "}\n" return s def gen_c_defaults(ty): s = gen_c_default(ty) return s def autogen_header(open_comment, close_comment): s = open_comment + " AUTO-GENERATED FILE DO NOT EDIT " + close_comment + "\n" s += open_comment + " autogenerated by \n" s += reduce(lambda x,y: x + " ", range(len(open_comment + " ")), "") s += "%s" % " ".join(sys.argv) s += "\n " + close_comment + "\n\n" return s if __name__ == '__main__': if len(sys.argv) < 4: print >>sys.stderr, "Usage: genwrap.py " sys.exit(1) (_,types) = idl.parse(sys.argv[1]) # Do not generate these yet. blacklist = [ "cpupoolinfo", "vcpuinfo", ] for t in blacklist: if t not in [ty.rawname for ty in types]: print "unknown type %s in blacklist" % t types = [ty for ty in types if not ty.rawname in blacklist] _ml = sys.argv[3] ml = open(_ml, 'w') ml.write(autogen_header("(*", "*)")) _mli = sys.argv[2] mli = open(_mli, 'w') mli.write(autogen_header("(*", "*)")) _cinc = sys.argv[4] cinc = open(_cinc, 'w') cinc.write(autogen_header("/*", "*/")) for ty in types: if ty.private: continue #sys.stdout.write(" TYPE %-20s " % ty.rawname) ml.write(gen_ocaml_ml(ty, False)) ml.write("\n") mli.write(gen_ocaml_ml(ty, True)) mli.write("\n") if ty.marshal_in(): cinc.write(gen_c_val(ty)) cinc.write("\n") cinc.write(gen_Val_ocaml(ty)) cinc.write("\n") if functions.has_key(ty.rawname): cinc.write(gen_c_stub_prototype(ty, functions[ty.rawname])) cinc.write("\n") if ty.init_fn is not None: cinc.write(gen_c_defaults(ty)) cinc.write("\n") #sys.stdout.write("\n") ml.write("(* END OF AUTO-GENERATED CODE *)\n") ml.close() mli.write("(* END OF AUTO-GENERATED CODE *)\n") mli.close() cinc.close() xen-4.9.2/tools/ocaml/libs/xl/xenlight_stubs.c0000664000175000017500000011140313256712137017472 0ustar smbsmb/* * Copyright (C) 2009-2011 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #define CAML_NAME_SPACE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "caml_xentoollog.h" /* * Starting with ocaml-3.09.3, CAMLreturn can only be used for ``value'' * types. CAMLreturnT was only added in 3.09.4, so we define our own * version here if needed. */ #ifndef CAMLreturnT #define CAMLreturnT(type, result) do { \ type caml__temp_result = (result); \ caml_local_roots = caml__frame; \ return (caml__temp_result); \ } while (0) #endif /* The following is equal to the CAMLreturn macro, but without the return */ #define CAMLdone do{ \ caml_local_roots = caml__frame; \ }while (0) #define Ctx_val(x)(*((libxl_ctx **) Data_custom_val(x))) #define CTX ((libxl_ctx *) Ctx_val(ctx)) static char * dup_String_val(value s) { int len; char *c; len = caml_string_length(s); c = calloc(len + 1, sizeof(char)); if (!c) caml_raise_out_of_memory(); memcpy(c, String_val(s), len); return c; } /* Forward reference: this is defined in the auto-generated include file below. */ static value Val_error (libxl_error error_c); static void failwith_xl(int error, char *fname) { CAMLparam0(); CAMLlocal1(arg); static value *exc = NULL; /* First time around, lookup by name */ if (!exc) exc = caml_named_value("Xenlight.Error"); if (!exc) caml_invalid_argument("Exception Xenlight.Error not initialized, please link xenlight.cma"); arg = caml_alloc(2, 0); Store_field(arg, 0, Val_error(error)); Store_field(arg, 1, caml_copy_string(fname)); caml_raise_with_arg(*exc, arg); CAMLreturn0; } CAMLprim value stub_raise_exception(value unit) { CAMLparam1(unit); failwith_xl(ERROR_FAIL, "test exception"); CAMLreturn(Val_unit); } void ctx_finalize(value ctx) { libxl_ctx_free(CTX); } static struct custom_operations libxl_ctx_custom_operations = { "libxl_ctx_custom_operations", ctx_finalize /* custom_finalize_default */, custom_compare_default, custom_hash_default, custom_serialize_default, custom_deserialize_default }; CAMLprim value stub_libxl_ctx_alloc(value logger) { CAMLparam1(logger); CAMLlocal1(handle); libxl_ctx *ctx; int ret; ret = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger *) Xtl_val(logger)); if (ret != 0) \ failwith_xl(ERROR_FAIL, "cannot init context"); handle = caml_alloc_custom(&libxl_ctx_custom_operations, sizeof(ctx), 0, 1); Ctx_val(handle) = ctx; CAMLreturn(handle); } static int list_len(value v) { int len = 0; while ( v != Val_emptylist ) { len++; v = Field(v, 1); } return len; } static int libxl_key_value_list_val(libxl_key_value_list *c_val, value v) { CAMLparam1(v); CAMLlocal1(elem); int nr, i; libxl_key_value_list array; nr = list_len(v); array = calloc((nr + 1) * 2, sizeof(char *)); if (!array) caml_raise_out_of_memory(); for (i=0; v != Val_emptylist; i++, v = Field(v, 1) ) { elem = Field(v, 0); array[i * 2] = dup_String_val(Field(elem, 0)); array[i * 2 + 1] = dup_String_val(Field(elem, 1)); } *c_val = array; CAMLreturn(0); } static value Val_key_value_list(libxl_key_value_list *c_val) { CAMLparam0(); CAMLlocal5(list, cons, key, val, kv); int i; list = Val_emptylist; for (i = libxl_string_list_length((libxl_string_list *) c_val) - 1; i >= 0; i -= 2) { val = caml_copy_string((*c_val)[i]); key = caml_copy_string((*c_val)[i - 1]); kv = caml_alloc_tuple(2); Store_field(kv, 0, key); Store_field(kv, 1, val); cons = caml_alloc(2, 0); Store_field(cons, 0, kv); // head Store_field(cons, 1, list); // tail list = cons; } CAMLreturn(list); } static int libxl_string_list_val(libxl_string_list *c_val, value v) { CAMLparam1(v); int nr, i; libxl_string_list array; nr = list_len(v); array = calloc(nr + 1, sizeof(char *)); if (!array) caml_raise_out_of_memory(); for (i=0; v != Val_emptylist; i++, v = Field(v, 1) ) array[i] = dup_String_val(Field(v, 0)); *c_val = array; CAMLreturn(0); } static value Val_string_list(libxl_string_list *c_val) { CAMLparam0(); CAMLlocal3(list, cons, string); int i; list = Val_emptylist; for (i = libxl_string_list_length(c_val) - 1; i >= 0; i--) { string = caml_copy_string((*c_val)[i]); cons = caml_alloc(2, 0); Store_field(cons, 0, string); // head Store_field(cons, 1, list); // tail list = cons; } CAMLreturn(list); } /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ #define Val_none Val_int(0) #define Some_val(v) Field(v,0) static value Val_some(value v) { CAMLparam1(v); CAMLlocal1(some); some = caml_alloc(1, 0); Store_field(some, 0, v); CAMLreturn(some); } static value Val_mac (libxl_mac *c_val) { CAMLparam0(); CAMLlocal1(v); int i; v = caml_alloc_tuple(6); for(i=0; i<6; i++) Store_field(v, i, Val_int((*c_val)[i])); CAMLreturn(v); } static int Mac_val(libxl_mac *c_val, value v) { CAMLparam1(v); int i; for(i=0; i<6; i++) (*c_val)[i] = Int_val(Field(v, i)); CAMLreturn(0); } static value Val_bitmap (libxl_bitmap *c_val) { CAMLparam0(); CAMLlocal1(v); int i; if (c_val->size == 0) v = Atom(0); else { v = caml_alloc(8 * (c_val->size), 0); libxl_for_each_bit(i, *c_val) { if (libxl_bitmap_test(c_val, i)) Store_field(v, i, Val_true); else Store_field(v, i, Val_false); } } CAMLreturn(v); } static int Bitmap_val(libxl_ctx *ctx, libxl_bitmap *c_val, value v) { CAMLparam1(v); int i, len = Wosize_val(v); c_val->size = 0; if (len > 0 && libxl_bitmap_alloc(ctx, c_val, len)) failwith_xl(ERROR_NOMEM, "cannot allocate bitmap"); for (i=0; ibytes[i])); CAMLreturn(v); } static int Ms_vm_genid_val(libxl_ms_vm_genid *c_val, value v) { CAMLparam1(v); int i; for(i=0; ibytes[i] = Int_val(Field(v, i)); CAMLreturn(0); } static value Val_string_option(const char *c_val) { CAMLparam0(); CAMLlocal2(tmp1, tmp2); if (c_val) { tmp1 = caml_copy_string(c_val); tmp2 = Val_some(tmp1); CAMLreturn(tmp2); } else CAMLreturn(Val_none); } static char *String_option_val(value v) { CAMLparam1(v); char *s = NULL; if (v != Val_none) s = dup_String_val(Some_val(v)); CAMLreturnT(char *, s); } #include "_libxl_types.inc" void async_callback(libxl_ctx *ctx, int rc, void *for_callback) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocal2(error, tmp); static value *func = NULL; value *p = (value *) for_callback; if (func == NULL) { /* First time around, lookup by name */ func = caml_named_value("libxl_async_callback"); } if (rc == 0) error = Val_none; else { tmp = Val_error(rc); error = Val_some(tmp); } /* for_callback is a pointer to a "value" that was malloc'ed and * registered with the OCaml GC. The value is handed back to OCaml * in the following callback, after which the pointer is unregistered * and freed. */ caml_callback2(*func, error, *p); caml_remove_global_root(p); free(p); CAMLdone; caml_enter_blocking_section(); } static libxl_asyncop_how *aohow_val(value async) { CAMLparam1(async); libxl_asyncop_how *ao_how = NULL; value *p; if (async != Val_none) { /* for_callback must be a pointer to a "value" that is malloc'ed and * registered with the OCaml GC. This ensures that the GC does not remove * the corresponding OCaml heap blocks, and allows the GC to update the value * when blocks are moved around, while libxl is free to copy the pointer if * it needs to. * The for_callback pointer must always be non-NULL. */ p = malloc(sizeof(value)); if (!p) failwith_xl(ERROR_NOMEM, "cannot allocate value"); *p = Some_val(async); caml_register_global_root(p); ao_how = malloc(sizeof(*ao_how)); ao_how->callback = async_callback; ao_how->u.for_callback = (void *) p; } CAMLreturnT(libxl_asyncop_how *, ao_how); } value stub_libxl_domain_create_new(value ctx, value domain_config, value async, value unit) { CAMLparam4(ctx, async, domain_config, unit); int ret; libxl_domain_config c_dconfig; uint32_t c_domid; libxl_asyncop_how *ao_how; libxl_domain_config_init(&c_dconfig); ret = domain_config_val(CTX, &c_dconfig, domain_config); if (ret != 0) { libxl_domain_config_dispose(&c_dconfig); failwith_xl(ret, "domain_create_new"); } ao_how = aohow_val(async); caml_enter_blocking_section(); ret = libxl_domain_create_new(CTX, &c_dconfig, &c_domid, ao_how, NULL); caml_leave_blocking_section(); free(ao_how); libxl_domain_config_dispose(&c_dconfig); if (ret != 0) failwith_xl(ret, "domain_create_new"); CAMLreturn(Val_int(c_domid)); } value stub_libxl_domain_create_restore(value ctx, value domain_config, value params, value async, value unit) { CAMLparam5(ctx, domain_config, params, async, unit); int ret; libxl_domain_config c_dconfig; libxl_domain_restore_params c_params; uint32_t c_domid; libxl_asyncop_how *ao_how; int restore_fd; libxl_domain_config_init(&c_dconfig); ret = domain_config_val(CTX, &c_dconfig, domain_config); if (ret != 0) { libxl_domain_config_dispose(&c_dconfig); failwith_xl(ret, "domain_create_restore"); } libxl_domain_restore_params_init(&c_params); ret = domain_restore_params_val(CTX, &c_params, Field(params, 1)); if (ret != 0) { libxl_domain_restore_params_dispose(&c_params); failwith_xl(ret, "domain_create_restore"); } ao_how = aohow_val(async); restore_fd = Int_val(Field(params, 0)); caml_enter_blocking_section(); ret = libxl_domain_create_restore(CTX, &c_dconfig, &c_domid, restore_fd, -1, &c_params, ao_how, NULL); caml_leave_blocking_section(); free(ao_how); libxl_domain_config_dispose(&c_dconfig); libxl_domain_restore_params_dispose(&c_params); if (ret != 0) failwith_xl(ret, "domain_create_restore"); CAMLreturn(Val_int(c_domid)); } value stub_libxl_domain_shutdown(value ctx, value domid) { CAMLparam2(ctx, domid); int ret; uint32_t c_domid = Int_val(domid); caml_enter_blocking_section(); ret = libxl_domain_shutdown(CTX, c_domid); caml_leave_blocking_section(); if (ret != 0) failwith_xl(ret, "domain_shutdown"); CAMLreturn(Val_unit); } value stub_libxl_domain_reboot(value ctx, value domid) { CAMLparam2(ctx, domid); int ret; uint32_t c_domid = Int_val(domid); caml_enter_blocking_section(); ret = libxl_domain_reboot(CTX, c_domid); caml_leave_blocking_section(); if (ret != 0) failwith_xl(ret, "domain_reboot"); CAMLreturn(Val_unit); } value stub_libxl_domain_destroy(value ctx, value domid, value async, value unit) { CAMLparam4(ctx, domid, async, unit); int ret; uint32_t c_domid = Int_val(domid); libxl_asyncop_how *ao_how = aohow_val(async); caml_enter_blocking_section(); ret = libxl_domain_destroy(CTX, c_domid, ao_how); caml_leave_blocking_section(); free(ao_how); if (ret != 0) failwith_xl(ret, "domain_destroy"); CAMLreturn(Val_unit); } value stub_libxl_domain_suspend(value ctx, value domid, value fd, value async, value unit) { CAMLparam5(ctx, domid, fd, async, unit); int ret; uint32_t c_domid = Int_val(domid); int c_fd = Int_val(fd); libxl_asyncop_how *ao_how = aohow_val(async); caml_enter_blocking_section(); ret = libxl_domain_suspend(CTX, c_domid, c_fd, 0, ao_how); caml_leave_blocking_section(); free(ao_how); if (ret != 0) failwith_xl(ret, "domain_suspend"); CAMLreturn(Val_unit); } value stub_libxl_domain_pause(value ctx, value domid) { CAMLparam2(ctx, domid); int ret; uint32_t c_domid = Int_val(domid); caml_enter_blocking_section(); ret = libxl_domain_pause(CTX, c_domid); caml_leave_blocking_section(); if (ret != 0) failwith_xl(ret, "domain_pause"); CAMLreturn(Val_unit); } value stub_libxl_domain_unpause(value ctx, value domid) { CAMLparam2(ctx, domid); int ret; uint32_t c_domid = Int_val(domid); caml_enter_blocking_section(); ret = libxl_domain_unpause(CTX, c_domid); caml_leave_blocking_section(); if (ret != 0) failwith_xl(ret, "domain_unpause"); CAMLreturn(Val_unit); } #define _STRINGIFY(x) #x #define STRINGIFY(x) _STRINGIFY(x) #define _DEVICE_ADDREMOVE(type,fn,op) \ value stub_xl_device_##type##_##op(value ctx, value info, value domid, \ value async, value unit) \ { \ CAMLparam5(ctx, info, domid, async, unit); \ libxl_device_##type c_info; \ int ret, marker_var; \ uint32_t c_domid = Int_val(domid); \ libxl_asyncop_how *ao_how = aohow_val(async); \ \ device_##type##_val(CTX, &c_info, info); \ \ caml_enter_blocking_section(); \ ret = libxl_##fn##_##op(CTX, c_domid, &c_info, ao_how); \ caml_leave_blocking_section(); \ \ free(ao_how); \ libxl_device_##type##_dispose(&c_info); \ \ if (ret != 0) \ failwith_xl(ret, STRINGIFY(type) "_" STRINGIFY(op)); \ \ CAMLreturn(Val_unit); \ } #define DEVICE_ADDREMOVE(type) \ _DEVICE_ADDREMOVE(type, device_##type, add) \ _DEVICE_ADDREMOVE(type, device_##type, remove) \ _DEVICE_ADDREMOVE(type, device_##type, destroy) DEVICE_ADDREMOVE(disk) DEVICE_ADDREMOVE(nic) DEVICE_ADDREMOVE(vfb) DEVICE_ADDREMOVE(vkb) DEVICE_ADDREMOVE(pci) _DEVICE_ADDREMOVE(disk, cdrom, insert) value stub_xl_device_nic_of_devid(value ctx, value domid, value devid) { CAMLparam3(ctx, domid, devid); CAMLlocal1(nic); libxl_device_nic c_nic; uint32_t c_domid = Int_val(domid); int c_devid = Int_val(devid); caml_enter_blocking_section(); libxl_devid_to_device_nic(CTX, c_domid, c_devid, &c_nic); caml_leave_blocking_section(); nic = Val_device_nic(&c_nic); libxl_device_nic_dispose(&c_nic); CAMLreturn(nic); } value stub_xl_device_nic_list(value ctx, value domid) { CAMLparam2(ctx, domid); CAMLlocal2(list, temp); libxl_device_nic *c_list; int i, nb; uint32_t c_domid = Int_val(domid); caml_enter_blocking_section(); c_list = libxl_device_nic_list(CTX, c_domid, &nb); caml_leave_blocking_section(); if (!c_list) failwith_xl(ERROR_FAIL, "nic_list"); list = temp = Val_emptylist; for (i = 0; i < nb; i++) { list = caml_alloc_small(2, Tag_cons); Field(list, 0) = Val_int(0); Field(list, 1) = temp; temp = list; Store_field(list, 0, Val_device_nic(&c_list[i])); libxl_device_nic_dispose(&c_list[i]); } free(c_list); CAMLreturn(list); } value stub_xl_device_disk_list(value ctx, value domid) { CAMLparam2(ctx, domid); CAMLlocal2(list, temp); libxl_device_disk *c_list; int i, nb; uint32_t c_domid = Int_val(domid); caml_enter_blocking_section(); c_list = libxl_device_disk_list(CTX, c_domid, &nb); caml_leave_blocking_section(); if (!c_list) failwith_xl(ERROR_FAIL, "disk_list"); list = temp = Val_emptylist; for (i = 0; i < nb; i++) { list = caml_alloc_small(2, Tag_cons); Field(list, 0) = Val_int(0); Field(list, 1) = temp; temp = list; Store_field(list, 0, Val_device_disk(&c_list[i])); libxl_device_disk_dispose(&c_list[i]); } free(c_list); CAMLreturn(list); } value stub_xl_device_disk_of_vdev(value ctx, value domid, value vdev) { CAMLparam3(ctx, domid, vdev); CAMLlocal1(disk); libxl_device_disk c_disk; char *c_vdev; uint32_t c_domid = Int_val(domid); c_vdev = strdup(String_val(vdev)); caml_enter_blocking_section(); libxl_vdev_to_device_disk(CTX, c_domid, c_vdev, &c_disk); caml_leave_blocking_section(); disk = Val_device_disk(&c_disk); libxl_device_disk_dispose(&c_disk); free(c_vdev); CAMLreturn(disk); } value stub_xl_device_pci_list(value ctx, value domid) { CAMLparam2(ctx, domid); CAMLlocal2(list, temp); libxl_device_pci *c_list; int i, nb; uint32_t c_domid = Int_val(domid); caml_enter_blocking_section(); c_list = libxl_device_pci_list(CTX, c_domid, &nb); caml_leave_blocking_section(); if (!c_list) failwith_xl(ERROR_FAIL, "pci_list"); list = temp = Val_emptylist; for (i = 0; i < nb; i++) { list = caml_alloc_small(2, Tag_cons); Field(list, 0) = Val_int(0); Field(list, 1) = temp; temp = list; Store_field(list, 0, Val_device_pci(&c_list[i])); libxl_device_pci_dispose(&c_list[i]); } free(c_list); CAMLreturn(list); } value stub_xl_device_pci_assignable_add(value ctx, value info, value rebind) { CAMLparam3(ctx, info, rebind); libxl_device_pci c_info; int ret, marker_var; int c_rebind = (int) Bool_val(rebind); device_pci_val(CTX, &c_info, info); caml_enter_blocking_section(); ret = libxl_device_pci_assignable_add(CTX, &c_info, c_rebind); caml_leave_blocking_section(); libxl_device_pci_dispose(&c_info); if (ret != 0) failwith_xl(ret, "pci_assignable_add"); CAMLreturn(Val_unit); } value stub_xl_device_pci_assignable_remove(value ctx, value info, value rebind) { CAMLparam3(ctx, info, rebind); libxl_device_pci c_info; int ret, marker_var; int c_rebind = (int) Bool_val(rebind); device_pci_val(CTX, &c_info, info); caml_enter_blocking_section(); ret = libxl_device_pci_assignable_remove(CTX, &c_info, c_rebind); caml_leave_blocking_section(); libxl_device_pci_dispose(&c_info); if (ret != 0) failwith_xl(ret, "pci_assignable_remove"); CAMLreturn(Val_unit); } value stub_xl_device_pci_assignable_list(value ctx) { CAMLparam1(ctx); CAMLlocal2(list, temp); libxl_device_pci *c_list; int i, nb; uint32_t c_domid; caml_enter_blocking_section(); c_list = libxl_device_pci_assignable_list(CTX, &nb); caml_leave_blocking_section(); if (!c_list) failwith_xl(ERROR_FAIL, "pci_assignable_list"); list = temp = Val_emptylist; for (i = 0; i < nb; i++) { list = caml_alloc_small(2, Tag_cons); Field(list, 0) = Val_int(0); Field(list, 1) = temp; temp = list; Store_field(list, 0, Val_device_pci(&c_list[i])); libxl_device_pci_dispose(&c_list[i]); } free(c_list); CAMLreturn(list); } value stub_xl_physinfo_get(value ctx) { CAMLparam1(ctx); CAMLlocal1(physinfo); libxl_physinfo c_physinfo; int ret; caml_enter_blocking_section(); ret = libxl_get_physinfo(CTX, &c_physinfo); caml_leave_blocking_section(); if (ret != 0) failwith_xl(ret, "get_physinfo"); physinfo = Val_physinfo(&c_physinfo); libxl_physinfo_dispose(&c_physinfo); CAMLreturn(physinfo); } value stub_xl_cputopology_get(value ctx) { CAMLparam1(ctx); CAMLlocal3(topology, v, v0); libxl_cputopology *c_topology; int i, nr; caml_enter_blocking_section(); c_topology = libxl_get_cpu_topology(CTX, &nr); caml_leave_blocking_section(); if (!c_topology) failwith_xl(ERROR_FAIL, "get_cpu_topologyinfo"); topology = caml_alloc_tuple(nr); for (i = 0; i < nr; i++) { if (c_topology[i].core != LIBXL_CPUTOPOLOGY_INVALID_ENTRY) { v0 = Val_cputopology(&c_topology[i]); v = Val_some(v0); } else v = Val_none; Store_field(topology, i, v); } libxl_cputopology_list_free(c_topology, nr); CAMLreturn(topology); } value stub_xl_dominfo_list(value ctx) { CAMLparam1(ctx); CAMLlocal2(domlist, temp); libxl_dominfo *c_domlist; int i, nb; caml_enter_blocking_section(); c_domlist = libxl_list_domain(CTX, &nb); caml_leave_blocking_section(); if (!c_domlist) failwith_xl(ERROR_FAIL, "dominfo_list"); domlist = temp = Val_emptylist; for (i = nb - 1; i >= 0; i--) { domlist = caml_alloc_small(2, Tag_cons); Field(domlist, 0) = Val_int(0); Field(domlist, 1) = temp; temp = domlist; Store_field(domlist, 0, Val_dominfo(&c_domlist[i])); } libxl_dominfo_list_free(c_domlist, nb); CAMLreturn(domlist); } value stub_xl_dominfo_get(value ctx, value domid) { CAMLparam2(ctx, domid); CAMLlocal1(dominfo); libxl_dominfo c_dominfo; int ret; uint32_t c_domid = Int_val(domid); caml_enter_blocking_section(); ret = libxl_domain_info(CTX, &c_dominfo, c_domid); caml_leave_blocking_section(); if (ret != 0) failwith_xl(ERROR_FAIL, "domain_info"); dominfo = Val_dominfo(&c_dominfo); CAMLreturn(dominfo); } value stub_xl_domain_sched_params_get(value ctx, value domid) { CAMLparam2(ctx, domid); CAMLlocal1(scinfo); libxl_domain_sched_params c_scinfo; int ret; uint32_t c_domid = Int_val(domid); caml_enter_blocking_section(); ret = libxl_domain_sched_params_get(CTX, c_domid, &c_scinfo); caml_leave_blocking_section(); if (ret != 0) failwith_xl(ret, "domain_sched_params_get"); scinfo = Val_domain_sched_params(&c_scinfo); libxl_domain_sched_params_dispose(&c_scinfo); CAMLreturn(scinfo); } value stub_xl_domain_sched_params_set(value ctx, value domid, value scinfo) { CAMLparam3(ctx, domid, scinfo); libxl_domain_sched_params c_scinfo; int ret; uint32_t c_domid = Int_val(domid); domain_sched_params_val(CTX, &c_scinfo, scinfo); caml_enter_blocking_section(); ret = libxl_domain_sched_params_set(CTX, c_domid, &c_scinfo); caml_leave_blocking_section(); libxl_domain_sched_params_dispose(&c_scinfo); if (ret != 0) failwith_xl(ret, "domain_sched_params_set"); CAMLreturn(Val_unit); } value stub_xl_send_trigger(value ctx, value domid, value trigger, value vcpuid) { CAMLparam4(ctx, domid, trigger, vcpuid); int ret; uint32_t c_domid = Int_val(domid); libxl_trigger c_trigger = LIBXL_TRIGGER_UNKNOWN; int c_vcpuid = Int_val(vcpuid); trigger_val(CTX, &c_trigger, trigger); caml_enter_blocking_section(); ret = libxl_send_trigger(CTX, c_domid, c_trigger, c_vcpuid); caml_leave_blocking_section(); if (ret != 0) failwith_xl(ret, "send_trigger"); CAMLreturn(Val_unit); } value stub_xl_send_sysrq(value ctx, value domid, value sysrq) { CAMLparam3(ctx, domid, sysrq); int ret; uint32_t c_domid = Int_val(domid); int c_sysrq = Int_val(sysrq); caml_enter_blocking_section(); ret = libxl_send_sysrq(CTX, c_domid, c_sysrq); caml_leave_blocking_section(); if (ret != 0) failwith_xl(ret, "send_sysrq"); CAMLreturn(Val_unit); } value stub_xl_send_debug_keys(value ctx, value keys) { CAMLparam2(ctx, keys); int ret; char *c_keys; c_keys = dup_String_val(keys); caml_enter_blocking_section(); ret = libxl_send_debug_keys(CTX, c_keys); caml_leave_blocking_section(); free(c_keys); if (ret != 0) failwith_xl(ret, "send_debug_keys"); CAMLreturn(Val_unit); } static struct custom_operations libxl_console_reader_custom_operations = { "libxl_console_reader_custom_operations", custom_finalize_default, custom_compare_default, custom_hash_default, custom_serialize_default, custom_deserialize_default }; #define Console_reader_val(x)(*((libxl_xen_console_reader **) Data_custom_val(x))) value stub_libxl_xen_console_read_start(value ctx, value clear) { CAMLparam2(ctx, clear); CAMLlocal1(handle); int c_clear = Int_val(clear); libxl_xen_console_reader *cr; caml_enter_blocking_section(); cr = libxl_xen_console_read_start(CTX, c_clear); caml_leave_blocking_section(); handle = caml_alloc_custom(&libxl_console_reader_custom_operations, sizeof(cr), 0, 1); Console_reader_val(handle) = cr; CAMLreturn(handle); } static void raise_eof(void) { static value *exc = NULL; /* First time around, lookup by name */ if (!exc) exc = caml_named_value("Xenlight.Host.End_of_file"); if (!exc) caml_invalid_argument("Exception Xenlight.Host.End_of_file not initialized, please link xenlight.cma"); caml_raise_constant(*exc); } value stub_libxl_xen_console_read_line(value ctx, value reader) { CAMLparam2(ctx, reader); CAMLlocal1(line); int ret; char *c_line; libxl_xen_console_reader *cr = (libxl_xen_console_reader *) Console_reader_val(reader); caml_enter_blocking_section(); ret = libxl_xen_console_read_line(CTX, cr, &c_line); caml_leave_blocking_section(); if (ret < 0) failwith_xl(ret, "xen_console_read_line"); if (ret == 0) raise_eof(); line = caml_copy_string(c_line); CAMLreturn(line); } value stub_libxl_xen_console_read_finish(value ctx, value reader) { CAMLparam2(ctx, reader); libxl_xen_console_reader *cr = (libxl_xen_console_reader *) Console_reader_val(reader); caml_enter_blocking_section(); libxl_xen_console_read_finish(CTX, cr); caml_leave_blocking_section(); CAMLreturn(Val_unit); } /* Event handling */ short Poll_val(value event) { CAMLparam1(event); short res = -1; switch (Int_val(event)) { case 0: res = POLLIN; break; case 1: res = POLLPRI; break; case 2: res = POLLOUT; break; case 3: res = POLLERR; break; case 4: res = POLLHUP; break; case 5: res = POLLNVAL; break; } CAMLreturn(res); } short Poll_events_val(value event_list) { CAMLparam1(event_list); short events = 0; while (event_list != Val_emptylist) { events |= Poll_val(Field(event_list, 0)); event_list = Field(event_list, 1); } CAMLreturn(events); } value Val_poll(short event) { CAMLparam0(); CAMLlocal1(res); switch (event) { case POLLIN: res = Val_int(0); break; case POLLPRI: res = Val_int(1); break; case POLLOUT: res = Val_int(2); break; case POLLERR: res = Val_int(3); break; case POLLHUP: res = Val_int(4); break; case POLLNVAL: res = Val_int(5); break; default: failwith_xl(ERROR_FAIL, "cannot convert poll event value"); break; } CAMLreturn(res); } value add_event(value event_list, short event) { CAMLparam1(event_list); CAMLlocal1(new_list); new_list = caml_alloc(2, 0); Store_field(new_list, 0, Val_poll(event)); Store_field(new_list, 1, event_list); CAMLreturn(new_list); } value Val_poll_events(short events) { CAMLparam0(); CAMLlocal1(event_list); event_list = Val_emptylist; if (events & POLLIN) event_list = add_event(event_list, POLLIN); if (events & POLLPRI) event_list = add_event(event_list, POLLPRI); if (events & POLLOUT) event_list = add_event(event_list, POLLOUT); if (events & POLLERR) event_list = add_event(event_list, POLLERR); if (events & POLLHUP) event_list = add_event(event_list, POLLHUP); if (events & POLLNVAL) event_list = add_event(event_list, POLLNVAL); CAMLreturn(event_list); } /* The process for dealing with the for_app_registration_ values in the * callbacks below (GC registrations etc) is similar to the way for_callback is * handled in the asynchronous operations above. */ int fd_register(void *user, int fd, void **for_app_registration_out, short events, void *for_libxl) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocalN(args, 4); int ret = 0; static value *func = NULL; value *p = (value *) user; value *for_app; if (func == NULL) { /* First time around, lookup by name */ func = caml_named_value("libxl_fd_register"); } args[0] = *p; args[1] = Val_int(fd); args[2] = Val_poll_events(events); args[3] = (value) for_libxl; for_app = malloc(sizeof(value)); if (!for_app) { ret = ERROR_OSEVENT_REG_FAIL; goto err; } *for_app = caml_callbackN_exn(*func, 4, args); if (Is_exception_result(*for_app)) { ret = ERROR_OSEVENT_REG_FAIL; free(for_app); goto err; } caml_register_global_root(for_app); *for_app_registration_out = for_app; err: CAMLdone; caml_enter_blocking_section(); return ret; } int fd_modify(void *user, int fd, void **for_app_registration_update, short events) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocalN(args, 4); int ret = 0; static value *func = NULL; value *p = (value *) user; value *for_app = *for_app_registration_update; /* If for_app == NULL, then something is very wrong */ assert(for_app); if (func == NULL) { /* First time around, lookup by name */ func = caml_named_value("libxl_fd_modify"); } args[0] = *p; args[1] = Val_int(fd); args[2] = *for_app; args[3] = Val_poll_events(events); *for_app = caml_callbackN_exn(*func, 4, args); if (Is_exception_result(*for_app)) { /* If an exception is caught, *for_app_registration_update is not * changed. It remains a valid pointer to a value that is registered * with the GC. */ ret = ERROR_OSEVENT_REG_FAIL; goto err; } *for_app_registration_update = for_app; err: CAMLdone; caml_enter_blocking_section(); return ret; } void fd_deregister(void *user, int fd, void *for_app_registration) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocalN(args, 3); static value *func = NULL; value *p = (value *) user; value *for_app = for_app_registration; /* If for_app == NULL, then something is very wrong */ assert(for_app); if (func == NULL) { /* First time around, lookup by name */ func = caml_named_value("libxl_fd_deregister"); } args[0] = *p; args[1] = Val_int(fd); args[2] = *for_app; caml_callbackN_exn(*func, 3, args); /* This hook does not return error codes, so the best thing we can do * to avoid trouble, if we catch an exception from the app, is abort. */ if (Is_exception_result(*for_app)) abort(); caml_remove_global_root(for_app); free(for_app); CAMLdone; caml_enter_blocking_section(); } struct timeout_handles { void *for_libxl; value for_app; }; int timeout_register(void *user, void **for_app_registration_out, struct timeval abs, void *for_libxl) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocal2(sec, usec); CAMLlocalN(args, 4); int ret = 0; static value *func = NULL; value *p = (value *) user; struct timeout_handles *handles; if (func == NULL) { /* First time around, lookup by name */ func = caml_named_value("libxl_timeout_register"); } sec = caml_copy_int64(abs.tv_sec); usec = caml_copy_int64(abs.tv_usec); /* This struct of "handles" will contain "for_libxl" as well as "for_app". * We'll give a pointer to the struct to the app, and get it back in * occurred_timeout, where we can clean it all up. */ handles = malloc(sizeof(*handles)); if (!handles) { ret = ERROR_OSEVENT_REG_FAIL; goto err; } handles->for_libxl = for_libxl; args[0] = *p; args[1] = sec; args[2] = usec; args[3] = (value) handles; handles->for_app = caml_callbackN_exn(*func, 4, args); if (Is_exception_result(handles->for_app)) { ret = ERROR_OSEVENT_REG_FAIL; free(handles); goto err; } caml_register_global_root(&handles->for_app); *for_app_registration_out = handles; err: CAMLdone; caml_enter_blocking_section(); return ret; } int timeout_modify(void *user, void **for_app_registration_update, struct timeval abs) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocal1(for_app_update); CAMLlocalN(args, 2); int ret = 0; static value *func = NULL; value *p = (value *) user; struct timeout_handles *handles = *for_app_registration_update; /* If for_app == NULL, then something is very wrong */ assert(handles->for_app); /* Libxl currently promises that timeout_modify is only ever called with * abs={0,0}, meaning "right away". We cannot deal with other values. */ assert(abs.tv_sec == 0 && abs.tv_usec == 0); if (func == NULL) { /* First time around, lookup by name */ func = caml_named_value("libxl_timeout_fire_now"); } args[0] = *p; args[1] = handles->for_app; for_app_update = caml_callbackN_exn(*func, 2, args); if (Is_exception_result(for_app_update)) { /* If an exception is caught, *for_app_registration_update is not * changed. It remains a valid pointer to a value that is registered * with the GC. */ ret = ERROR_OSEVENT_REG_FAIL; goto err; } handles->for_app = for_app_update; err: CAMLdone; caml_enter_blocking_section(); return ret; } void timeout_deregister(void *user, void *for_app_registration) { /* This hook will never be called by libxl. */ abort(); } value stub_libxl_osevent_register_hooks(value ctx, value user) { CAMLparam2(ctx, user); CAMLlocal1(result); libxl_osevent_hooks *hooks; value *p; hooks = malloc(sizeof(*hooks)); if (!hooks) failwith_xl(ERROR_NOMEM, "cannot allocate osevent hooks"); hooks->fd_register = fd_register; hooks->fd_modify = fd_modify; hooks->fd_deregister = fd_deregister; hooks->timeout_register = timeout_register; hooks->timeout_modify = timeout_modify; hooks->timeout_deregister = timeout_deregister; p = malloc(sizeof(value)); if (!p) failwith_xl(ERROR_NOMEM, "cannot allocate value"); *p = user; caml_register_global_root(p); caml_enter_blocking_section(); libxl_osevent_register_hooks(CTX, hooks, (void *) p); caml_leave_blocking_section(); result = caml_alloc(1, Abstract_tag); *((libxl_osevent_hooks **) result) = hooks; CAMLreturn(result); } value stub_libxl_osevent_occurred_fd(value ctx, value for_libxl, value fd, value events, value revents) { CAMLparam5(ctx, for_libxl, fd, events, revents); int c_fd = Int_val(fd); short c_events = Poll_events_val(events); short c_revents = Poll_events_val(revents); caml_enter_blocking_section(); libxl_osevent_occurred_fd(CTX, (void *) for_libxl, c_fd, c_events, c_revents); caml_leave_blocking_section(); CAMLreturn(Val_unit); } value stub_libxl_osevent_occurred_timeout(value ctx, value handles) { CAMLparam1(ctx); struct timeout_handles *c_handles = (struct timeout_handles *) handles; caml_enter_blocking_section(); libxl_osevent_occurred_timeout(CTX, (void *) c_handles->for_libxl); caml_leave_blocking_section(); caml_remove_global_root(&c_handles->for_app); free(c_handles); CAMLreturn(Val_unit); } struct user_with_ctx { libxl_ctx *ctx; value user; }; void event_occurs(void *user, libxl_event *event) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocalN(args, 2); struct user_with_ctx *c_user = (struct user_with_ctx *) user; static value *func = NULL; if (func == NULL) { /* First time around, lookup by name */ func = caml_named_value("libxl_event_occurs_callback"); } args[0] = c_user->user; args[1] = Val_event(event); libxl_event_free(c_user->ctx, event); caml_callbackN(*func, 2, args); CAMLdone; caml_enter_blocking_section(); } void disaster(void *user, libxl_event_type type, const char *msg, int errnoval) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocalN(args, 4); struct user_with_ctx *c_user = (struct user_with_ctx *) user; static value *func = NULL; if (func == NULL) { /* First time around, lookup by name */ func = caml_named_value("libxl_event_disaster_callback"); } args[0] = c_user->user; args[1] = Val_event_type(type); args[2] = caml_copy_string(msg); args[3] = Val_int(errnoval); caml_callbackN(*func, 4, args); CAMLdone; caml_enter_blocking_section(); } value stub_libxl_event_register_callbacks(value ctx, value user) { CAMLparam2(ctx, user); CAMLlocal1(result); struct user_with_ctx *c_user = NULL; libxl_event_hooks *hooks; c_user = malloc(sizeof(*c_user)); if (!c_user) failwith_xl(ERROR_NOMEM, "cannot allocate user value"); c_user->user = user; c_user->ctx = CTX; caml_register_global_root(&c_user->user); hooks = malloc(sizeof(*hooks)); if (!hooks) failwith_xl(ERROR_NOMEM, "cannot allocate event hooks"); hooks->event_occurs_mask = LIBXL_EVENTMASK_ALL; hooks->event_occurs = event_occurs; hooks->disaster = disaster; caml_enter_blocking_section(); libxl_event_register_callbacks(CTX, hooks, (void *) c_user); caml_leave_blocking_section(); result = caml_alloc(1, Abstract_tag); *((libxl_event_hooks **) result) = hooks; CAMLreturn(result); } value stub_libxl_evenable_domain_death(value ctx, value domid, value user) { CAMLparam3(ctx, domid, user); uint32_t c_domid = Int_val(domid); int c_user = Int_val(user); libxl_evgen_domain_death *evgen_out; caml_enter_blocking_section(); libxl_evenable_domain_death(CTX, c_domid, c_user, &evgen_out); caml_leave_blocking_section(); CAMLreturn(Val_unit); } /* * Local variables: * indent-tabs-mode: t * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/ocaml/libs/eventchn/0000775000175000017500000000000013256712137015453 5ustar smbsmbxen-4.9.2/tools/ocaml/libs/eventchn/xeneventchn.ml0000664000175000017500000000236613256712137020341 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) type handle external init: unit -> handle = "stub_eventchn_init" external fd: handle -> Unix.file_descr = "stub_eventchn_fd" type t = int external notify: handle -> int -> unit = "stub_eventchn_notify" external bind_interdomain: handle -> int -> int -> int = "stub_eventchn_bind_interdomain" external bind_dom_exc_virq: handle -> int = "stub_eventchn_bind_dom_exc_virq" external unbind: handle -> int -> unit = "stub_eventchn_unbind" external pending: handle -> int = "stub_eventchn_pending" external unmask: handle -> int -> unit = "stub_eventchn_unmask" let to_int x = x let of_int x = x xen-4.9.2/tools/ocaml/libs/eventchn/xeneventchn.mli0000664000175000017500000000406413256712137020507 0ustar smbsmb(* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) (** Event channel bindings: see tools/libxc/include/xenctrl.h *) type handle (** An initialised event channel interface. *) type t (** A local event channel. *) val to_int: t -> int val of_int: int -> t val init: unit -> handle (** Return an initialised event channel interface. On error it will throw a Failure exception. *) val fd: handle -> Unix.file_descr (** Return a file descriptor suitable for Unix.select. When the descriptor becomes readable, it is safe to call 'pending'. On error it will throw a Failure exception. *) val notify : handle -> t -> unit (** Notify the given event channel. On error it will throw a Failure exception. *) val bind_interdomain : handle -> int -> int -> t (** [bind_interdomain h domid remote_port] returns a local event channel connected to domid:remote_port. On error it will throw a Failure exception. *) val bind_dom_exc_virq : handle -> t (** Binds a local event channel to the VIRQ_DOM_EXC (domain exception VIRQ). On error it will throw a Failure exception. *) val unbind : handle -> t -> unit (** Unbinds the given event channel. On error it will throw a Failure exception. *) val pending : handle -> t (** Returns the next event channel to become pending. On error it will throw a Failure exception. *) val unmask : handle -> t -> unit (** Unmasks the given event channel. On error it will throw a Failure exception. *) xen-4.9.2/tools/ocaml/libs/eventchn/Makefile0000664000175000017500000000144613256712137017120 0ustar smbsmbTOPLEVEL=$(CURDIR)/../.. XEN_ROOT=$(TOPLEVEL)/../.. include $(TOPLEVEL)/common.make CFLAGS += $(CFLAGS_libxenevtchn) $(CFLAGS_xeninclude) OBJS = xeneventchn INTF = $(foreach obj, $(OBJS),$(obj).cmi) LIBS = xeneventchn.cma xeneventchn.cmxa LIBS_xeneventchn = $(LDLIBS_libxenevtchn) all: $(INTF) $(LIBS) $(PROGRAMS) bins: $(PROGRAMS) libs: $(LIBS) xeneventchn_OBJS = $(OBJS) xeneventchn_C_OBJS = xeneventchn_stubs OCAML_LIBRARY = xeneventchn .PHONY: install install: $(LIBS) META mkdir -p $(OCAMLDESTDIR) $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xeneventchn $(OCAMLFIND) install -destdir $(OCAMLDESTDIR) -ldconf ignore xeneventchn META $(INTF) $(LIBS) *.a *.so *.cmx .PHONY: uninstall uninstall: $(OCAMLFIND) remove -destdir $(OCAMLDESTDIR) xeneventchn include $(TOPLEVEL)/Makefile.rules xen-4.9.2/tools/ocaml/libs/eventchn/META.in0000664000175000017500000000023413256712137016530 0ustar smbsmbversion = "@VERSION@" description = "Eventchn interface extension" requires = "unix" archive(byte) = "xeneventchn.cma" archive(native) = "xeneventchn.cmxa" xen-4.9.2/tools/ocaml/libs/eventchn/xeneventchn_stubs.c0000664000175000017500000000623613256712137021373 0ustar smbsmb/* * Copyright (C) 2006-2007 XenSource Ltd. * Copyright (C) 2008 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #define CAML_NAME_SPACE #include #include #include #include #include #include #define _H(__h) ((xenevtchn_handle *)(__h)) CAMLprim value stub_eventchn_init(void) { CAMLparam0(); CAMLlocal1(result); xenevtchn_handle *xce = xenevtchn_open(NULL, 0); if (xce == NULL) caml_failwith("open failed"); result = (value)xce; CAMLreturn(result); } CAMLprim value stub_eventchn_fd(value xce) { CAMLparam1(xce); CAMLlocal1(result); int fd; fd = xenevtchn_fd(_H(xce)); if (fd == -1) caml_failwith("evtchn fd failed"); result = Val_int(fd); CAMLreturn(result); } CAMLprim value stub_eventchn_notify(value xce, value port) { CAMLparam2(xce, port); int rc; rc = xenevtchn_notify(_H(xce), Int_val(port)); if (rc == -1) caml_failwith("evtchn notify failed"); CAMLreturn(Val_unit); } CAMLprim value stub_eventchn_bind_interdomain(value xce, value domid, value remote_port) { CAMLparam3(xce, domid, remote_port); CAMLlocal1(port); xenevtchn_port_or_error_t rc; rc = xenevtchn_bind_interdomain(_H(xce), Int_val(domid), Int_val(remote_port)); if (rc == -1) caml_failwith("evtchn bind_interdomain failed"); port = Val_int(rc); CAMLreturn(port); } CAMLprim value stub_eventchn_bind_dom_exc_virq(value xce) { CAMLparam1(xce); CAMLlocal1(port); xenevtchn_port_or_error_t rc; rc = xenevtchn_bind_virq(_H(xce), VIRQ_DOM_EXC); if (rc == -1) caml_failwith("evtchn bind_dom_exc_virq failed"); port = Val_int(rc); CAMLreturn(port); } CAMLprim value stub_eventchn_unbind(value xce, value port) { CAMLparam2(xce, port); int rc; rc = xenevtchn_unbind(_H(xce), Int_val(port)); if (rc == -1) caml_failwith("evtchn unbind failed"); CAMLreturn(Val_unit); } CAMLprim value stub_eventchn_pending(value xce) { CAMLparam1(xce); CAMLlocal1(result); xenevtchn_port_or_error_t port; port = xenevtchn_pending(_H(xce)); if (port == -1) caml_failwith("evtchn pending failed"); result = Val_int(port); CAMLreturn(result); } CAMLprim value stub_eventchn_unmask(value xce, value _port) { CAMLparam2(xce, _port); evtchn_port_t port; port = Int_val(_port); if (xenevtchn_unmask(_H(xce), port)) caml_failwith("evtchn unmask failed"); CAMLreturn(Val_unit); } xen-4.9.2/tools/ocaml/libs/xentoollog/0000775000175000017500000000000013256712137016033 5ustar smbsmbxen-4.9.2/tools/ocaml/libs/xentoollog/caml_xentoollog.h0000664000175000017500000000146313256712137021376 0ustar smbsmb/* * Copyright (C) 2013 Citrix Ltd. * Author Ian Campbell * Author Rob Hoes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ struct caml_xtl { xentoollog_logger vtable; char *vmessage_cb; char *progress_cb; }; #define Xtl_val(x)(*((struct caml_xtl **) Data_custom_val(x))) xen-4.9.2/tools/ocaml/libs/xentoollog/Makefile0000664000175000017500000000345613256712137017503 0ustar smbsmbTOPLEVEL=$(CURDIR)/../.. XEN_ROOT=$(TOPLEVEL)/../.. include $(TOPLEVEL)/common.make # allow mixed declarations and code CFLAGS += -Wno-declaration-after-statement CFLAGS += $(CFLAGS_libxentoollog) CFLAGS += $(APPEND_CFLAGS) OCAMLINCLUDE += OBJS = xentoollog INTF = xentoollog.cmi LIBS = xentoollog.cma xentoollog.cmxa LIBS_xentoollog = $(LDLIBS_libxentoollog) xentoollog_OBJS = $(OBJS) xentoollog_C_OBJS = xentoollog_stubs OCAML_LIBRARY = xentoollog GENERATED_FILES += xentoollog.ml xentoollog.ml.tmp xentoollog.mli xentoollog.mli.tmp GENERATED_FILES += _xtl_levels.mli.in _xtl_levels.ml.in _xtl_levels.inc META all: $(INTF) $(LIBS) xentoollog.ml: xentoollog.ml.in _xtl_levels.ml.in $(Q)sed -e '1i\ (*\ * AUTO-GENERATED FILE DO NOT EDIT\ * Generated from xentoollog.ml.in and _xtl_levels.ml.in\ *)\ ' \ -e '/^(\* @@XTL_LEVELS@@ \*)$$/r_xtl_levels.ml.in' \ < xentoollog.ml.in > xentoollog.ml.tmp $(Q)mv xentoollog.ml.tmp xentoollog.ml xentoollog.mli: xentoollog.mli.in _xtl_levels.mli.in $(Q)sed -e '1i\ (*\ * AUTO-GENERATED FILE DO NOT EDIT\ * Generated from xentoollog.mli.in and _xtl_levels.mli.in\ *)\ ' \ -e '/^(\* @@XTL_LEVELS@@ \*)$$/r_xtl_levels.mli.in' \ < xentoollog.mli.in > xentoollog.mli.tmp $(Q)mv xentoollog.mli.tmp xentoollog.mli libs: $(LIBS) _xtl_levels.ml.in _xtl_levels.mli.in _xtl_levels.inc: genlevels.py $(XEN_ROOT)/tools/libs/toollog/include/xentoollog.h $(PYTHON) genlevels.py _xtl_levels.mli.in _xtl_levels.ml.in _xtl_levels.inc .PHONY: install install: $(LIBS) META mkdir -p $(OCAMLDESTDIR) ocamlfind remove -destdir $(OCAMLDESTDIR) xentoollog ocamlfind install -destdir $(OCAMLDESTDIR) -ldconf ignore xentoollog META $(INTF) $(LIBS) *.a *.so *.cmx .PHONY: uninstall uninstall: ocamlfind remove -destdir $(OCAMLDESTDIR) xentoollog include $(TOPLEVEL)/Makefile.rules xen-4.9.2/tools/ocaml/libs/xentoollog/META.in0000664000175000017500000000020613256712137017107 0ustar smbsmbversion = "@VERSION@" description = "Xen Tools Logger Interface" archive(byte) = "xentoollog.cma" archive(native) = "xentoollog.cmxa" xen-4.9.2/tools/ocaml/libs/xentoollog/genlevels.py0000775000175000017500000000514113256712137020375 0ustar smbsmb#!/usr/bin/python import sys def read_levels(): f = open('../../../libs/toollog/include/xentoollog.h', 'r') levels = [] record = False for l in f.readlines(): if 'XTL_NUM_LEVELS' in l: break if record == True: levels.append(l.split(',')[0].strip()) if 'XTL_NONE' in l: record = True f.close() olevels = [level[4:].capitalize() for level in levels] return levels, olevels # .ml def gen_ml(olevels): s = "" s += "type level = \n" for level in olevels: s += '\t| %s\n' % level s += "\nlet level_to_string level =\n" s += "\tmatch level with\n" for level in olevels: s += '\t| %s -> "%s"\n' % (level, level) s += "\nlet level_to_prio level =\n" s += "\tmatch level with\n" for index,level in enumerate(olevels): s += '\t| %s -> %d\n' % (level, index) return s # .mli def gen_mli(olevels): s = "" s += "type level = \n" for level in olevels: s += '\t| %s\n' % level return s # .c def gen_c(level): s = "" s += "static value Val_level(xentoollog_level c_level)\n" s += "{\n" s += "\tswitch (c_level) {\n" s += "\tcase XTL_NONE: /* Not a real value */\n" s += '\t\tcaml_raise_sys_error(caml_copy_string("Val_level XTL_NONE"));\n' s += "\t\tbreak;\n" for index,level in enumerate(levels): s += "\tcase %s:\n\t\treturn Val_int(%d);\n" % (level, index) s += """\tcase XTL_NUM_LEVELS: /* Not a real value! */ \t\tcaml_raise_sys_error( \t\t\tcaml_copy_string("Val_level XTL_NUM_LEVELS")); #if 0 /* Let the compiler catch this */ \tdefault: \t\tcaml_raise_sys_error(caml_copy_string("Val_level Unknown")); \t\tbreak; #endif \t} \tabort(); } """ return s def autogen_header(open_comment, close_comment): s = open_comment + " AUTO-GENERATED FILE DO NOT EDIT " + close_comment + "\n" s += open_comment + " autogenerated by \n" s += reduce(lambda x,y: x + " ", range(len(open_comment + " ")), "") s += "%s" % " ".join(sys.argv) s += "\n " + close_comment + "\n\n" return s if __name__ == '__main__': if len(sys.argv) < 3: print >>sys.stderr, "Usage: genlevels.py " sys.exit(1) levels, olevels = read_levels() _mli = sys.argv[1] mli = open(_mli, 'w') mli.write(autogen_header("(*", "*)")) _ml = sys.argv[2] ml = open(_ml, 'w') ml.write(autogen_header("(*", "*)")) _cinc = sys.argv[3] cinc = open(_cinc, 'w') cinc.write(autogen_header("/*", "*/")) mli.write(gen_mli(olevels)) mli.write("\n") ml.write(gen_ml(olevels)) ml.write("\n") cinc.write(gen_c(levels)) cinc.write("\n") ml.write("(* END OF AUTO-GENERATED CODE *)\n") ml.close() mli.write("(* END OF AUTO-GENERATED CODE *)\n") mli.close() cinc.close() xen-4.9.2/tools/ocaml/libs/xentoollog/xentoollog_stubs.c0000664000175000017500000001217013256712137021612 0ustar smbsmb/* * Copyright (C) 2012 Citrix Ltd. * Author Ian Campbell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #define _GNU_SOURCE #include #include #include #include #define CAML_NAME_SPACE #include #include #include #include #include #include #include #include "caml_xentoollog.h" /* The following is equal to the CAMLreturn macro, but without the return */ #define CAMLdone do{ \ caml_local_roots = caml__frame; \ }while (0) #define XTL ((xentoollog_logger *) Xtl_val(handle)) static char * dup_String_val(value s) { int len; char *c; len = caml_string_length(s); c = calloc(len + 1, sizeof(char)); if (!c) caml_raise_out_of_memory(); memcpy(c, String_val(s), len); return c; } #include "_xtl_levels.inc" /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ #define Val_none Val_int(0) #define Some_val(v) Field(v,0) static value Val_some(value v) { CAMLparam1(v); CAMLlocal1(some); some = caml_alloc(1, 0); Store_field(some, 0, v); CAMLreturn(some); } static value Val_errno(int errnoval) { if (errnoval == -1) return Val_none; return Val_some(Val_int(errnoval)); } static value Val_context(const char *context) { if (context == NULL) return Val_none; return Val_some(caml_copy_string(context)); } static void stub_xtl_ocaml_vmessage(struct xentoollog_logger *logger, xentoollog_level level, int errnoval, const char *context, const char *format, va_list al) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocalN(args, 4); struct caml_xtl *xtl = (struct caml_xtl*)logger; value *func = caml_named_value(xtl->vmessage_cb) ; char *msg; if (func == NULL) caml_raise_sys_error(caml_copy_string("Unable to find callback")); if (vasprintf(&msg, format, al) < 0) caml_raise_out_of_memory(); /* vmessage : level -> int option -> string option -> string -> unit; */ args[0] = Val_level(level); args[1] = Val_errno(errnoval); args[2] = Val_context(context); args[3] = caml_copy_string(msg); free(msg); caml_callbackN(*func, 4, args); CAMLdone; caml_enter_blocking_section(); } static void stub_xtl_ocaml_progress(struct xentoollog_logger *logger, const char *context, const char *doing_what /* no \r,\n */, int percent, unsigned long done, unsigned long total) { caml_leave_blocking_section(); CAMLparam0(); CAMLlocalN(args, 5); struct caml_xtl *xtl = (struct caml_xtl*)logger; value *func = caml_named_value(xtl->progress_cb) ; if (func == NULL) caml_raise_sys_error(caml_copy_string("Unable to find callback")); /* progress : string option -> string -> int -> int64 -> int64 -> unit; */ args[0] = Val_context(context); args[1] = caml_copy_string(doing_what); args[2] = Val_int(percent); args[3] = caml_copy_int64(done); args[4] = caml_copy_int64(total); caml_callbackN(*func, 5, args); CAMLdone; caml_enter_blocking_section(); } static void xtl_destroy(struct xentoollog_logger *logger) { struct caml_xtl *xtl = (struct caml_xtl*)logger; free(xtl->vmessage_cb); free(xtl->progress_cb); free(xtl); } void xtl_finalize(value handle) { xtl_destroy(XTL); } static struct custom_operations xentoollogger_custom_operations = { "xentoollogger_custom_operations", xtl_finalize /* custom_finalize_default */, custom_compare_default, custom_hash_default, custom_serialize_default, custom_deserialize_default }; /* external _create_logger: (string * string) -> handle = "stub_xtl_create_logger" */ CAMLprim value stub_xtl_create_logger(value cbs) { CAMLparam1(cbs); CAMLlocal1(handle); struct caml_xtl *xtl = malloc(sizeof(*xtl)); if (xtl == NULL) caml_raise_out_of_memory(); memset(xtl, 0, sizeof(*xtl)); xtl->vtable.vmessage = &stub_xtl_ocaml_vmessage; xtl->vtable.progress = &stub_xtl_ocaml_progress; xtl->vtable.destroy = &xtl_destroy; xtl->vmessage_cb = dup_String_val(Field(cbs, 0)); xtl->progress_cb = dup_String_val(Field(cbs, 1)); handle = caml_alloc_custom(&xentoollogger_custom_operations, sizeof(xtl), 0, 1); Xtl_val(handle) = xtl; CAMLreturn(handle); } /* external test: handle -> unit = "stub_xtl_test" */ CAMLprim value stub_xtl_test(value handle) { unsigned long l; CAMLparam1(handle); xtl_log(XTL, XTL_DEBUG, -1, "debug", "%s -- debug", __func__); xtl_log(XTL, XTL_INFO, -1, "test", "%s -- test 1", __func__); xtl_log(XTL, XTL_INFO, ENOSYS, "test errno", "%s -- test 2", __func__); xtl_log(XTL, XTL_CRITICAL, -1, "critical", "%s -- critical", __func__); for (l = 0UL; l<=100UL; l += 10UL) { xtl_progress(XTL, "progress", "testing", l, 100UL); usleep(10000); } CAMLreturn(Val_unit); } xen-4.9.2/tools/ocaml/libs/xentoollog/xentoollog.mli.in0000664000175000017500000000251213256712137021335 0ustar smbsmb(* * Copyright (C) 2012 Citrix Ltd. * Author Ian Campbell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) (* @@XTL_LEVELS@@ *) val level_to_string : level -> string val compare_level : level -> level -> int type handle (** call back arguments. See xentoollog.h for more info. vmessage: level: level as above errno: Some or None context: Some or None message: The log message (already formatted) progress: context: Some or None doing_what: string percent, done, total. *) type logger_cbs = { vmessage : level -> int option -> string option -> string -> unit; progress : string option -> string -> int -> int64 -> int64 -> unit; (*destroy : handle -> unit*) } external test: handle -> unit = "stub_xtl_test" val create : string -> logger_cbs -> handle xen-4.9.2/tools/ocaml/libs/xentoollog/xentoollog.ml.in0000664000175000017500000000305413256712137021166 0ustar smbsmb(* * Copyright (C) 2012 Citrix Ltd. * Author Ian Campbell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *) open Printf open Random open Callback (* @@XTL_LEVELS@@ *) let compare_level x y = compare (level_to_prio x) (level_to_prio y) type handle type logger_cbs = { vmessage : level -> int option -> string option -> string -> unit; progress : string option -> string -> int -> int64 -> int64 -> unit; (*destroy : unit -> unit*) } external _create_logger: (string * string) -> handle = "stub_xtl_create_logger" external test: handle -> unit = "stub_xtl_test" let counter = ref 0L let create name cbs : handle = (* Callback names are supposed to be unique *) let suffix = Int64.to_string !counter in counter := Int64.succ !counter; let vmessage_name = sprintf "%s_vmessage_%s" name suffix in let progress_name = sprintf "%s_progress_%s" name suffix in (*let destroy_name = sprintf "%s_destroy" name in*) Callback.register vmessage_name cbs.vmessage; Callback.register progress_name cbs.progress; _create_logger (vmessage_name, progress_name) xen-4.9.2/tools/python/0000775000175000017500000000000013256712137013136 5ustar smbsmbxen-4.9.2/tools/python/test.py0000664000175000017500000011014213256712137014466 0ustar smbsmb#! /usr/bin/env python2.3 ############################################################################## # # Copyright (c) 2001, 2002 Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """ test.py [-abBcdDfFgGhklLmMPprstTuUv] [modfilter [testfilter]] Find and run tests written using the unittest module. The test runner searches for Python modules that contain test suites. It collects those suites, and runs the tests. There are many options for controlling how the tests are run. There are options for using the debugger, reporting code coverage, and checking for refcount problems. The test runner uses the following rules for finding tests to run. It searches for packages and modules that contain "tests" as a component of the name, e.g. "frob.tests.nitz" matches this rule because tests is a sub-package of frob. Within each "tests" package, it looks for modules that begin with the name "test." For each test module, it imports the module and calls the module's test_suite() function, which must return a unittest TestSuite object. Options can be specified as command line arguments (see below). However, options may also be specified in a file named 'test.config', a Python script which, if found, will be executed before the command line arguments are processed. The test.config script should specify options by setting zero or more of the global variables: LEVEL, BUILD, and other capitalized variable names found in the test runner script (see the list of global variables in process_args().). -a level --at-level level --all Run the tests at the given level. Any test at a level at or below this is run, any test at a level above this is not run. Level 0 runs all tests. The default is to run tests at level 1. --all is a shortcut for -a 0. -b --build Run "python setup.py build" before running tests, where "python" is the version of python used to run test.py. Highly recommended. Tests will be run from the build directory. -B --build-inplace Run "python setup.py build_ext -i" before running tests. Tests will be run from the source directory. -c --pychecker use pychecker -d --debug Instead of the normal test harness, run a debug version which doesn't catch any exceptions. This is occasionally handy when the unittest code catching the exception doesn't work right. Unfortunately, the debug harness doesn't print the name of the test, so Use With Care. -D --debug-inplace Works like -d, except that it loads pdb when an exception occurs. --dir directory -s directory Option to limit where tests are searched for. This is important when you *really* want to limit the code that gets run. This can be specified more than once to run tests in two different parts of the source tree. For example, if refactoring interfaces, you don't want to see the way you have broken setups for tests in other packages. You *just* want to run the interface tests. -f --skip-unit Run functional tests but not unit tests. Note that functional tests will be skipped if the module zope.app.tests.functional cannot be imported. Functional tests also expect to find the file ftesting.zcml, which is used to configure the functional-test run. -F DEPRECATED. Run both unit and functional tests. This option is deprecated, because this is the new default mode. Note that functional tests will be skipped if the module zope.app.tests.functional cannot be imported. -g threshold --gc-threshold threshold Set the garbage collector generation0 threshold. This can be used to stress memory and gc correctness. Some crashes are only reproducible when the threshold is set to 1 (agressive garbage collection). Do "-g 0" to disable garbage collection altogether. -G gc_option --gc-option gc_option Set the garbage collection debugging flags. The argument must be one of the DEBUG_ flags defined bythe Python gc module. Multiple options can be specified by using "-G OPTION1 -G OPTION2." -k --keepbytecode Do not delete all stale bytecode before running tests -l test_root --libdir test_root Search for tests starting in the specified start directory (useful for testing components being developed outside the main "src" or "build" trees). -L --loop Keep running the selected tests in a loop. You may experience memory leakage. -m -M minimal GUI. See -U. -P --profile Run the tests under hotshot and display the top 50 stats, sorted by cumulative time and number of calls. -p --progress Show running progress. It can be combined with -v or -vv. -r --refcount Look for refcount problems. This requires that Python was built --with-pydebug. -t --top-fifty Time the individual tests and print a list of the top 50, sorted from longest to shortest. --times n --times outfile With an integer argument, time the tests and print a list of the top tests, sorted from longest to shortest. With a non-integer argument, specifies a file to which timing information is to be printed. -T --trace Use the trace module from Python for code coverage. The current utility writes coverage files to a directory named `coverage' that is parallel to `build'. It also prints a summary to stdout. -u --skip-functional CHANGED. Run unit tests but not functional tests. Note that the meaning of -u is changed from its former meaning, which is now specified by -U or --gui. -U --gui Use the PyUnit GUI instead of output to the command line. The GUI imports tests on its own, taking care to reload all dependencies on each run. The debug (-d), verbose (-v), progress (-p), and Loop (-L) options will be ignored. The testfilter filter is also not applied. -m -M --minimal-gui Note: -m is DEPRECATED in favour of -M or --minimal-gui. -m starts the gui minimized. Double-clicking the progress bar will start the import and run all tests. -v --verbose Verbose output. With one -v, unittest prints a dot (".") for each test run. With -vv, unittest prints the name of each test (for some definition of "name" ...). With no -v, unittest is silent until the end of the run, except when errors occur. When -p is also specified, the meaning of -v is slightly different. With -p and no -v only the percent indicator is displayed. With -p and -v the test name of the current test is shown to the right of the percent indicator. With -p and -vv the test name is not truncated to fit into 80 columns and it is not cleared after the test finishes. modfilter testfilter Case-sensitive regexps to limit which tests are run, used in search (not match) mode. In an extension of Python regexp notation, a leading "!" is stripped and causes the sense of the remaining regexp to be negated (so "!bc" matches any string that does not match "bc", and vice versa). By default these act like ".", i.e. nothing is excluded. modfilter is applied to a test file's path, starting at "build" and including (OS-dependent) path separators. testfilter is applied to the (method) name of the unittest methods contained in the test files whose paths modfilter matched. Extreme (yet useful) examples: test.py -vvb . "^testWriteClient$" Builds the project silently, then runs unittest in verbose mode on all tests whose names are precisely "testWriteClient". Useful when debugging a specific test. test.py -vvb . "!^testWriteClient$" As before, but runs all tests whose names aren't precisely "testWriteClient". Useful to avoid a specific failing test you don't want to deal with just yet. test.py -M . "!^testWriteClient$" As before, but now opens up a minimized PyUnit GUI window (only showing the progress bar). Useful for refactoring runs where you continually want to make sure all tests still pass. """ import gc import hotshot, hotshot.stats import os import re import pdb import sys import threading # just to get at Thread objects created by tests import time import traceback import unittest import warnings def set_trace_doctest(stdin=sys.stdin, stdout=sys.stdout, trace=pdb.set_trace): sys.stdin = stdin sys.stdout = stdout trace() pdb.set_trace_doctest = set_trace_doctest from distutils.util import get_platform PLAT_SPEC = "%s-%s" % (get_platform(), sys.version[0:3]) class ImmediateTestResult(unittest._TextTestResult): __super_init = unittest._TextTestResult.__init__ __super_startTest = unittest._TextTestResult.startTest __super_printErrors = unittest._TextTestResult.printErrors def __init__(self, stream, descriptions, verbosity, debug=False, count=None, progress=False): self.__super_init(stream, descriptions, verbosity) self._debug = debug self._progress = progress self._progressWithNames = False self.count = count self._testtimes = {} if progress and verbosity == 1: self.dots = False self._progressWithNames = True self._lastWidth = 0 self._maxWidth = 80 try: import curses except ImportError: pass else: curses.setupterm() self._maxWidth = curses.tigetnum('cols') self._maxWidth -= len("xxxx/xxxx (xxx.x%): ") + 1 def stopTest(self, test): self._testtimes[test] = time.time() - self._testtimes[test] if gc.garbage: print "The following test left garbage:" print test print gc.garbage # XXX Perhaps eat the garbage here, so that the garbage isn't # printed for every subsequent test. # Did the test leave any new threads behind? new_threads = [t for t in threading.enumerate() if (t.isAlive() and t not in self._threads)] if new_threads: print "The following test left new threads behind:" print test print "New thread(s):", new_threads def print_times(self, stream, count=None): results = self._testtimes.items() results.sort(lambda x, y: cmp(y[1], x[1])) if count: n = min(count, len(results)) if n: print >>stream, "Top %d longest tests:" % n else: n = len(results) if not n: return for i in range(n): print >>stream, "%6dms" % int(results[i][1] * 1000), results[i][0] def _print_traceback(self, msg, err, test, errlist): if self.showAll or self.dots or self._progress: self.stream.writeln("\n") self._lastWidth = 0 tb = "".join(traceback.format_exception(*err)) self.stream.writeln(msg) self.stream.writeln(tb) errlist.append((test, tb)) def startTest(self, test): if self._progress: self.stream.write("\r%4d" % (self.testsRun + 1)) if self.count: self.stream.write("/%d (%5.1f%%)" % (self.count, (self.testsRun + 1) * 100.0 / self.count)) if self.showAll: self.stream.write(": ") elif self._progressWithNames: # XXX will break with multibyte strings name = self.getShortDescription(test) width = len(name) if width < self._lastWidth: name += " " * (self._lastWidth - width) self.stream.write(": %s" % name) self._lastWidth = width self.stream.flush() self._threads = threading.enumerate() self.__super_startTest(test) self._testtimes[test] = time.time() def getShortDescription(self, test): s = self.getDescription(test) if len(s) > self._maxWidth: pos = s.find(" (") if pos >= 0: w = self._maxWidth - (pos + 5) if w < 1: # first portion (test method name) is too long s = s[:self._maxWidth-3] + "..." else: pre = s[:pos+2] post = s[-w:] s = "%s...%s" % (pre, post) return s[:self._maxWidth] def addError(self, test, err): if self._progress: self.stream.write("\r") if self._debug: raise err[0], err[1], err[2] self._print_traceback("Error in test %s" % test, err, test, self.errors) def addFailure(self, test, err): if self._progress: self.stream.write("\r") if self._debug: raise err[0], err[1], err[2] self._print_traceback("Failure in test %s" % test, err, test, self.failures) def printErrors(self): if self._progress and not (self.dots or self.showAll): self.stream.writeln() self.__super_printErrors() def printErrorList(self, flavor, errors): for test, err in errors: self.stream.writeln(self.separator1) self.stream.writeln("%s: %s" % (flavor, self.getDescription(test))) self.stream.writeln(self.separator2) self.stream.writeln(err) class ImmediateTestRunner(unittest.TextTestRunner): __super_init = unittest.TextTestRunner.__init__ def __init__(self, **kwarg): debug = kwarg.get("debug") if debug is not None: del kwarg["debug"] progress = kwarg.get("progress") if progress is not None: del kwarg["progress"] profile = kwarg.get("profile") if profile is not None: del kwarg["profile"] self.__super_init(**kwarg) self._debug = debug self._progress = progress self._profile = profile # Create the test result here, so that we can add errors if # the test suite search process has problems. The count # attribute must be set in run(), because we won't know the # count until all test suites have been found. self.result = ImmediateTestResult( self.stream, self.descriptions, self.verbosity, debug=self._debug, progress=self._progress) def _makeResult(self): # Needed base class run method. return self.result def run(self, test): self.result.count = test.countTestCases() if self._debug: club_debug(test) if self._profile: prof = hotshot.Profile("tests_profile.prof") args = (self, test) r = prof.runcall(unittest.TextTestRunner.run, *args) prof.close() stats = hotshot.stats.load("tests_profile.prof") stats.sort_stats('cumulative', 'calls') stats.print_stats(50) return r return unittest.TextTestRunner.run(self, test) def club_debug(test): # Beat a debug flag into debug-aware test cases setDebugModeOn = getattr(test, 'setDebugModeOn', None) if setDebugModeOn is not None: setDebugModeOn() for subtest in getattr(test, '_tests', ()): club_debug(subtest) # setup list of directories to put on the path class PathInit: def __init__(self, build, build_inplace, libdir=None): self.inplace = None # Figure out if we should test in-place or test in-build. If the -b # or -B option was given, test in the place we were told to build in. # Otherwise, we'll look for a build directory and if we find one, # we'll test there, otherwise we'll test in-place. if build: self.inplace = build_inplace if self.inplace is None: # Need to figure it out if os.path.isdir(os.path.join("build", "lib.%s" % PLAT_SPEC)): self.inplace = False else: self.inplace = True # Calculate which directories we're going to add to sys.path, and cd # to the appropriate working directory self.org_cwd = os.getcwd() if self.inplace: self.libdir = "src" else: self.libdir = "lib.%s" % PLAT_SPEC os.chdir("build") # Hack sys.path self.cwd = os.getcwd() sys.path.insert(0, os.path.join(self.cwd, self.libdir)) # Hack again for external products. global functional kind = functional and "FUNCTIONAL" or "UNIT" if libdir: extra = os.path.join(self.org_cwd, libdir) print "Running %s tests from %s" % (kind, extra) self.libdir = extra sys.path.insert(0, extra) else: print "Running %s tests from %s" % (kind, self.cwd) # Make sure functional tests find ftesting.zcml if functional: config_file = 'ftesting.zcml' if not self.inplace: # We chdired into build, so ftesting.zcml is in the # parent directory config_file = os.path.join('..', 'ftesting.zcml') print "Parsing %s" % config_file from zope.app.tests.functional import FunctionalTestSetup FunctionalTestSetup(config_file) def match(rx, s): if not rx: return True if rx[0] == "!": return re.search(rx[1:], s) is None else: return re.search(rx, s) is not None class TestFileFinder: def __init__(self, prefix): self.files = [] self._plen = len(prefix) if not prefix.endswith(os.sep): self._plen += 1 global functional if functional: self.dirname = "ftests" else: self.dirname = "tests" def visit(self, rx, dir, files): if os.path.split(dir)[1] != self.dirname: # Allow tests/ftests module rather than package. modfname = self.dirname + '.py' if modfname in files: path = os.path.join(dir, modfname) if match(rx, path): self.files.append(path) return return # ignore tests that aren't in packages if not "__init__.py" in files: if not files or files == ["CVS"]: return print "not a package", dir return # Put matching files in matches. If matches is non-empty, # then make sure that the package is importable. matches = [] for file in files: if file.startswith('test') and os.path.splitext(file)[-1] == '.py': path = os.path.join(dir, file) if match(rx, path): matches.append(path) # ignore tests when the package can't be imported, possibly due to # dependency failures. pkg = dir[self._plen:].replace(os.sep, '.') try: __import__(pkg) # We specifically do not want to catch ImportError since that's useful # information to know when running the tests. except RuntimeError, e: if VERBOSE: print "skipping %s because: %s" % (pkg, e) return else: self.files.extend(matches) def module_from_path(self, path): """Return the Python package name indicated by the filesystem path.""" assert path.endswith(".py") path = path[self._plen:-3] mod = path.replace(os.sep, ".") return mod def walk_with_symlinks(top, func, arg): """Like os.path.walk, but follows symlinks on POSIX systems. This could theoreticaly result in an infinite loop, if you create symlink cycles in your Zope sandbox, so don't do that. """ try: names = os.listdir(top) except os.error: return func(arg, top, names) exceptions = ('.', '..') for name in names: if name not in exceptions: name = os.path.join(top, name) if os.path.isdir(name): walk_with_symlinks(name, func, arg) def find_test_dir(dir): if os.path.exists(dir): return dir d = os.path.join(pathinit.libdir, dir) if os.path.exists(d): if os.path.isdir(d): return d raise ValueError("%s does not exist and %s is not a directory" % (dir, d)) raise ValueError("%s does not exist!" % dir) def find_tests(rx): global finder finder = TestFileFinder(pathinit.libdir) if TEST_DIRS: for d in TEST_DIRS: d = find_test_dir(d) walk_with_symlinks(d, finder.visit, rx) else: walk_with_symlinks(pathinit.libdir, finder.visit, rx) return finder.files def package_import(modname): mod = __import__(modname) for part in modname.split(".")[1:]: mod = getattr(mod, part) return mod class PseudoTestCase: """Minimal test case objects to create error reports. If test.py finds something that looks like it should be a test but can't load it or find its test suite, it will report an error using a PseudoTestCase. """ def __init__(self, name, descr=None): self.name = name self.descr = descr def shortDescription(self): return self.descr def __str__(self): return "Invalid Test (%s)" % self.name def get_suite(file, result): modname = finder.module_from_path(file) try: mod = package_import(modname) return mod.test_suite() except: result.addError(PseudoTestCase(modname), sys.exc_info()) return None def filter_testcases(s, rx): new = unittest.TestSuite() for test in s._tests: # See if the levels match dolevel = (LEVEL == 0) or LEVEL >= getattr(test, "level", 0) if not dolevel: continue if isinstance(test, unittest.TestCase): name = test.id() # Full test name: package.module.class.method name = name[1 + name.rfind("."):] # extract method name if not rx or match(rx, name): new.addTest(test) else: filtered = filter_testcases(test, rx) if filtered: new.addTest(filtered) return new def gui_runner(files, test_filter): if BUILD_INPLACE: utildir = os.path.join(os.getcwd(), "utilities") else: utildir = os.path.join(os.getcwd(), "..", "utilities") sys.path.append(utildir) import unittestgui suites = [] for file in files: suites.append(finder.module_from_path(file) + ".test_suite") suites = ", ".join(suites) minimal = (GUI == "minimal") unittestgui.main(suites, minimal) class TrackRefs: """Object to track reference counts across test runs.""" def __init__(self): self.type2count = {} self.type2all = {} def update(self): obs = sys.getobjects(0) type2count = {} type2all = {} for o in obs: all = sys.getrefcount(o) if type(o) is str and o == '': # avoid dictionary madness continue t = type(o) if t in type2count: type2count[t] += 1 type2all[t] += all else: type2count[t] = 1 type2all[t] = all ct = [(type2count[t] - self.type2count.get(t, 0), type2all[t] - self.type2all.get(t, 0), t) for t in type2count.iterkeys()] ct.sort() ct.reverse() printed = False for delta1, delta2, t in ct: if delta1 or delta2: if not printed: print "%-55s %8s %8s" % ('', 'insts', 'refs') printed = True print "%-55s %8d %8d" % (t, delta1, delta2) self.type2count = type2count self.type2all = type2all def runner(files, test_filter, debug): runner = ImmediateTestRunner(verbosity=VERBOSE, debug=DEBUG, progress=PROGRESS, profile=PROFILE, descriptions=False) suite = unittest.TestSuite() for file in files: s = get_suite(file, runner.result) # See if the levels match dolevel = (LEVEL == 0) or LEVEL >= getattr(s, "level", 0) if s is not None and dolevel: s = filter_testcases(s, test_filter) suite.addTest(s) try: r = runner.run(suite) if TIMESFN: r.print_times(open(TIMESFN, "w")) if VERBOSE: print "Wrote timing data to", TIMESFN if TIMETESTS: r.print_times(sys.stdout, TIMETESTS) except: if DEBUGGER: print "%s:" % (sys.exc_info()[0], ) print sys.exc_info()[1] pdb.post_mortem(sys.exc_info()[2]) else: raise def remove_stale_bytecode(arg, dirname, names): names = map(os.path.normcase, names) for name in names: if name.endswith(".pyc") or name.endswith(".pyo"): srcname = name[:-1] if srcname not in names: fullname = os.path.join(dirname, name) print "Removing stale bytecode file", fullname os.unlink(fullname) def main(module_filter, test_filter, libdir): if not KEEP_STALE_BYTECODE: os.path.walk(os.curdir, remove_stale_bytecode, None) configure_logging() # Initialize the path and cwd global pathinit pathinit = PathInit(BUILD, BUILD_INPLACE, libdir) files = find_tests(module_filter) files.sort() if GUI: gui_runner(files, test_filter) elif LOOP: if REFCOUNT: rc = sys.gettotalrefcount() track = TrackRefs() while True: runner(files, test_filter, DEBUG) gc.collect() if gc.garbage: print "GARBAGE:", len(gc.garbage), gc.garbage return if REFCOUNT: prev = rc rc = sys.gettotalrefcount() print "totalrefcount=%-8d change=%-6d" % (rc, rc - prev) track.update() else: runner(files, test_filter, DEBUG) os.chdir(pathinit.org_cwd) def configure_logging(): """Initialize the logging module.""" import logging.config # Get the log.ini file from the current directory instead of possibly # buried in the build directory. XXX This isn't perfect because if # log.ini specifies a log file, it'll be relative to the build directory. # Hmm... logini = os.path.abspath("log.ini") if os.path.exists(logini): logging.config.fileConfig(logini) else: logging.basicConfig() if os.environ.has_key("LOGGING"): level = int(os.environ["LOGGING"]) logging.getLogger().setLevel(level) def process_args(argv=None): import getopt global MODULE_FILTER global TEST_FILTER global VERBOSE global LOOP global GUI global TRACE global REFCOUNT global DEBUG global DEBUGGER global BUILD global LEVEL global LIBDIR global TIMESFN global TIMETESTS global PROGRESS global BUILD_INPLACE global KEEP_STALE_BYTECODE global TEST_DIRS global PROFILE global GC_THRESHOLD global GC_FLAGS global RUN_UNIT global RUN_FUNCTIONAL global PYCHECKER if argv is None: argv = sys.argv MODULE_FILTER = None TEST_FILTER = None VERBOSE = 0 LOOP = False GUI = False TRACE = False REFCOUNT = False DEBUG = False # Don't collect test results; simply let tests crash DEBUGGER = False BUILD = False BUILD_INPLACE = False GC_THRESHOLD = None gcdebug = 0 GC_FLAGS = [] LEVEL = 1 LIBDIR = None PROGRESS = False TIMESFN = None TIMETESTS = 0 KEEP_STALE_BYTECODE = 0 RUN_UNIT = True RUN_FUNCTIONAL = True TEST_DIRS = [] PROFILE = False PYCHECKER = False config_filename = 'test.config' # import the config file if os.path.isfile(config_filename): print 'Configuration file found.' execfile(config_filename, globals()) try: opts, args = getopt.getopt(argv[1:], "a:bBcdDfFg:G:hkl:LmMPprs:tTuUv", ["all", "help", "libdir=", "times=", "keepbytecode", "dir=", "build", "build-inplace", "at-level=", "pychecker", "debug", "pdebug", "gc-threshold=", "gc-option=", "loop", "gui", "minimal-gui", "profile", "progress", "refcount", "trace", "top-fifty", "verbose", ]) # fixme: add the long names # fixme: add the extra documentation # fixme: test for functional first! except getopt.error, msg: print msg print "Try `python %s -h' for more information." % argv[0] sys.exit(2) for k, v in opts: if k in ("-a", "--at-level"): LEVEL = int(v) elif k == "--all": LEVEL = 0 os.environ["COMPLAIN_IF_TESTS_MISSED"]='1' elif k in ("-b", "--build"): BUILD = True elif k in ("-B", "--build-inplace"): BUILD = BUILD_INPLACE = True elif k in("-c", "--pychecker"): PYCHECKER = True elif k in ("-d", "--debug"): DEBUG = True elif k in ("-D", "--pdebug"): DEBUG = True DEBUGGER = True elif k in ("-f", "--skip-unit"): RUN_UNIT = False elif k in ("-u", "--skip-functional"): RUN_FUNCTIONAL = False elif k == "-F": message = 'Unit plus functional is the default behaviour.' warnings.warn(message, DeprecationWarning) RUN_UNIT = True RUN_FUNCTIONAL = True elif k in ("-h", "--help"): print __doc__ sys.exit(0) elif k in ("-g", "--gc-threshold"): GC_THRESHOLD = int(v) elif k in ("-G", "--gc-option"): if not v.startswith("DEBUG_"): print "-G argument must be DEBUG_ flag, not", repr(v) sys.exit(1) GC_FLAGS.append(v) elif k in ('-k', '--keepbytecode'): KEEP_STALE_BYTECODE = 1 elif k in ('-l', '--libdir'): LIBDIR = v elif k in ("-L", "--loop"): LOOP = 1 elif k == "-m": GUI = "minimal" msg = "Use -M or --minimal-gui instead of -m." warnings.warn(msg, DeprecationWarning) elif k in ("-M", "--minimal-gui"): GUI = "minimal" elif k in ("-P", "--profile"): PROFILE = True elif k in ("-p", "--progress"): PROGRESS = True elif k in ("-r", "--refcount"): REFCOUNT = True elif k in ("-T", "--trace"): TRACE = True elif k in ("-t", "--top-fifty"): if not TIMETESTS: TIMETESTS = 50 elif k in ("-u", "--gui"): GUI = 1 elif k in ("-v", "--verbose"): VERBOSE += 1 elif k == "--times": try: TIMETESTS = int(v) except ValueError: # must be a filename to write TIMESFN = v elif k in ('-s', '--dir'): TEST_DIRS.append(v) if PYCHECKER: # make sure you have a recent version of pychecker if not os.environ.get("PYCHECKER"): os.environ["PYCHECKER"] = "-q" import pychecker.checker if REFCOUNT and not hasattr(sys, "gettotalrefcount"): print "-r ignored, because it needs a debug build of Python" REFCOUNT = False if sys.version_info < ( 2,3,2 ): print """\ ERROR: Your python version is not supported by Zope3. Zope3 needs Python 2.3.2 or greater. You are running:""" + sys.version sys.exit(1) if GC_THRESHOLD is not None: if GC_THRESHOLD == 0: gc.disable() print "gc disabled" else: gc.set_threshold(GC_THRESHOLD) print "gc threshold:", gc.get_threshold() if GC_FLAGS: val = 0 for flag in GC_FLAGS: v = getattr(gc, flag, None) if v is None: print "Unknown gc flag", repr(flag) print gc.set_debug.__doc__ sys.exit(1) val |= v gcdebug |= v if gcdebug: gc.set_debug(gcdebug) if BUILD: # Python 2.3 is more sane in its non -q output if sys.hexversion >= 0x02030000: qflag = "" else: qflag = "-q" cmd = sys.executable + " setup.py " + qflag + " build" if BUILD_INPLACE: cmd += "_ext -i" if VERBOSE: print cmd sts = os.system(cmd) if sts: print "Build failed", hex(sts) sys.exit(1) k = [] if RUN_UNIT: k.append(False) if RUN_FUNCTIONAL: k.append(True) global functional for functional in k: if VERBOSE: kind = functional and "FUNCTIONAL" or "UNIT" if LEVEL == 0: print "Running %s tests at all levels" % kind else: print "Running %s tests at level %d" % (kind, LEVEL) # This was to avoid functional tests outside of z3, but this doesn't really # work right. ## if functional: ## try: ## from zope.app.tests.functional import FunctionalTestSetup ## except ImportError: ## raise ## print ('Skipping functional tests: could not import ' ## 'zope.app.tests.functional') ## continue # XXX We want to change *visible* warnings into errors. The next # line changes all warnings into errors, including warnings we # normally never see. In particular, test_datetime does some # short-integer arithmetic that overflows to long ints, and, by # default, Python doesn't display the overflow warning that can # be enabled when this happens. The next line turns that into an # error instead. Guido suggests that a better to get what we're # after is to replace warnings.showwarning() with our own thing # that raises an error. ## warnings.filterwarnings("error") warnings.filterwarnings("ignore", module="logging") if args: if len(args) > 1: TEST_FILTER = args[1] MODULE_FILTER = args[0] try: if TRACE: # if the trace module is used, then we don't exit with # status if on a false return value from main. coverdir = os.path.join(os.getcwd(), "coverage") import trace ignoremods = ["os", "posixpath", "stat"] tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix], ignoremods=ignoremods, trace=False, count=True) tracer.runctx("main(MODULE_FILTER, TEST_FILTER, LIBDIR)", globals=globals(), locals=vars()) r = tracer.results() path = "/tmp/trace.%s" % os.getpid() import cPickle f = open(path, "wb") cPickle.dump(r, f) f.close() print path r.write_results(show_missing=True, summary=True, coverdir=coverdir) else: bad = main(MODULE_FILTER, TEST_FILTER, LIBDIR) if bad: sys.exit(1) except ImportError, err: print err print sys.path raise if __name__ == "__main__": process_args() xen-4.9.2/tools/python/setup.py0000664000175000017500000000362713256712137014660 0ustar smbsmb from distutils.core import setup, Extension import os, sys XEN_ROOT = "../.." extra_compile_args = [ "-fno-strict-aliasing", "-Werror" ] PATH_XEN = XEN_ROOT + "/tools/include" PATH_LIBXENTOOLLOG = XEN_ROOT + "/tools/libs/toollog" PATH_LIBXENEVTCHN = XEN_ROOT + "/tools/libs/evtchn" PATH_LIBXC = XEN_ROOT + "/tools/libxc" PATH_LIBXL = XEN_ROOT + "/tools/libxl" PATH_XENSTORE = XEN_ROOT + "/tools/xenstore" xc = Extension("xc", extra_compile_args = extra_compile_args, include_dirs = [ PATH_XEN, PATH_LIBXENTOOLLOG + "/include", PATH_LIBXENEVTCHN + "/include", PATH_LIBXC + "/include", "xen/lowlevel/xc" ], library_dirs = [ PATH_LIBXC ], libraries = [ "xenctrl", "xenguest" ], depends = [ PATH_LIBXC + "/libxenctrl.so", PATH_LIBXC + "/libxenguest.so" ], extra_link_args = [ "-Wl,-rpath-link="+PATH_LIBXENTOOLLOG ], sources = [ "xen/lowlevel/xc/xc.c" ]) xs = Extension("xs", extra_compile_args = extra_compile_args, include_dirs = [ PATH_XEN, PATH_XENSTORE + "/include", "xen/lowlevel/xs" ], library_dirs = [ PATH_XENSTORE ], libraries = [ "xenstore" ], depends = [ PATH_XENSTORE + "/libxenstore.so" ], sources = [ "xen/lowlevel/xs/xs.c" ]) plat = os.uname()[0] modules = [ xc, xs ] setup(name = 'xen', version = '3.0', description = 'Xen', packages = ['xen', 'xen.migration', 'xen.lowlevel', ], ext_package = "xen.lowlevel", ext_modules = modules ) xen-4.9.2/tools/python/pylintrc0000664000175000017500000002676313256712137014743 0ustar smbsmb# lint Python modules using external checkers. # # This is the main checker controling the other ones and the reports # generation. It is itself both a raw checker and an astng checker in order # to: # * handle message activation / deactivation at the module level # * handle some basic but necessary stats'data (number of classes, methods...) # # This checker also defines the following reports: # * R0001: Total errors / warnings # * R0002: % errors / warnings by module # * R0003: Messages # * R0004: Global evaluation # [MASTER] # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. ignore=CVS # Pickle collected data for later comparisons. persistent=yes # Set the cache size for astng objects. cache-size=500 [REPORTS] # Tells wether to display a full report or only the messages reports=yes # Use HTML as output format instead of text html=no # Use a parseable text output format, so your favorite text editor will be able # to jump to the line corresponding to a message. parseable=no # Colorizes text output using ansi escape codes color=no # Put messages in a separate file for each module / package specified on the # command line instead of printing them on stdout. Reports (if any) will be # written in a file name "pylint_global.[txt|html]". files-output=no # Python expression which should return a note less than 10 (10 is the highest # note).You have access to the variables errors warning, statement which # respectivly contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation report # (R0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Add a comment according to your evaluation note. This is used by the global # evaluation report (R0004). comment=no # Include message's id in output include-ids=yes # checks for # * unused variables / imports # * undefined variables # * redefinition of variable from builtins or from an outer scope # * use of variable before assigment # [VARIABLES] # Enable / disable this checker enable-variables=yes # Tells wether we should check for unused import in __init__ files. init-import=no # List of variable names used for dummy variables (i.e. not used). dummy-variables=_,_1,_2,_3,_4,_5,dummy # checks for : # * doc strings # * modules / classes / functions / methods / arguments / variables name # * number of arguments, local variables, branchs, returns and statements in # functions, methods # * required module attributes # * dangerous default values as arguments # * redefinition of function / method / class # * uses of the global statement # # This checker also defines the following reports: # * R0101: Statistics by type # [BASIC] # Enable / disable this checker enable-basic=yes # Required attributes for module, separated by a comma required-attributes= # Regular expression which should only match functions or classes name which do # not require a docstring no-docstring-rgx=.* # Minimal length for module / class / function / method / argument / variable # names min-name-length=1 # Regular expression which should only match correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Regular expression which should only match correct class names class-rgx=[A-Z_][a-zA-Z0-9]+$ # Regular expression which should only match correct function names function-rgx=[a-z_][A-Za-z0-9_]*$ # Regular expression which should only match correct method names method-rgx=[a-z_][A-Za-z0-9_]*$ # Regular expression which should only match correct argument names argument-rgx=[a-z_][A-Za-z0-9_]*$ # Regular expression which should only match correct variable names variable-rgx=[a-z_][A-Za-z0-9_]*$ # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata # List of builtins function names that should not be used, separated by a comma bad-functions=apply,input # checks for sign of poor/misdesign: # * number of methods, attributes, local variables... # * size, complexity of functions, methods # [DESIGN] # Enable / disable this checker enable-design=yes # Maximum number of arguments for function / method max-args=15 # Maximum number of locals for function / method body max-locals=15 # Maximum number of return / yield for function / method body max-returns=6 # Maximum number of branch for function / method body max-branchs=12 # Maximum number of statements in function / method body max-statements=50 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Minimum number of public methods for a class (see R0903). min-public-methods=2 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # checks for : # * methods without self as first argument # * overriden methods signature # * access only to existant members via self # * attributes not defined in the __init__ method # * supported interfaces implementation # * unreachable code # [CLASSES] # Enable / disable this checker enable-classes=yes # List of interface methods to ignore, separated by a comma. This is used for # instance to not check methods defines in Zope's Interface base class. ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by # Tells wether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # checks for # * external modules dependencies # * relative / wildcard imports # * cyclic imports # * uses of deprecated modules # # This checker also defines the following reports: # * R0401: External dependencies # * R0402: Modules dependencies graph # [IMPORTS] # Enable / disable this checker enable-imports=no # Deprecated modules which should not be used, separated by a comma deprecated-modules=regsub,string,TERMIOS,Bastion,rexec # Create a graph of every (i.e. internal and external) dependencies in the given # file (report R0402 must not be disabled) import-graph= # Create a graph of external dependencies in the given file (report R0402 must # not be disabled) ext-import-graph= # Create a graph of internal dependencies in the given file (report R0402 must # not be disabled) int-import-graph= # checks for # * excepts without exception filter # * string exceptions # [EXCEPTIONS] # Enable / disable this checker enable-exceptions=yes # checks for : # * unauthorized constructions # * strict indentation # * line length # * use of <> instead of != # [FORMAT] # Enable / disable this checker enable-format=no # Maximum number of characters on a single line. max-line-length=80 # Maximum number of lines in a module max-module-lines=1000 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 tab). indent-string=' ' # does not check anything but gives some raw metrics : # * total number of lines # * total number of code lines # * total number of docstring lines # * total number of comments lines # * total number of empty lines # # This checker also defines the following reports: # * R0701: Raw metrics # [METRICS] # Enable / disable this checker enable-metrics=yes # checks for: # * warning notes in the code like FIXME, XXX # * PEP 263: source code with non ascii character but no encoding declaration # [MISCELLANEOUS] # Enable / disable this checker enable-miscellaneous=yes # List of note tags to take in consideration, separated by a comma. Default to # FIXME, XXX, TODO notes=FIXME,XXX,TODO # checks for similarities and duplicated code. This computation may be # memory / CPU intensive, so you should disable it if you experiments some # problems. # # This checker also defines the following reports: # * R0801: Duplication # [SIMILARITIES] # Enable / disable this checker enable-similarities=yes # Minimum lines number of a similarity. min-similarity-lines=4 # Ignore comments when computing similarities. ignore-comments=yes xen-4.9.2/tools/python/ZPL-2.00000664000175000017500000000447513256712137014035 0ustar smbsmbZope Public License (ZPL) Version 2.0 ----------------------------------------------- This software is Copyright (c) Zope Corporation (tm) and Contributors. All rights reserved. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the above copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name Zope Corporation (tm) must not be used to endorse or promote products derived from this software without prior written permission from Zope Corporation. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of Zope Corporation. Use of them is covered in a separate agreement (see http://www.zope.com/Marks). 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This software consists of contributions made by Zope Corporation and many individuals on behalf of Zope Corporation. Specific attributions are listed in the accompanying credits file.xen-4.9.2/tools/python/Makefile0000664000175000017500000000160713256712137014602 0ustar smbsmbXEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk .PHONY: all all: build PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) PY_LDFLAGS = $(LDFLAGS) $(APPEND_LDFLAGS) .PHONY: build build: CC="$(CC)" CFLAGS="$(PY_CFLAGS)" $(PYTHON) setup.py build .PHONY: install install: $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN) CC="$(CC)" CFLAGS="$(PY_CFLAGS)" LDFLAGS="$(PY_LDFLAGS)" $(PYTHON) \ setup.py install $(PYTHON_PREFIX_ARG) --root="$(DESTDIR)" \ --force $(INSTALL_PROG) scripts/convert-legacy-stream $(DESTDIR)$(LIBEXEC_BIN) $(INSTALL_PROG) scripts/verify-stream-v2 $(DESTDIR)$(LIBEXEC_BIN) .PHONY: test test: export LD_LIBRARY_PATH=$$(readlink -f ../libxc):$$(readlink -f ../xenstore); $(PYTHON) test.py -b -u .PHONY: clean clean: find . \( -name "*.py[ocd]" -o -name "*~" -o -name "_*.[hc]" \) -delete rm -rf build/ rm -f $(DEPS) .PHONY: distclean distclean: clean -include $(DEPS) xen-4.9.2/tools/python/xen/0000775000175000017500000000000013256712137013730 5ustar smbsmbxen-4.9.2/tools/python/xen/lowlevel/0000775000175000017500000000000013256712137015561 5ustar smbsmbxen-4.9.2/tools/python/xen/lowlevel/xc/0000775000175000017500000000000013256712137016173 5ustar smbsmbxen-4.9.2/tools/python/xen/lowlevel/xc/xc.c0000664000175000017500000024711713256712137016765 0ustar smbsmb/****************************************************************************** * Xc.c * * Copyright (c) 2003-2004, K A Fraser (University of Cambridge) */ #include #define XC_WANT_COMPAT_MAP_FOREIGN_API #include #include #include #include #include #include #include #include #include #include #include #include "xc_dom.h" #include #include #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /* Needed for Python versions earlier than 2.3. */ #ifndef PyMODINIT_FUNC #define PyMODINIT_FUNC DL_EXPORT(void) #endif #define PKG "xen.lowlevel.xc" #define CLS "xc" #define FLASK_CTX_LEN 1024 /* Python 2 compatibility */ #if PY_MAJOR_VERSION >= 3 #define PyLongOrInt_FromLong PyLong_FromLong #define PyLongOrInt_Check PyLong_Check #define PyLongOrInt_AsLong PyLong_AsLong #else #define PyLongOrInt_FromLong PyInt_FromLong #define PyLongOrInt_Check PyInt_Check #define PyLongOrInt_AsLong PyInt_AsLong #endif static PyObject *xc_error_obj, *zero; typedef struct { PyObject_HEAD; xc_interface *xc_handle; } XcObject; static PyObject *dom_op(XcObject *self, PyObject *args, int (*fn)(xc_interface *, uint32_t)); static PyObject *pyxc_error_to_exception(xc_interface *xch) { PyObject *pyerr; static xc_error err_buf; const char *desc; const xc_error *err; if (xch) { err = xc_get_last_error(xch); } else { snprintf(err_buf.message, sizeof(err_buf.message), "xc_interface_open failed: %s", strerror(errno)); err_buf.code = XC_INTERNAL_ERROR; err = &err_buf; } desc = xc_error_code_to_desc(err->code); if ( err->code == XC_ERROR_NONE ) return PyErr_SetFromErrno(xc_error_obj); if ( err->message[0] != '\0' ) pyerr = Py_BuildValue("(iss)", err->code, desc, err->message); else pyerr = Py_BuildValue("(is)", err->code, desc); if (xch) xc_clear_last_error(xch); if ( pyerr != NULL ) { PyErr_SetObject(xc_error_obj, pyerr); Py_DECREF(pyerr); } return NULL; } static PyObject *pyxc_domain_dumpcore(XcObject *self, PyObject *args) { uint32_t dom; char *corefile; if ( !PyArg_ParseTuple(args, "is", &dom, &corefile) ) return NULL; if ( (corefile == NULL) || (corefile[0] == '\0') ) return NULL; if ( xc_domain_dumpcore(self->xc_handle, dom, corefile) != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_create(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom = 0, ssidref = 0, flags = 0, target = 0; int ret, i; PyObject *pyhandle = NULL; xen_domain_handle_t handle = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef }; static char *kwd_list[] = { "domid", "ssidref", "handle", "flags", "target", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iiOii", kwd_list, &dom, &ssidref, &pyhandle, &flags, &target)) return NULL; if ( pyhandle != NULL ) { if ( !PyList_Check(pyhandle) || (PyList_Size(pyhandle) != sizeof(xen_domain_handle_t)) ) goto out_exception; for ( i = 0; i < sizeof(xen_domain_handle_t); i++ ) { PyObject *p = PyList_GetItem(pyhandle, i); if ( !PyLongOrInt_Check(p) ) goto out_exception; handle[i] = (uint8_t)PyLongOrInt_AsLong(p); } } if ( (ret = xc_domain_create(self->xc_handle, ssidref, handle, flags, &dom, NULL)) < 0 ) return pyxc_error_to_exception(self->xc_handle); if ( target ) if ( (ret = xc_domain_set_target(self->xc_handle, dom, target)) < 0 ) return pyxc_error_to_exception(self->xc_handle); return PyLongOrInt_FromLong(dom); out_exception: errno = EINVAL; PyErr_SetFromErrno(xc_error_obj); return NULL; } static PyObject *pyxc_domain_max_vcpus(XcObject *self, PyObject *args) { uint32_t dom, max; if (!PyArg_ParseTuple(args, "ii", &dom, &max)) return NULL; if (xc_domain_max_vcpus(self->xc_handle, dom, max) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_pause(XcObject *self, PyObject *args) { return dom_op(self, args, xc_domain_pause); } static PyObject *pyxc_domain_unpause(XcObject *self, PyObject *args) { return dom_op(self, args, xc_domain_unpause); } static PyObject *pyxc_domain_destroy_hook(XcObject *self, PyObject *args) { Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_destroy(XcObject *self, PyObject *args) { return dom_op(self, args, xc_domain_destroy); } static PyObject *pyxc_domain_shutdown(XcObject *self, PyObject *args) { uint32_t dom, reason; if ( !PyArg_ParseTuple(args, "ii", &dom, &reason) ) return NULL; if ( xc_domain_shutdown(self->xc_handle, dom, reason) != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_resume(XcObject *self, PyObject *args) { uint32_t dom; int fast; if ( !PyArg_ParseTuple(args, "ii", &dom, &fast) ) return NULL; if ( xc_domain_resume(self->xc_handle, dom, fast) != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_vcpu_setaffinity(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; int vcpu = 0, i; xc_cpumap_t cpumap; PyObject *cpulist = NULL; int nr_cpus; static char *kwd_list[] = { "domid", "vcpu", "cpumap", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|iO", kwd_list, &dom, &vcpu, &cpulist) ) return NULL; nr_cpus = xc_get_max_cpus(self->xc_handle); if ( nr_cpus < 0 ) return pyxc_error_to_exception(self->xc_handle); cpumap = xc_cpumap_alloc(self->xc_handle); if(cpumap == NULL) return pyxc_error_to_exception(self->xc_handle); if ( (cpulist != NULL) && PyList_Check(cpulist) ) { for ( i = 0; i < PyList_Size(cpulist); i++ ) { long cpu = PyLongOrInt_AsLong(PyList_GetItem(cpulist, i)); if ( cpu < 0 || cpu >= nr_cpus ) { free(cpumap); errno = EINVAL; PyErr_SetFromErrno(xc_error_obj); return NULL; } cpumap[cpu / 8] |= 1 << (cpu % 8); } } if ( xc_vcpu_setaffinity(self->xc_handle, dom, vcpu, cpumap, NULL, XEN_VCPUAFFINITY_HARD) != 0 ) { free(cpumap); return pyxc_error_to_exception(self->xc_handle); } Py_INCREF(zero); free(cpumap); return zero; } static PyObject *pyxc_domain_sethandle(XcObject *self, PyObject *args) { int i; uint32_t dom; PyObject *pyhandle; xen_domain_handle_t handle; if (!PyArg_ParseTuple(args, "iO", &dom, &pyhandle)) return NULL; if ( !PyList_Check(pyhandle) || (PyList_Size(pyhandle) != sizeof(xen_domain_handle_t)) ) { goto out_exception; } for ( i = 0; i < sizeof(xen_domain_handle_t); i++ ) { PyObject *p = PyList_GetItem(pyhandle, i); if ( !PyLongOrInt_Check(p) ) goto out_exception; handle[i] = (uint8_t)PyLongOrInt_AsLong(p); } if (xc_domain_sethandle(self->xc_handle, dom, handle) < 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; out_exception: PyErr_SetFromErrno(xc_error_obj); return NULL; } static PyObject *pyxc_domain_getinfo(XcObject *self, PyObject *args, PyObject *kwds) { PyObject *list, *info_dict, *pyhandle; uint32_t first_dom = 0; int max_doms = 1024, nr_doms, i, j; xc_dominfo_t *info; static char *kwd_list[] = { "first_dom", "max_doms", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwd_list, &first_dom, &max_doms) ) return NULL; info = calloc(max_doms, sizeof(xc_dominfo_t)); if (info == NULL) return PyErr_NoMemory(); nr_doms = xc_domain_getinfo(self->xc_handle, first_dom, max_doms, info); if (nr_doms < 0) { free(info); return pyxc_error_to_exception(self->xc_handle); } list = PyList_New(nr_doms); for ( i = 0 ; i < nr_doms; i++ ) { info_dict = Py_BuildValue( "{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i" ",s:L,s:L,s:L,s:i,s:i,s:i}", "domid", (int)info[i].domid, "online_vcpus", info[i].nr_online_vcpus, "max_vcpu_id", info[i].max_vcpu_id, "hvm", info[i].hvm, "dying", info[i].dying, "crashed", info[i].crashed, "shutdown", info[i].shutdown, "paused", info[i].paused, "blocked", info[i].blocked, "running", info[i].running, "mem_kb", (long long)info[i].nr_pages*(XC_PAGE_SIZE/1024), "cpu_time", (long long)info[i].cpu_time, "maxmem_kb", (long long)info[i].max_memkb, "ssidref", (int)info[i].ssidref, "shutdown_reason", info[i].shutdown_reason, "cpupool", (int)info[i].cpupool); pyhandle = PyList_New(sizeof(xen_domain_handle_t)); if ( (pyhandle == NULL) || (info_dict == NULL) ) { Py_DECREF(list); if ( pyhandle != NULL ) { Py_DECREF(pyhandle); } if ( info_dict != NULL ) { Py_DECREF(info_dict); } free(info); return NULL; } for ( j = 0; j < sizeof(xen_domain_handle_t); j++ ) PyList_SetItem(pyhandle, j, PyLongOrInt_FromLong(info[i].handle[j])); PyDict_SetItemString(info_dict, "handle", pyhandle); Py_DECREF(pyhandle); PyList_SetItem(list, i, info_dict); } free(info); return list; } static PyObject *pyxc_vcpu_getinfo(XcObject *self, PyObject *args, PyObject *kwds) { PyObject *info_dict, *cpulist; uint32_t dom, vcpu = 0; xc_vcpuinfo_t info; int rc, i; xc_cpumap_t cpumap; int nr_cpus; static char *kwd_list[] = { "domid", "vcpu", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list, &dom, &vcpu) ) return NULL; nr_cpus = xc_get_max_cpus(self->xc_handle); if ( nr_cpus < 0 ) return pyxc_error_to_exception(self->xc_handle); rc = xc_vcpu_getinfo(self->xc_handle, dom, vcpu, &info); if ( rc < 0 ) return pyxc_error_to_exception(self->xc_handle); cpumap = xc_cpumap_alloc(self->xc_handle); if(cpumap == NULL) return pyxc_error_to_exception(self->xc_handle); rc = xc_vcpu_getaffinity(self->xc_handle, dom, vcpu, cpumap, NULL, XEN_VCPUAFFINITY_HARD); if ( rc < 0 ) { free(cpumap); return pyxc_error_to_exception(self->xc_handle); } info_dict = Py_BuildValue("{s:i,s:i,s:i,s:L,s:i}", "online", info.online, "blocked", info.blocked, "running", info.running, "cpu_time", info.cpu_time, "cpu", info.cpu); cpulist = PyList_New(0); for ( i = 0; i < nr_cpus; i++ ) { if (*(cpumap + i / 8) & 1 ) { PyObject *pyint = PyLongOrInt_FromLong(i); PyList_Append(cpulist, pyint); Py_DECREF(pyint); } cpumap[i / 8] >>= 1; } PyDict_SetItemString(info_dict, "cpumap", cpulist); Py_DECREF(cpulist); free(cpumap); return info_dict; } static PyObject *pyxc_hvm_param_get(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; int param; uint64_t value; static char *kwd_list[] = { "domid", "param", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwd_list, &dom, ¶m) ) return NULL; if ( xc_hvm_param_get(self->xc_handle, dom, param, &value) != 0 ) return pyxc_error_to_exception(self->xc_handle); return PyLong_FromUnsignedLongLong(value); } static PyObject *pyxc_hvm_param_set(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; int param; uint64_t value; static char *kwd_list[] = { "domid", "param", "value", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiL", kwd_list, &dom, ¶m, &value) ) return NULL; if ( xc_hvm_param_set(self->xc_handle, dom, param, value) != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static int token_value(char *token) { token = strchr(token, 'x') + 1; return strtol(token, NULL, 16); } static int next_bdf(char **str, int *seg, int *bus, int *dev, int *func) { char *token; if ( !(*str) || !strchr(*str, ',') ) return 0; token = *str; *seg = token_value(token); token = strchr(token, ',') + 1; *bus = token_value(token); token = strchr(token, ',') + 1; *dev = token_value(token); token = strchr(token, ',') + 1; *func = token_value(token); token = strchr(token, ','); *str = token ? token + 1 : NULL; return 1; } static PyObject *pyxc_test_assign_device(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; char *pci_str; int32_t sbdf = 0; int seg, bus, dev, func; static char *kwd_list[] = { "domid", "pci", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is", kwd_list, &dom, &pci_str) ) return NULL; while ( next_bdf(&pci_str, &seg, &bus, &dev, &func) ) { sbdf = seg << 16; sbdf |= (bus & 0xff) << 8; sbdf |= (dev & 0x1f) << 3; sbdf |= (func & 0x7); if ( xc_test_assign_device(self->xc_handle, dom, sbdf) != 0 ) { if (errno == ENOSYS) sbdf = -1; break; } sbdf = 0; } return Py_BuildValue("i", sbdf); } static PyObject *pyxc_assign_device(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; char *pci_str; int32_t sbdf = 0; int seg, bus, dev, func; static char *kwd_list[] = { "domid", "pci", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is", kwd_list, &dom, &pci_str) ) return NULL; while ( next_bdf(&pci_str, &seg, &bus, &dev, &func) ) { sbdf = seg << 16; sbdf |= (bus & 0xff) << 8; sbdf |= (dev & 0x1f) << 3; sbdf |= (func & 0x7); if ( xc_assign_device(self->xc_handle, dom, sbdf, 0) != 0 ) { if (errno == ENOSYS) sbdf = -1; break; } sbdf = 0; } return Py_BuildValue("i", sbdf); } static PyObject *pyxc_deassign_device(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; char *pci_str; int32_t sbdf = 0; int seg, bus, dev, func; static char *kwd_list[] = { "domid", "pci", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is", kwd_list, &dom, &pci_str) ) return NULL; while ( next_bdf(&pci_str, &seg, &bus, &dev, &func) ) { sbdf = seg << 16; sbdf |= (bus & 0xff) << 8; sbdf |= (dev & 0x1f) << 3; sbdf |= (func & 0x7); if ( xc_deassign_device(self->xc_handle, dom, sbdf) != 0 ) { if (errno == ENOSYS) sbdf = -1; break; } sbdf = 0; } return Py_BuildValue("i", sbdf); } static PyObject *pyxc_get_device_group(XcObject *self, PyObject *args) { uint32_t sbdf; uint32_t max_sdevs, num_sdevs; int domid, seg, bus, dev, func, rc, i; PyObject *Pystr; char *group_str; char dev_str[9]; uint32_t *sdev_array; if ( !PyArg_ParseTuple(args, "iiiii", &domid, &seg, &bus, &dev, &func) ) return NULL; /* Maximum allowed siblings device number per group */ max_sdevs = 1024; sdev_array = calloc(max_sdevs, sizeof(*sdev_array)); if (sdev_array == NULL) return PyErr_NoMemory(); sbdf = seg << 16; sbdf |= (bus & 0xff) << 8; sbdf |= (dev & 0x1f) << 3; sbdf |= (func & 0x7); rc = xc_get_device_group(self->xc_handle, domid, sbdf, max_sdevs, &num_sdevs, sdev_array); if ( rc < 0 ) { free(sdev_array); return pyxc_error_to_exception(self->xc_handle); } if ( !num_sdevs ) { free(sdev_array); return Py_BuildValue("s", ""); } group_str = calloc(num_sdevs, sizeof(dev_str)); if (group_str == NULL) { free(sdev_array); return PyErr_NoMemory(); } for ( i = 0; i < num_sdevs; i++ ) { bus = (sdev_array[i] >> 16) & 0xff; dev = (sdev_array[i] >> 11) & 0x1f; func = (sdev_array[i] >> 8) & 0x7; snprintf(dev_str, sizeof(dev_str), "%02x:%02x.%x,", bus, dev, func); strcat(group_str, dev_str); } Pystr = Py_BuildValue("s", group_str); free(sdev_array); free(group_str); return Pystr; } #if defined(__i386__) || defined(__x86_64__) static void pyxc_dom_extract_cpuid(PyObject *config, char **regs) { const char *regs_extract[4] = { "eax", "ebx", "ecx", "edx" }; PyObject *obj; int i; memset(regs, 0, 4*sizeof(*regs)); if ( !PyDict_Check(config) ) return; for ( i = 0; i < 4; i++ ) if ( (obj = PyDict_GetItemString(config, regs_extract[i])) != NULL ) regs[i] = PyBytes_AS_STRING(obj); } static PyObject *pyxc_create_cpuid_dict(char **regs) { const char *regs_extract[4] = { "eax", "ebx", "ecx", "edx" }; PyObject *dict; int i; dict = PyDict_New(); for ( i = 0; i < 4; i++ ) { if ( regs[i] == NULL ) continue; PyDict_SetItemString(dict, regs_extract[i], PyBytes_FromString(regs[i])); free(regs[i]); regs[i] = NULL; } return dict; } static PyObject *pyxc_dom_check_cpuid(XcObject *self, PyObject *args) { PyObject *sub_input, *config; unsigned int input[2]; char *regs[4], *regs_transform[4]; if ( !PyArg_ParseTuple(args, "iOO", &input[0], &sub_input, &config) ) return NULL; pyxc_dom_extract_cpuid(config, regs); input[1] = XEN_CPUID_INPUT_UNUSED; if ( PyLong_Check(sub_input) ) input[1] = PyLong_AsUnsignedLong(sub_input); if ( xc_cpuid_check(self->xc_handle, input, (const char **)regs, regs_transform) ) return pyxc_error_to_exception(self->xc_handle); return pyxc_create_cpuid_dict(regs_transform); } static PyObject *pyxc_dom_set_policy_cpuid(XcObject *self, PyObject *args) { int domid; if ( !PyArg_ParseTuple(args, "i", &domid) ) return NULL; if ( xc_cpuid_apply_policy(self->xc_handle, domid, NULL, 0) ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_dom_set_cpuid(XcObject *self, PyObject *args) { PyObject *sub_input, *config; unsigned int domid, input[2]; char *regs[4], *regs_transform[4]; if ( !PyArg_ParseTuple(args, "IIOO", &domid, &input[0], &sub_input, &config) ) return NULL; pyxc_dom_extract_cpuid(config, regs); input[1] = XEN_CPUID_INPUT_UNUSED; if ( PyLong_Check(sub_input) ) input[1] = PyLong_AsUnsignedLong(sub_input); if ( xc_cpuid_set(self->xc_handle, domid, input, (const char **)regs, regs_transform) ) return pyxc_error_to_exception(self->xc_handle); return pyxc_create_cpuid_dict(regs_transform); } static PyObject *pyxc_dom_set_machine_address_size(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom, width; if (!PyArg_ParseTuple(args, "ii", &dom, &width)) return NULL; if (xc_domain_set_machine_address_size(self->xc_handle, dom, width) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_dom_suppress_spurious_page_faults(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; if (!PyArg_ParseTuple(args, "i", &dom)) return NULL; if (xc_domain_suppress_spurious_page_faults(self->xc_handle, dom) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } #endif /* __i386__ || __x86_64__ */ static PyObject *pyxc_gnttab_hvm_seed(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom, console_domid, xenstore_domid; unsigned long xenstore_gmfn = 0; unsigned long console_gmfn = 0; static char *kwd_list[] = { "domid", "console_gmfn", "xenstore_gmfn", "console_domid", "xenstore_domid", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiiii", kwd_list, &dom, &console_gmfn, &xenstore_gmfn, &console_domid, &xenstore_domid) ) return NULL; if ( xc_dom_gnttab_hvm_seed(self->xc_handle, dom, console_gmfn, xenstore_gmfn, console_domid, xenstore_domid) != 0 ) return pyxc_error_to_exception(self->xc_handle); return Py_None; } static PyObject *pyxc_evtchn_alloc_unbound(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom, remote_dom; int port; static char *kwd_list[] = { "domid", "remote_dom", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwd_list, &dom, &remote_dom) ) return NULL; if ( (port = xc_evtchn_alloc_unbound(self->xc_handle, dom, remote_dom)) < 0 ) return pyxc_error_to_exception(self->xc_handle); return PyLongOrInt_FromLong(port); } static PyObject *pyxc_evtchn_reset(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; static char *kwd_list[] = { "dom", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &dom) ) return NULL; if ( xc_evtchn_reset(self->xc_handle, dom) < 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_physdev_map_pirq(PyObject *self, PyObject *args, PyObject *kwds) { XcObject *xc = (XcObject *)self; uint32_t dom; int index, pirq, ret; static char *kwd_list[] = {"domid", "index", "pirq", NULL}; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iii", kwd_list, &dom, &index, &pirq) ) return NULL; ret = xc_physdev_map_pirq(xc->xc_handle, dom, index, &pirq); if ( ret != 0 ) return pyxc_error_to_exception(xc->xc_handle); return PyLong_FromUnsignedLong(pirq); } static PyObject *pyxc_physdev_pci_access_modify(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; int bus, dev, func, enable, ret; static char *kwd_list[] = { "domid", "bus", "dev", "func", "enable", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiiii", kwd_list, &dom, &bus, &dev, &func, &enable) ) return NULL; ret = xc_physdev_pci_access_modify( self->xc_handle, dom, bus, dev, func, enable); if ( ret != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_readconsolering(XcObject *self, PyObject *args, PyObject *kwds) { unsigned int clear = 0, index = 0, incremental = 0; unsigned int count = 16384 + 1, size = count; char *str, *ptr; PyObject *obj; int ret; static char *kwd_list[] = { "clear", "index", "incremental", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwd_list, &clear, &index, &incremental) || !(str = malloc(size)) ) return NULL; ret = xc_readconsolering(self->xc_handle, str, &count, clear, incremental, &index); if ( ret < 0 ) { free(str); return pyxc_error_to_exception(self->xc_handle); } while ( !incremental && count == size && ret >= 0 ) { size += count - 1; if ( size < count ) break; ptr = realloc(str, size); if ( !ptr ) break; str = ptr + count; count = size - count; ret = xc_readconsolering(self->xc_handle, str, &count, clear, 1, &index); count += str - ptr; str = ptr; } obj = PyBytes_FromStringAndSize(str, count); free(str); return obj; } static unsigned long pages_to_kib(unsigned long pages) { return pages * (XC_PAGE_SIZE / 1024); } static PyObject *pyxc_pages_to_kib(XcObject *self, PyObject *args) { unsigned long pages; if (!PyArg_ParseTuple(args, "l", &pages)) return NULL; return PyLong_FromUnsignedLong(pages_to_kib(pages)); } static PyObject *pyxc_physinfo(XcObject *self) { xc_physinfo_t pinfo; char cpu_cap[128], virt_caps[128], *p; int i; const char *virtcap_names[] = { "hvm", "hvm_directio" }; if ( xc_physinfo(self->xc_handle, &pinfo) != 0 ) return pyxc_error_to_exception(self->xc_handle); p = cpu_cap; *p = '\0'; for ( i = 0; i < sizeof(pinfo.hw_cap)/4; i++ ) p += sprintf(p, "%08x:", pinfo.hw_cap[i]); *(p-1) = 0; p = virt_caps; *p = '\0'; for ( i = 0; i < 2; i++ ) if ( (pinfo.capabilities >> i) & 1 ) p += sprintf(p, "%s ", virtcap_names[i]); if ( p != virt_caps ) *(p-1) = '\0'; return Py_BuildValue("{s:i,s:i,s:i,s:i,s:l,s:l,s:l,s:i,s:s,s:s}", "nr_nodes", pinfo.nr_nodes, "threads_per_core", pinfo.threads_per_core, "cores_per_socket", pinfo.cores_per_socket, "nr_cpus", pinfo.nr_cpus, "total_memory", pages_to_kib(pinfo.total_pages), "free_memory", pages_to_kib(pinfo.free_pages), "scrub_memory", pages_to_kib(pinfo.scrub_pages), "cpu_khz", pinfo.cpu_khz, "hw_caps", cpu_cap, "virt_caps", virt_caps); } static PyObject *pyxc_getcpuinfo(XcObject *self, PyObject *args, PyObject *kwds) { xc_cpuinfo_t *cpuinfo, *cpuinfo_ptr; PyObject *cpuinfo_list_obj, *cpuinfo_obj; int max_cpus, nr_cpus, ret, i; static char *kwd_list[] = { "max_cpus", NULL }; static char kwd_type[] = "i"; if(!PyArg_ParseTupleAndKeywords(args, kwds, kwd_type, kwd_list, &max_cpus)) return NULL; cpuinfo = malloc(sizeof(xc_cpuinfo_t) * max_cpus); if (!cpuinfo) return NULL; ret = xc_getcpuinfo(self->xc_handle, max_cpus, cpuinfo, &nr_cpus); if (ret != 0) { free(cpuinfo); return pyxc_error_to_exception(self->xc_handle); } cpuinfo_list_obj = PyList_New(0); cpuinfo_ptr = cpuinfo; for (i = 0; i < nr_cpus; i++) { cpuinfo_obj = Py_BuildValue("{s:k}", "idletime", cpuinfo_ptr->idletime); PyList_Append(cpuinfo_list_obj, cpuinfo_obj); Py_DECREF(cpuinfo_obj); cpuinfo_ptr++; } free(cpuinfo); return cpuinfo_list_obj; } static PyObject *pyxc_topologyinfo(XcObject *self) { xc_cputopo_t *cputopo = NULL; unsigned i, num_cpus = 0; PyObject *ret_obj = NULL; PyObject *cpu_to_core_obj, *cpu_to_socket_obj, *cpu_to_node_obj; if ( xc_cputopoinfo(self->xc_handle, &num_cpus, NULL) != 0 ) goto out; cputopo = calloc(num_cpus, sizeof(*cputopo)); if ( cputopo == NULL ) goto out; if ( xc_cputopoinfo(self->xc_handle, &num_cpus, cputopo) != 0 ) goto out; /* Construct cpu-to-* lists. */ cpu_to_core_obj = PyList_New(0); cpu_to_socket_obj = PyList_New(0); cpu_to_node_obj = PyList_New(0); for ( i = 0; i < num_cpus; i++ ) { if ( cputopo[i].core == XEN_INVALID_CORE_ID ) { PyList_Append(cpu_to_core_obj, Py_None); } else { PyObject *pyint = PyLongOrInt_FromLong(cputopo[i].core); PyList_Append(cpu_to_core_obj, pyint); Py_DECREF(pyint); } if ( cputopo[i].socket == XEN_INVALID_SOCKET_ID ) { PyList_Append(cpu_to_socket_obj, Py_None); } else { PyObject *pyint = PyLongOrInt_FromLong(cputopo[i].socket); PyList_Append(cpu_to_socket_obj, pyint); Py_DECREF(pyint); } if ( cputopo[i].node == XEN_INVALID_NODE_ID ) { PyList_Append(cpu_to_node_obj, Py_None); } else { PyObject *pyint = PyLongOrInt_FromLong(cputopo[i].node); PyList_Append(cpu_to_node_obj, pyint); Py_DECREF(pyint); } } ret_obj = Py_BuildValue("{s:i}", "max_cpu_index", num_cpus + 1); PyDict_SetItemString(ret_obj, "cpu_to_core", cpu_to_core_obj); Py_DECREF(cpu_to_core_obj); PyDict_SetItemString(ret_obj, "cpu_to_socket", cpu_to_socket_obj); Py_DECREF(cpu_to_socket_obj); PyDict_SetItemString(ret_obj, "cpu_to_node", cpu_to_node_obj); Py_DECREF(cpu_to_node_obj); out: free(cputopo); return ret_obj ? ret_obj : pyxc_error_to_exception(self->xc_handle); } static PyObject *pyxc_numainfo(XcObject *self) { unsigned i, j, num_nodes = 0; uint64_t free_heap; PyObject *ret_obj = NULL, *node_to_node_dist_list_obj; PyObject *node_to_memsize_obj, *node_to_memfree_obj; PyObject *node_to_dma32_mem_obj, *node_to_node_dist_obj; xc_meminfo_t *meminfo = NULL; uint32_t *distance = NULL; if ( xc_numainfo(self->xc_handle, &num_nodes, NULL, NULL) != 0 ) goto out; meminfo = calloc(num_nodes, sizeof(*meminfo)); distance = calloc(num_nodes * num_nodes, sizeof(*distance)); if ( (meminfo == NULL) || (distance == NULL) ) goto out; if ( xc_numainfo(self->xc_handle, &num_nodes, meminfo, distance) != 0 ) goto out; /* Construct node-to-* lists. */ node_to_memsize_obj = PyList_New(0); node_to_memfree_obj = PyList_New(0); node_to_dma32_mem_obj = PyList_New(0); node_to_node_dist_list_obj = PyList_New(0); for ( i = 0; i < num_nodes; i++ ) { PyObject *pyint; unsigned invalid_node; /* Total Memory */ pyint = PyLongOrInt_FromLong(meminfo[i].memsize >> 20); /* MB */ PyList_Append(node_to_memsize_obj, pyint); Py_DECREF(pyint); /* Free Memory */ pyint = PyLongOrInt_FromLong(meminfo[i].memfree >> 20); /* MB */ PyList_Append(node_to_memfree_obj, pyint); Py_DECREF(pyint); /* DMA memory. */ xc_availheap(self->xc_handle, 0, 32, i, &free_heap); pyint = PyLongOrInt_FromLong(free_heap >> 20); /* MB */ PyList_Append(node_to_dma32_mem_obj, pyint); Py_DECREF(pyint); /* Node to Node Distance */ node_to_node_dist_obj = PyList_New(0); invalid_node = (meminfo[i].memsize == XEN_INVALID_MEM_SZ); for ( j = 0; j < num_nodes; j++ ) { uint32_t dist = distance[i * num_nodes + j]; if ( invalid_node || (dist == XEN_INVALID_NODE_DIST) ) { PyList_Append(node_to_node_dist_obj, Py_None); } else { pyint = PyLongOrInt_FromLong(dist); PyList_Append(node_to_node_dist_obj, pyint); Py_DECREF(pyint); } } PyList_Append(node_to_node_dist_list_obj, node_to_node_dist_obj); Py_DECREF(node_to_node_dist_obj); } ret_obj = Py_BuildValue("{s:i}", "max_node_index", num_nodes + 1); PyDict_SetItemString(ret_obj, "node_memsize", node_to_memsize_obj); Py_DECREF(node_to_memsize_obj); PyDict_SetItemString(ret_obj, "node_memfree", node_to_memfree_obj); Py_DECREF(node_to_memfree_obj); PyDict_SetItemString(ret_obj, "node_to_dma32_mem", node_to_dma32_mem_obj); Py_DECREF(node_to_dma32_mem_obj); PyDict_SetItemString(ret_obj, "node_to_node_dist", node_to_node_dist_list_obj); Py_DECREF(node_to_node_dist_list_obj); out: free(meminfo); free(distance); return ret_obj ? ret_obj : pyxc_error_to_exception(self->xc_handle); } static PyObject *pyxc_xeninfo(XcObject *self) { xen_extraversion_t xen_extra; xen_compile_info_t xen_cc; xen_changeset_info_t xen_chgset; xen_capabilities_info_t xen_caps; xen_platform_parameters_t p_parms; xen_commandline_t xen_commandline; long xen_version; long xen_pagesize; char str[128]; xen_version = xc_version(self->xc_handle, XENVER_version, NULL); if ( xc_version(self->xc_handle, XENVER_extraversion, &xen_extra) != 0 ) return pyxc_error_to_exception(self->xc_handle); if ( xc_version(self->xc_handle, XENVER_compile_info, &xen_cc) != 0 ) return pyxc_error_to_exception(self->xc_handle); if ( xc_version(self->xc_handle, XENVER_changeset, &xen_chgset) != 0 ) return pyxc_error_to_exception(self->xc_handle); if ( xc_version(self->xc_handle, XENVER_capabilities, &xen_caps) != 0 ) return pyxc_error_to_exception(self->xc_handle); if ( xc_version(self->xc_handle, XENVER_platform_parameters, &p_parms) != 0 ) return pyxc_error_to_exception(self->xc_handle); if ( xc_version(self->xc_handle, XENVER_commandline, &xen_commandline) != 0 ) return pyxc_error_to_exception(self->xc_handle); snprintf(str, sizeof(str), "virt_start=0x%"PRI_xen_ulong, p_parms.virt_start); xen_pagesize = xc_version(self->xc_handle, XENVER_pagesize, NULL); if (xen_pagesize < 0 ) return pyxc_error_to_exception(self->xc_handle); return Py_BuildValue("{s:i,s:i,s:s,s:s,s:i,s:s,s:s,s:s,s:s,s:s,s:s,s:s}", "xen_major", xen_version >> 16, "xen_minor", (xen_version & 0xffff), "xen_extra", xen_extra, "xen_caps", xen_caps, "xen_pagesize", xen_pagesize, "platform_params", str, "xen_changeset", xen_chgset, "xen_commandline", xen_commandline, "cc_compiler", xen_cc.compiler, "cc_compile_by", xen_cc.compile_by, "cc_compile_domain", xen_cc.compile_domain, "cc_compile_date", xen_cc.compile_date); } static PyObject *pyxc_shadow_control(PyObject *self, PyObject *args, PyObject *kwds) { XcObject *xc = (XcObject *)self; uint32_t dom; int op=0; static char *kwd_list[] = { "dom", "op", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list, &dom, &op) ) return NULL; if ( xc_shadow_control(xc->xc_handle, dom, op, NULL, 0, NULL, 0, NULL) < 0 ) return pyxc_error_to_exception(xc->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_shadow_mem_control(PyObject *self, PyObject *args, PyObject *kwds) { XcObject *xc = (XcObject *)self; int op; uint32_t dom; int mbarg = -1; unsigned long mb; static char *kwd_list[] = { "dom", "mb", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list, &dom, &mbarg) ) return NULL; if ( mbarg < 0 ) op = XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION; else { mb = mbarg; op = XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION; } if ( xc_shadow_control(xc->xc_handle, dom, op, NULL, 0, &mb, 0, NULL) < 0 ) return pyxc_error_to_exception(xc->xc_handle); mbarg = mb; return Py_BuildValue("i", mbarg); } static PyObject *pyxc_sched_id_get(XcObject *self) { int sched_id; if (xc_sched_id(self->xc_handle, &sched_id) != 0) return PyErr_SetFromErrno(xc_error_obj); return Py_BuildValue("i", sched_id); } static PyObject *pyxc_sched_credit_domain_set(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t domid; uint16_t weight; uint16_t cap; static char *kwd_list[] = { "domid", "weight", "cap", NULL }; static char kwd_type[] = "I|HH"; struct xen_domctl_sched_credit sdom; weight = 0; cap = (uint16_t)~0U; if( !PyArg_ParseTupleAndKeywords(args, kwds, kwd_type, kwd_list, &domid, &weight, &cap) ) return NULL; sdom.weight = weight; sdom.cap = cap; if ( xc_sched_credit_domain_set(self->xc_handle, domid, &sdom) != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_sched_credit_domain_get(XcObject *self, PyObject *args) { uint32_t domid; struct xen_domctl_sched_credit sdom; if( !PyArg_ParseTuple(args, "I", &domid) ) return NULL; if ( xc_sched_credit_domain_get(self->xc_handle, domid, &sdom) != 0 ) return pyxc_error_to_exception(self->xc_handle); return Py_BuildValue("{s:H,s:H}", "weight", sdom.weight, "cap", sdom.cap); } static PyObject *pyxc_sched_credit2_domain_set(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t domid; uint16_t weight; static char *kwd_list[] = { "domid", "weight", NULL }; static char kwd_type[] = "I|H"; struct xen_domctl_sched_credit2 sdom; weight = 0; if( !PyArg_ParseTupleAndKeywords(args, kwds, kwd_type, kwd_list, &domid, &weight) ) return NULL; sdom.weight = weight; if ( xc_sched_credit2_domain_set(self->xc_handle, domid, &sdom) != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_sched_credit2_domain_get(XcObject *self, PyObject *args) { uint32_t domid; struct xen_domctl_sched_credit2 sdom; if( !PyArg_ParseTuple(args, "I", &domid) ) return NULL; if ( xc_sched_credit2_domain_get(self->xc_handle, domid, &sdom) != 0 ) return pyxc_error_to_exception(self->xc_handle); return Py_BuildValue("{s:H}", "weight", sdom.weight); } static PyObject *pyxc_domain_setmaxmem(XcObject *self, PyObject *args) { uint32_t dom; unsigned int maxmem_kb; if (!PyArg_ParseTuple(args, "ii", &dom, &maxmem_kb)) return NULL; if (xc_domain_setmaxmem(self->xc_handle, dom, maxmem_kb) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_set_target_mem(XcObject *self, PyObject *args) { uint32_t dom; unsigned int mem_kb, mem_pages; if (!PyArg_ParseTuple(args, "ii", &dom, &mem_kb)) return NULL; mem_pages = mem_kb / 4; if (xc_domain_set_pod_target(self->xc_handle, dom, mem_pages, NULL, NULL, NULL) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_set_memmap_limit(XcObject *self, PyObject *args) { uint32_t dom; unsigned int maplimit_kb; if ( !PyArg_ParseTuple(args, "ii", &dom, &maplimit_kb) ) return NULL; if ( xc_domain_set_memmap_limit(self->xc_handle, dom, maplimit_kb) != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_ioport_permission(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; int first_port, nr_ports, allow_access, ret; static char *kwd_list[] = { "domid", "first_port", "nr_ports", "allow_access", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiii", kwd_list, &dom, &first_port, &nr_ports, &allow_access) ) return NULL; ret = xc_domain_ioport_permission( self->xc_handle, dom, first_port, nr_ports, allow_access); if ( ret != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_irq_permission(PyObject *self, PyObject *args, PyObject *kwds) { XcObject *xc = (XcObject *)self; uint32_t dom; int pirq, allow_access, ret; static char *kwd_list[] = { "domid", "pirq", "allow_access", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iii", kwd_list, &dom, &pirq, &allow_access) ) return NULL; ret = xc_domain_irq_permission( xc->xc_handle, dom, pirq, allow_access); if ( ret != 0 ) return pyxc_error_to_exception(xc->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_iomem_permission(PyObject *self, PyObject *args, PyObject *kwds) { XcObject *xc = (XcObject *)self; uint32_t dom; unsigned long first_pfn, nr_pfns, allow_access, ret; static char *kwd_list[] = { "domid", "first_pfn", "nr_pfns", "allow_access", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "illi", kwd_list, &dom, &first_pfn, &nr_pfns, &allow_access) ) return NULL; ret = xc_domain_iomem_permission( xc->xc_handle, dom, first_pfn, nr_pfns, allow_access); if ( ret != 0 ) return pyxc_error_to_exception(xc->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_set_time_offset(XcObject *self, PyObject *args) { uint32_t dom; int32_t offset; if (!PyArg_ParseTuple(args, "ii", &dom, &offset)) return NULL; if (xc_domain_set_time_offset(self->xc_handle, dom, offset) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_set_tsc_info(XcObject *self, PyObject *args) { uint32_t dom, tsc_mode; if (!PyArg_ParseTuple(args, "ii", &dom, &tsc_mode)) return NULL; if (xc_domain_set_tsc_info(self->xc_handle, dom, tsc_mode, 0, 0, 0) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_disable_migrate(XcObject *self, PyObject *args) { uint32_t dom; if (!PyArg_ParseTuple(args, "i", &dom)) return NULL; if (xc_domain_disable_migrate(self->xc_handle, dom) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_domain_send_trigger(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t dom; int trigger, vcpu = 0; static char *kwd_list[] = { "domid", "trigger", "vcpu", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ii|i", kwd_list, &dom, &trigger, &vcpu) ) return NULL; if (xc_domain_send_trigger(self->xc_handle, dom, trigger, vcpu) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_send_debug_keys(XcObject *self, PyObject *args, PyObject *kwds) { char *keys; static char *kwd_list[] = { "keys", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "s", kwd_list, &keys) ) return NULL; if ( xc_send_debug_keys(self->xc_handle, keys) != 0 ) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *dom_op(XcObject *self, PyObject *args, int (*fn)(xc_interface*, uint32_t)) { uint32_t dom; if (!PyArg_ParseTuple(args, "i", &dom)) return NULL; if (fn(self->xc_handle, dom) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_tmem_control(XcObject *self, PyObject *args, PyObject *kwds) { int32_t pool_id; uint32_t subop; uint32_t cli_id; uint32_t len; uint32_t arg; char *buf; char _buffer[32768], *buffer = _buffer; int rc; static char *kwd_list[] = { "pool_id", "subop", "cli_id", "arg1", "arg2", "buf", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiiiis", kwd_list, &pool_id, &subop, &cli_id, &len, &arg, &buf) ) return NULL; if ( (subop == XEN_SYSCTL_TMEM_OP_LIST) && (len > 32768) ) len = 32768; if ( (rc = xc_tmem_control(self->xc_handle, pool_id, subop, cli_id, len, arg, buffer)) < 0 ) return Py_BuildValue("i", rc); switch (subop) { case XEN_SYSCTL_TMEM_OP_LIST: return Py_BuildValue("s", buffer); case XEN_SYSCTL_TMEM_OP_FLUSH: return Py_BuildValue("i", rc); case XEN_SYSCTL_TMEM_OP_QUERY_FREEABLE_MB: return Py_BuildValue("i", rc); case XEN_SYSCTL_TMEM_OP_THAW: case XEN_SYSCTL_TMEM_OP_FREEZE: case XEN_SYSCTL_TMEM_OP_DESTROY: default: break; } Py_INCREF(zero); return zero; } static PyObject *pyxc_tmem_shared_auth(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t cli_id; uint32_t arg1; char *uuid_str; int rc; static char *kwd_list[] = { "cli_id", "uuid_str", "arg1", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "isi", kwd_list, &cli_id, &uuid_str, &arg1) ) return NULL; if ( (rc = xc_tmem_auth(self->xc_handle, cli_id, uuid_str, arg1)) < 0 ) return Py_BuildValue("i", rc); Py_INCREF(zero); return zero; } static PyObject *pyxc_dom_set_memshr(XcObject *self, PyObject *args) { uint32_t dom; int enable; if (!PyArg_ParseTuple(args, "ii", &dom, &enable)) return NULL; if (xc_memshr_control(self->xc_handle, dom, enable) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *cpumap_to_cpulist(XcObject *self, xc_cpumap_t cpumap) { PyObject *cpulist = NULL; int i; int nr_cpus; nr_cpus = xc_get_max_cpus(self->xc_handle); if ( nr_cpus < 0 ) return pyxc_error_to_exception(self->xc_handle); cpulist = PyList_New(0); for ( i = 0; i < nr_cpus; i++ ) { if ( *cpumap & (1 << (i % 8)) ) { PyObject* pyint = PyLongOrInt_FromLong(i); PyList_Append(cpulist, pyint); Py_DECREF(pyint); } if ( (i % 8) == 7 ) cpumap++; } return cpulist; } static PyObject *pyxc_cpupool_create(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t cpupool = XC_CPUPOOL_POOLID_ANY, sched = XEN_SCHEDULER_CREDIT; static char *kwd_list[] = { "pool", "sched", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwd_list, &cpupool, &sched)) return NULL; if ( xc_cpupool_create(self->xc_handle, &cpupool, sched) < 0 ) return pyxc_error_to_exception(self->xc_handle); return PyLongOrInt_FromLong(cpupool); } static PyObject *pyxc_cpupool_destroy(XcObject *self, PyObject *args) { uint32_t cpupool; if (!PyArg_ParseTuple(args, "i", &cpupool)) return NULL; if (xc_cpupool_destroy(self->xc_handle, cpupool) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_cpupool_getinfo(XcObject *self) { PyObject *list, *info_dict; uint32_t pool; xc_cpupoolinfo_t *info; list = PyList_New(0); for (pool = 0;;) { info = xc_cpupool_getinfo(self->xc_handle, pool); if (info == NULL) break; info_dict = Py_BuildValue( "{s:i,s:i,s:i,s:N}", "cpupool", (int)info->cpupool_id, "sched", info->sched_id, "n_dom", info->n_dom, "cpulist", cpumap_to_cpulist(self, info->cpumap)); pool = info->cpupool_id + 1; xc_cpupool_infofree(self->xc_handle, info); if ( info_dict == NULL ) { Py_DECREF(list); return NULL; } PyList_Append(list, info_dict); Py_DECREF(info_dict); } return list; } static PyObject *pyxc_cpupool_addcpu(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t cpupool; int cpu = -1; static char *kwd_list[] = { "cpupool", "cpu", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list, &cpupool, &cpu) ) return NULL; if (xc_cpupool_addcpu(self->xc_handle, cpupool, cpu) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_cpupool_removecpu(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t cpupool; int cpu = -1; static char *kwd_list[] = { "cpupool", "cpu", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list, &cpupool, &cpu) ) return NULL; if (xc_cpupool_removecpu(self->xc_handle, cpupool, cpu) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_cpupool_movedomain(XcObject *self, PyObject *args, PyObject *kwds) { uint32_t cpupool, domid; static char *kwd_list[] = { "cpupool", "domid", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwd_list, &cpupool, &domid) ) return NULL; if (xc_cpupool_movedomain(self->xc_handle, cpupool, domid) != 0) return pyxc_error_to_exception(self->xc_handle); Py_INCREF(zero); return zero; } static PyObject *pyxc_cpupool_freeinfo(XcObject *self) { xc_cpumap_t cpumap; PyObject *info = NULL; cpumap = xc_cpupool_freeinfo(self->xc_handle); if (!cpumap) return pyxc_error_to_exception(self->xc_handle); info = cpumap_to_cpulist(self, cpumap); free(cpumap); return info; } static PyObject *pyflask_context_to_sid(PyObject *self, PyObject *args, PyObject *kwds) { xc_interface *xc_handle; char *ctx; uint32_t sid; int ret; static char *kwd_list[] = { "context", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "s", kwd_list, &ctx) ) return NULL; xc_handle = xc_interface_open(0,0,0); if (!xc_handle) { return PyErr_SetFromErrno(xc_error_obj); } ret = xc_flask_context_to_sid(xc_handle, ctx, strlen(ctx), &sid); xc_interface_close(xc_handle); if ( ret != 0 ) { errno = -ret; return PyErr_SetFromErrno(xc_error_obj); } return PyLongOrInt_FromLong(sid); } static PyObject *pyflask_sid_to_context(PyObject *self, PyObject *args, PyObject *kwds) { xc_interface *xc_handle; uint32_t sid; char ctx[FLASK_CTX_LEN]; uint32_t ctx_len = FLASK_CTX_LEN; int ret; static char *kwd_list[] = { "sid", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &sid) ) return NULL; xc_handle = xc_interface_open(0,0,0); if (!xc_handle) { return PyErr_SetFromErrno(xc_error_obj); } ret = xc_flask_sid_to_context(xc_handle, sid, ctx, ctx_len); xc_interface_close(xc_handle); if ( ret != 0 ) { errno = -ret; return PyErr_SetFromErrno(xc_error_obj); } return Py_BuildValue("s", ctx, ctx_len); } static PyObject *pyflask_load(PyObject *self, PyObject *args, PyObject *kwds) { xc_interface *xc_handle; char *policy; uint32_t len; int ret; static char *kwd_list[] = { "policy", NULL }; if( !PyArg_ParseTupleAndKeywords(args, kwds, "s#", kwd_list, &policy, &len) ) return NULL; xc_handle = xc_interface_open(0,0,0); if (!xc_handle) { return PyErr_SetFromErrno(xc_error_obj); } ret = xc_flask_load(xc_handle, policy, len); xc_interface_close(xc_handle); if ( ret != 0 ) { errno = -ret; return PyErr_SetFromErrno(xc_error_obj); } return Py_BuildValue("i", ret); } static PyObject *pyflask_getenforce(PyObject *self) { xc_interface *xc_handle; int ret; xc_handle = xc_interface_open(0,0,0); if (!xc_handle) { return PyErr_SetFromErrno(xc_error_obj); } ret = xc_flask_getenforce(xc_handle); xc_interface_close(xc_handle); if ( ret < 0 ) { errno = -ret; return PyErr_SetFromErrno(xc_error_obj); } return Py_BuildValue("i", ret); } static PyObject *pyflask_setenforce(PyObject *self, PyObject *args, PyObject *kwds) { xc_interface *xc_handle; int mode; int ret; static char *kwd_list[] = { "mode", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &mode) ) return NULL; xc_handle = xc_interface_open(0,0,0); if (!xc_handle) { return PyErr_SetFromErrno(xc_error_obj); } ret = xc_flask_setenforce(xc_handle, mode); xc_interface_close(xc_handle); if ( ret != 0 ) { errno = -ret; return PyErr_SetFromErrno(xc_error_obj); } return Py_BuildValue("i", ret); } static PyObject *pyflask_access(PyObject *self, PyObject *args, PyObject *kwds) { xc_interface *xc_handle; char *tcon, *scon; uint16_t tclass; uint32_t req, allowed, decided, auditallow, auditdeny, seqno; int ret; static char *kwd_list[] = { "src_context", "tar_context", "tar_class", "req_permissions", "decided", "auditallow","auditdeny", "seqno", NULL }; if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ssil|llll", kwd_list, &scon, &tcon, &tclass, &req, &decided, &auditallow, &auditdeny, &seqno) ) return NULL; xc_handle = xc_interface_open(0,0,0); if (!xc_handle) { return PyErr_SetFromErrno(xc_error_obj); } ret = xc_flask_access(xc_handle, scon, tcon, tclass, req, &allowed, &decided, &auditallow, &auditdeny, &seqno); xc_interface_close(xc_handle); if ( ret != 0 ) { errno = -ret; return PyErr_SetFromErrno(xc_error_obj); } return Py_BuildValue("i",ret); } static PyMethodDef pyxc_methods[] = { { "domain_create", (PyCFunction)pyxc_domain_create, METH_VARARGS | METH_KEYWORDS, "\n" "Create a new domain.\n" " dom [int, 0]: Domain identifier to use (allocated if zero).\n" "Returns: [int] new domain identifier; -1 on error.\n" }, { "domain_max_vcpus", (PyCFunction)pyxc_domain_max_vcpus, METH_VARARGS, "\n" "Set the maximum number of VCPUs a domain may create.\n" " dom [int, 0]: Domain identifier to use.\n" " max [int, 0]: New maximum number of VCPUs in domain.\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_dumpcore", (PyCFunction)pyxc_domain_dumpcore, METH_VARARGS, "\n" "Dump core of a domain.\n" " dom [int]: Identifier of domain to dump core of.\n" " corefile [string]: Name of corefile to be created.\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_pause", (PyCFunction)pyxc_domain_pause, METH_VARARGS, "\n" "Temporarily pause execution of a domain.\n" " dom [int]: Identifier of domain to be paused.\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_unpause", (PyCFunction)pyxc_domain_unpause, METH_VARARGS, "\n" "(Re)start execution of a domain.\n" " dom [int]: Identifier of domain to be unpaused.\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_destroy", (PyCFunction)pyxc_domain_destroy, METH_VARARGS, "\n" "Destroy a domain.\n" " dom [int]: Identifier of domain to be destroyed.\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_destroy_hook", (PyCFunction)pyxc_domain_destroy_hook, METH_VARARGS, "\n" "Add a hook for arch stuff before destroy a domain.\n" " dom [int]: Identifier of domain to be destroyed.\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_resume", (PyCFunction)pyxc_domain_resume, METH_VARARGS, "\n" "Resume execution of a suspended domain.\n" " dom [int]: Identifier of domain to be resumed.\n" " fast [int]: Use cooperative resume.\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_shutdown", (PyCFunction)pyxc_domain_shutdown, METH_VARARGS, "\n" "Shutdown a domain.\n" " dom [int, 0]: Domain identifier to use.\n" " reason [int, 0]: Reason for shutdown.\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "vcpu_setaffinity", (PyCFunction)pyxc_vcpu_setaffinity, METH_VARARGS | METH_KEYWORDS, "\n" "Pin a VCPU to a specified set CPUs.\n" " dom [int]: Identifier of domain to which VCPU belongs.\n" " vcpu [int, 0]: VCPU being pinned.\n" " cpumap [list, []]: list of usable CPUs.\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_sethandle", (PyCFunction)pyxc_domain_sethandle, METH_VARARGS, "\n" "Set domain's opaque handle.\n" " dom [int]: Identifier of domain.\n" " handle [list of 16 ints]: New opaque handle.\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_getinfo", (PyCFunction)pyxc_domain_getinfo, METH_VARARGS | METH_KEYWORDS, "\n" "Get information regarding a set of domains, in increasing id order.\n" " first_dom [int, 0]: First domain to retrieve info about.\n" " max_doms [int, 1024]: Maximum number of domains to retrieve info" " about.\n\n" "Returns: [list of dicts] if list length is less than 'max_doms'\n" " parameter then there was an error, or the end of the\n" " domain-id space was reached.\n" " dom [int]: Identifier of domain to which this info pertains\n" " cpu [int]: CPU to which this domain is bound\n" " vcpus [int]: Number of Virtual CPUS in this domain\n" " dying [int]: Bool - is the domain dying?\n" " crashed [int]: Bool - has the domain crashed?\n" " shutdown [int]: Bool - has the domain shut itself down?\n" " paused [int]: Bool - is the domain paused by control software?\n" " blocked [int]: Bool - is the domain blocked waiting for an event?\n" " running [int]: Bool - is the domain currently running?\n" " mem_kb [int]: Memory reservation, in kilobytes\n" " maxmem_kb [int]: Maximum memory limit, in kilobytes\n" " cpu_time [long]: CPU time consumed, in nanoseconds\n" " shutdown_reason [int]: Numeric code from guest OS, explaining " "reason why it shut itself down.\n" " cpupool [int] Id of cpupool domain is bound to.\n" }, { "vcpu_getinfo", (PyCFunction)pyxc_vcpu_getinfo, METH_VARARGS | METH_KEYWORDS, "\n" "Get information regarding a VCPU.\n" " dom [int]: Domain to retrieve info about.\n" " vcpu [int, 0]: VCPU to retrieve info about.\n\n" "Returns: [dict]\n" " online [int]: Bool - Is this VCPU currently online?\n" " blocked [int]: Bool - Is this VCPU blocked waiting for an event?\n" " running [int]: Bool - Is this VCPU currently running on a CPU?\n" " cpu_time [long]: CPU time consumed, in nanoseconds\n" " cpumap [int]: Bitmap of CPUs this VCPU can run on\n" " cpu [int]: CPU that this VCPU is currently bound to\n" }, { "gnttab_hvm_seed", (PyCFunction)pyxc_gnttab_hvm_seed, METH_KEYWORDS, "\n" "Initialise HVM guest grant table.\n" " dom [int]: Identifier of domain to build into.\n" " console_gmfn [int]: \n" " xenstore_gmfn [int]: \n" " console_domid [int]: \n" " xenstore_domid [int]: \n" "Returns: None on sucess. Raises exception on error.\n" }, { "hvm_get_param", (PyCFunction)pyxc_hvm_param_get, METH_VARARGS | METH_KEYWORDS, "\n" "get a parameter of HVM guest OS.\n" " dom [int]: Identifier of domain to build into.\n" " param [int]: No. of HVM param.\n" "Returns: [long] value of the param.\n" }, { "hvm_set_param", (PyCFunction)pyxc_hvm_param_set, METH_VARARGS | METH_KEYWORDS, "\n" "set a parameter of HVM guest OS.\n" " dom [int]: Identifier of domain to build into.\n" " param [int]: No. of HVM param.\n" " value [long]: Value of param.\n" "Returns: [int] 0 on success.\n" }, { "get_device_group", (PyCFunction)pyxc_get_device_group, METH_VARARGS, "\n" "get sibling devices infomation.\n" " dom [int]: Domain to assign device to.\n" " seg [int]: PCI segment.\n" " bus [int]: PCI bus.\n" " dev [int]: PCI dev.\n" " func [int]: PCI func.\n" "Returns: [string]: Sibling devices \n" }, { "test_assign_device", (PyCFunction)pyxc_test_assign_device, METH_VARARGS | METH_KEYWORDS, "\n" "test device assignment with VT-d.\n" " dom [int]: Identifier of domain to build into.\n" " pci_str [str]: PCI devices.\n" "Returns: [int] 0 on success, or device bdf that can't be assigned.\n" }, { "assign_device", (PyCFunction)pyxc_assign_device, METH_VARARGS | METH_KEYWORDS, "\n" "Assign device to IOMMU domain.\n" " dom [int]: Domain to assign device to.\n" " pci_str [str]: PCI devices.\n" "Returns: [int] 0 on success, or device bdf that can't be assigned.\n" }, { "deassign_device", (PyCFunction)pyxc_deassign_device, METH_VARARGS | METH_KEYWORDS, "\n" "Deassign device from IOMMU domain.\n" " dom [int]: Domain to deassign device from.\n" " pci_str [str]: PCI devices.\n" "Returns: [int] 0 on success, or device bdf that can't be deassigned.\n" }, { "sched_id_get", (PyCFunction)pyxc_sched_id_get, METH_NOARGS, "\n" "Get the current scheduler type in use.\n" "Returns: [int] sched_id.\n" }, { "sched_credit_domain_set", (PyCFunction)pyxc_sched_credit_domain_set, METH_KEYWORDS, "\n" "Set the scheduling parameters for a domain when running with the\n" "SMP credit scheduler.\n" " domid [int]: domain id to set\n" " weight [short]: domain's scheduling weight\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "sched_credit_domain_get", (PyCFunction)pyxc_sched_credit_domain_get, METH_VARARGS, "\n" "Get the scheduling parameters for a domain when running with the\n" "SMP credit scheduler.\n" " domid [int]: domain id to get\n" "Returns: [dict]\n" " weight [short]: domain's scheduling weight\n"}, { "sched_credit2_domain_set", (PyCFunction)pyxc_sched_credit2_domain_set, METH_KEYWORDS, "\n" "Set the scheduling parameters for a domain when running with the\n" "SMP credit2 scheduler.\n" " domid [int]: domain id to set\n" " weight [short]: domain's scheduling weight\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "sched_credit2_domain_get", (PyCFunction)pyxc_sched_credit2_domain_get, METH_VARARGS, "\n" "Get the scheduling parameters for a domain when running with the\n" "SMP credit2 scheduler.\n" " domid [int]: domain id to get\n" "Returns: [dict]\n" " weight [short]: domain's scheduling weight\n"}, { "evtchn_alloc_unbound", (PyCFunction)pyxc_evtchn_alloc_unbound, METH_VARARGS | METH_KEYWORDS, "\n" "Allocate an unbound port that will await a remote connection.\n" " dom [int]: Domain whose port space to allocate from.\n" " remote_dom [int]: Remote domain to accept connections from.\n\n" "Returns: [int] Unbound event-channel port.\n" }, { "evtchn_reset", (PyCFunction)pyxc_evtchn_reset, METH_VARARGS | METH_KEYWORDS, "\n" "Reset all connections.\n" " dom [int]: Domain to reset.\n" }, { "physdev_map_pirq", (PyCFunction)pyxc_physdev_map_pirq, METH_VARARGS | METH_KEYWORDS, "\n" "map physical irq to guest pirq.\n" " dom [int]: Identifier of domain to map for.\n" " index [int]: physical irq.\n" " pirq [int]: guest pirq.\n" "Returns: [long] value of the param.\n" }, { "physdev_pci_access_modify", (PyCFunction)pyxc_physdev_pci_access_modify, METH_VARARGS | METH_KEYWORDS, "\n" "Allow a domain access to a PCI device\n" " dom [int]: Identifier of domain to be allowed access.\n" " bus [int]: PCI bus\n" " dev [int]: PCI slot\n" " func [int]: PCI function\n" " enable [int]: Non-zero means enable access; else disable access\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "readconsolering", (PyCFunction)pyxc_readconsolering, METH_VARARGS | METH_KEYWORDS, "\n" "Read Xen's console ring.\n" " clear [int, 0]: Bool - clear the ring after reading from it?\n\n" "Returns: [str] string is empty on failure.\n" }, { "physinfo", (PyCFunction)pyxc_physinfo, METH_NOARGS, "\n" "Get information about the physical host machine\n" "Returns [dict]: information about the hardware" " [None]: on failure.\n" }, { "getcpuinfo", (PyCFunction)pyxc_getcpuinfo, METH_VARARGS | METH_KEYWORDS, "\n" "Get information about physical CPUs\n" "Returns [list]: information about physical CPUs" " [None]: on failure.\n" }, { "topologyinfo", (PyCFunction)pyxc_topologyinfo, METH_NOARGS, "\n" "Get information about the cpu topology on the host machine\n" "Returns [dict]: information about the cpu topology on host" " [None]: on failure.\n" }, { "numainfo", (PyCFunction)pyxc_numainfo, METH_NOARGS, "\n" "Get NUMA information on the host machine\n" "Returns [dict]: NUMA information on host" " [None]: on failure.\n" }, { "xeninfo", (PyCFunction)pyxc_xeninfo, METH_NOARGS, "\n" "Get information about the Xen host\n" "Returns [dict]: information about Xen" " [None]: on failure.\n" }, { "shadow_control", (PyCFunction)pyxc_shadow_control, METH_VARARGS | METH_KEYWORDS, "\n" "Set parameter for shadow pagetable interface\n" " dom [int]: Identifier of domain.\n" " op [int, 0]: operation\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "shadow_mem_control", (PyCFunction)pyxc_shadow_mem_control, METH_VARARGS | METH_KEYWORDS, "\n" "Set or read shadow pagetable memory use\n" " dom [int]: Identifier of domain.\n" " mb [int, -1]: MB of shadow memory this domain should have.\n\n" "Returns: [int] MB of shadow memory in use by this domain.\n" }, { "domain_setmaxmem", (PyCFunction)pyxc_domain_setmaxmem, METH_VARARGS, "\n" "Set a domain's memory limit\n" " dom [int]: Identifier of domain.\n" " maxmem_kb [int]: .\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_set_target_mem", (PyCFunction)pyxc_domain_set_target_mem, METH_VARARGS, "\n" "Set a domain's memory target\n" " dom [int]: Identifier of domain.\n" " mem_kb [int]: .\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_set_memmap_limit", (PyCFunction)pyxc_domain_set_memmap_limit, METH_VARARGS, "\n" "Set a domain's physical memory mappping limit\n" " dom [int]: Identifier of domain.\n" " map_limitkb [int]: .\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_ioport_permission", (PyCFunction)pyxc_domain_ioport_permission, METH_VARARGS | METH_KEYWORDS, "\n" "Allow a domain access to a range of IO ports\n" " dom [int]: Identifier of domain to be allowed access.\n" " first_port [int]: First IO port\n" " nr_ports [int]: Number of IO ports\n" " allow_access [int]: Non-zero means enable access; else disable access\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_irq_permission", (PyCFunction)pyxc_domain_irq_permission, METH_VARARGS | METH_KEYWORDS, "\n" "Allow a domain access to a physical IRQ\n" " dom [int]: Identifier of domain to be allowed access.\n" " pirq [int]: The Physical IRQ\n" " allow_access [int]: Non-zero means enable access; else disable access\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_iomem_permission", (PyCFunction)pyxc_domain_iomem_permission, METH_VARARGS | METH_KEYWORDS, "\n" "Allow a domain access to a range of IO memory pages\n" " dom [int]: Identifier of domain to be allowed access.\n" " first_pfn [long]: First page of I/O Memory\n" " nr_pfns [long]: Number of pages of I/O Memory (>0)\n" " allow_access [int]: Non-zero means enable access; else disable access\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "pages_to_kib", (PyCFunction)pyxc_pages_to_kib, METH_VARARGS, "\n" "Returns: [int]: The size in KiB of memory spanning the given number " "of pages.\n" }, { "domain_set_time_offset", (PyCFunction)pyxc_domain_set_time_offset, METH_VARARGS, "\n" "Set a domain's time offset to Dom0's localtime\n" " dom [int]: Domain whose time offset is being set.\n" " offset [int]: Time offset from UTC in seconds.\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_set_tsc_info", (PyCFunction)pyxc_domain_set_tsc_info, METH_VARARGS, "\n" "Set a domain's TSC mode\n" " dom [int]: Domain whose TSC mode is being set.\n" " tsc_mode [int]: 0=default (monotonic, but native where possible)\n" " 1=always emulate 2=never emulate 3=pvrdtscp\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_disable_migrate", (PyCFunction)pyxc_domain_disable_migrate, METH_VARARGS, "\n" "Marks domain as non-migratable AND non-restoreable\n" " dom [int]: Domain whose TSC mode is being set.\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "domain_send_trigger", (PyCFunction)pyxc_domain_send_trigger, METH_VARARGS | METH_KEYWORDS, "\n" "Send trigger to a domain.\n" " dom [int]: Identifier of domain to be sent trigger.\n" " trigger [int]: Trigger type number.\n" " vcpu [int]: VCPU to be sent trigger.\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "send_debug_keys", (PyCFunction)pyxc_send_debug_keys, METH_VARARGS | METH_KEYWORDS, "\n" "Inject debug keys into Xen.\n" " keys [str]: String of keys to inject.\n" }, #if defined(__i386__) || defined(__x86_64__) { "domain_check_cpuid", (PyCFunction)pyxc_dom_check_cpuid, METH_VARARGS, "\n" "Apply checks to host CPUID.\n" " input [long]: Input for cpuid instruction (eax)\n" " sub_input [long]: Second input (optional, may be None) for cpuid " " instruction (ecx)\n" " config [dict]: Dictionary of register\n" " config [dict]: Dictionary of register, use for checking\n\n" "Returns: [int] 0 on success; exception on error.\n" }, { "domain_set_cpuid", (PyCFunction)pyxc_dom_set_cpuid, METH_VARARGS, "\n" "Set cpuid response for an input and a domain.\n" " dom [int]: Identifier of domain.\n" " input [long]: Input for cpuid instruction (eax)\n" " sub_input [long]: Second input (optional, may be None) for cpuid " " instruction (ecx)\n" " config [dict]: Dictionary of register\n\n" "Returns: [int] 0 on success; exception on error.\n" }, { "domain_set_policy_cpuid", (PyCFunction)pyxc_dom_set_policy_cpuid, METH_VARARGS, "\n" "Set the default cpuid policy for a domain.\n" " dom [int]: Identifier of domain.\n\n" "Returns: [int] 0 on success; exception on error.\n" }, { "domain_set_machine_address_size", (PyCFunction)pyxc_dom_set_machine_address_size, METH_VARARGS, "\n" "Set maximum machine address size for this domain.\n" " dom [int]: Identifier of domain.\n" " width [int]: Maximum machine address width.\n" }, { "domain_suppress_spurious_page_faults", (PyCFunction)pyxc_dom_suppress_spurious_page_faults, METH_VARARGS, "\n" "Do not propagate spurious page faults to this guest.\n" " dom [int]: Identifier of domain.\n" }, #endif { "tmem_control", (PyCFunction)pyxc_tmem_control, METH_VARARGS | METH_KEYWORDS, "\n" "Do various control on a tmem pool.\n" " pool_id [int]: Identifier of the tmem pool (-1 == all).\n" " subop [int]: Supplementary Operation.\n" " cli_id [int]: Client identifier (-1 == all).\n" " len [int]: Length of 'buf'.\n" " arg [int]: Argument.\n" " buf [str]: Buffer.\n\n" "Returns: [int] 0 or [str] tmem info on success; exception on error.\n" }, { "tmem_shared_auth", (PyCFunction)pyxc_tmem_shared_auth, METH_VARARGS | METH_KEYWORDS, "\n" "De/authenticate a shared tmem pool.\n" " cli_id [int]: Client identifier (-1 == all).\n" " uuid_str [str]: uuid.\n" " auth [int]: 0|1 .\n" "Returns: [int] 0 on success; exception on error.\n" }, { "dom_set_memshr", (PyCFunction)pyxc_dom_set_memshr, METH_VARARGS, "\n" "Enable/disable memory sharing for the domain.\n" " dom [int]: Domain identifier.\n" " enable [int,0|1]: Disable or enable?\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "cpupool_create", (PyCFunction)pyxc_cpupool_create, METH_VARARGS | METH_KEYWORDS, "\n" "Create new cpupool.\n" " pool [int, 0]: cpupool identifier to use (allocated if zero).\n" " sched [int]: scheduler to use (credit if unspecified).\n\n" "Returns: [int] new cpupool identifier; -1 on error.\n" }, { "cpupool_destroy", (PyCFunction)pyxc_cpupool_destroy, METH_VARARGS, "\n" "Destroy a cpupool.\n" " pool [int]: Identifier of cpupool to be destroyed.\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "cpupool_getinfo", (PyCFunction)pyxc_cpupool_getinfo, METH_NOARGS, "\n" "Get information regarding a set of cpupools, in increasing id order.\n" "Returns: [list of dicts]\n" " pool [int]: Identifier of cpupool to which this info pertains\n" " sched [int]: Scheduler used for this cpupool\n" " n_dom [int]: Number of Domains in this cpupool\n" " cpulist [list]: List of CPUs this cpupool is using\n" }, { "cpupool_addcpu", (PyCFunction)pyxc_cpupool_addcpu, METH_VARARGS | METH_KEYWORDS, "\n" "Add a cpu to a cpupool.\n" " pool [int]: Identifier of cpupool.\n" " cpu [int, -1]: Cpu to add (lowest free if -1)\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "cpupool_removecpu", (PyCFunction)pyxc_cpupool_removecpu, METH_VARARGS | METH_KEYWORDS, "\n" "Remove a cpu from a cpupool.\n" " pool [int]: Identifier of cpupool.\n" " cpu [int, -1]: Cpu to remove (highest used if -1)\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "cpupool_movedomain", (PyCFunction)pyxc_cpupool_movedomain, METH_VARARGS | METH_KEYWORDS, "\n" "Move a domain to another cpupool.\n" " pool [int]: Identifier of cpupool to move domain to.\n" " dom [int]: Domain to move\n\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "cpupool_freeinfo", (PyCFunction)pyxc_cpupool_freeinfo, METH_NOARGS, "\n" "Get info about cpus not in any cpupool.\n" "Returns: [list]: List of CPUs\n" }, { "flask_context_to_sid", (PyCFunction)pyflask_context_to_sid, METH_KEYWORDS, "\n" "Convert a context string to a dynamic SID.\n" " context [str]: String specifying context to be converted\n" "Returns: [int]: Numeric SID on success; -1 on error.\n" }, { "flask_sid_to_context", (PyCFunction)pyflask_sid_to_context, METH_KEYWORDS, "\n" "Convert a dynamic SID to context string.\n" " context [int]: SID to be converted\n" "Returns: [str]: Numeric SID on success; -1 on error.\n" }, { "flask_load", (PyCFunction)pyflask_load, METH_KEYWORDS, "\n" "Loads a policy into the hypervisor.\n" " policy [str]: policy to be load\n" "Returns: [int]: 0 on success; -1 on failure.\n" }, { "flask_getenforce", (PyCFunction)pyflask_getenforce, METH_NOARGS, "\n" "Returns the current mode of the Flask XSM module.\n" "Returns: [int]: 0 for permissive; 1 for enforcing; -1 on failure.\n" }, { "flask_setenforce", (PyCFunction)pyflask_setenforce, METH_KEYWORDS, "\n" "Modifies the current mode for the Flask XSM module.\n" " mode [int]: mode to change to\n" "Returns: [int]: 0 on success; -1 on failure.\n" }, { "flask_access", (PyCFunction)pyflask_access, METH_KEYWORDS, "\n" "Returns whether a source context has access to target context based on \ class and permissions requested.\n" " scon [str]: source context\n" " tcon [str]: target context\n" " tclass [int]: target security class\n" " req [int] requested permissions\n" " allowed [int] permissions allow for the target class between the source \ and target context\n" " decided [int] the permissions that were returned in the allowed \ parameter\n" " auditallow [int] permissions set to audit on allow\n" " auditdeny [int] permissions set to audit on deny\n" " seqno [int] not used\n" "Returns: [int]: 0 on all permission granted; -1 if any permissions are \ denied\n" }, { NULL, NULL, 0, NULL } }; static PyObject *PyXc_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { XcObject *self = (XcObject *)type->tp_alloc(type, 0); if (self == NULL) return NULL; self->xc_handle = NULL; return (PyObject *)self; } static int PyXc_init(XcObject *self, PyObject *args, PyObject *kwds) { if ((self->xc_handle = xc_interface_open(0,0,0)) == 0) { pyxc_error_to_exception(0); return -1; } return 0; } static void PyXc_dealloc(XcObject *self) { if (self->xc_handle) { xc_interface_close(self->xc_handle); self->xc_handle = NULL; } Py_TYPE(self)->tp_free((PyObject *)self); } static PyTypeObject PyXcType = { #if PY_MAJOR_VERSION >= 3 .ob_base = { PyObject_HEAD_INIT(NULL) }, #else PyObject_HEAD_INIT(NULL) #endif .tp_name = PKG "." CLS, .tp_basicsize = sizeof(XcObject), .tp_itemsize = 0, .tp_dealloc = (destructor)PyXc_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "Xen client connections", .tp_methods = pyxc_methods, .tp_init = (initproc)PyXc_init, .tp_new = PyXc_new, }; static PyMethodDef xc_methods[] = { { NULL } }; #if PY_MAJOR_VERSION >= 3 static PyModuleDef xc_module = { PyModuleDef_HEAD_INIT, PKG, /* name */ NULL, /* docstring */ -1, /* size of per-interpreter state, -1 means the module use global variables */ xc_methods }; #endif #if PY_MAJOR_VERSION >= 3 #define INITERROR return NULL PyMODINIT_FUNC PyInit_xc(void) #else #define INITERROR return PyMODINIT_FUNC initxc(void) #endif { PyObject *m; if (PyType_Ready(&PyXcType) < 0) INITERROR; #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&xc_module); #else m = Py_InitModule(PKG, xc_methods); #endif if (m == NULL) INITERROR; xc_error_obj = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL); if (xc_error_obj == NULL) { Py_DECREF(m); INITERROR; } zero = PyLongOrInt_FromLong(0); /* KAF: This ensures that we get debug output in a timely manner. */ setbuf(stdout, NULL); setbuf(stderr, NULL); Py_INCREF(&PyXcType); PyModule_AddObject(m, CLS, (PyObject *)&PyXcType); Py_INCREF(xc_error_obj); PyModule_AddObject(m, "Error", xc_error_obj); /* Expose some libxc constants to Python */ PyModule_AddIntConstant(m, "XEN_SCHEDULER_CREDIT", XEN_SCHEDULER_CREDIT); PyModule_AddIntConstant(m, "XEN_SCHEDULER_CREDIT2", XEN_SCHEDULER_CREDIT2); #if PY_MAJOR_VERSION >= 3 return m; #endif } /* * Local variables: * c-indent-level: 4 * c-basic-offset: 4 * End: */ xen-4.9.2/tools/python/xen/lowlevel/xs/0000775000175000017500000000000013256712137016213 5ustar smbsmbxen-4.9.2/tools/python/xen/lowlevel/xs/xs.c0000664000175000017500000006272013256712137017020 0ustar smbsmb/* * Python interface to the Xen Store Daemon. * * This library is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (C) 2005 Mike Wray Hewlett-Packard * Copyright (C) 2005 Christian Limpach * Copyright (C) 2005 XenSource Ltd. */ #include #include #include #include #include #include #include #include #include #include /** @file * Python interface to the Xen Store Daemon (xs). */ /* Needed for Python versions earlier than 2.3. */ #ifndef PyMODINIT_FUNC #define PyMODINIT_FUNC DL_EXPORT(void) #endif #define PKG "xen.lowlevel.xs" #define CLS "xs" #if PY_MAJOR_VERSION < 3 /* Python 2 compatibility */ #define PyLong_FromLong PyInt_FromLong #undef PyLong_Check #define PyLong_Check PyInt_Check #define PyLong_AsLong PyInt_AsLong #endif static PyObject *xs_error; /** Python wrapper round an xs handle. */ typedef struct XsHandle { PyObject_HEAD; struct xs_handle *xh; PyObject *watches; } XsHandle; static void xs_set_error(int value) { errno = value; PyErr_SetFromErrno(xs_error); } static inline struct xs_handle *xshandle(XsHandle *self) { struct xs_handle *xh = self->xh; if (!xh) xs_set_error(EINVAL); return xh; } static void remove_watch(XsHandle *xsh, PyObject *token); static PyObject *none(bool result); static int parse_transaction_path(XsHandle *self, PyObject *args, struct xs_handle **xh, xs_transaction_t *th, char **path); #define xspy_read_doc "\n" \ "Read data from a path.\n" \ " transaction [string]: transaction handle\n" \ " path [string]: xenstore path\n" \ "\n" \ "Returns: [string] data read.\n" \ " None if key doesn't exist.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_read(XsHandle *self, PyObject *args) { struct xs_handle *xh; xs_transaction_t th; char *path; char *xsval; unsigned int xsval_n; if (!parse_transaction_path(self, args, &xh, &th, &path)) return NULL; Py_BEGIN_ALLOW_THREADS xsval = xs_read(xh, th, path, &xsval_n); Py_END_ALLOW_THREADS if (xsval) { PyObject *val = PyBytes_FromStringAndSize(xsval, xsval_n); free(xsval); return val; } else { return none(errno == ENOENT); } } #define xspy_write_doc "\n" \ "Write data to a path.\n" \ " transaction [string]: transaction handle\n" \ " path [string] : xenstore path to write to\n." \ " data [string] : data to write.\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_write(XsHandle *self, PyObject *args) { static char *arg_spec = "sss#"; struct xs_handle *xh = xshandle(self); xs_transaction_t th; char *thstr; char *path; char *data; int data_n; bool result; if (!xh) return NULL; if (!PyArg_ParseTuple(args, arg_spec, &thstr, &path, &data, &data_n)) return NULL; th = strtoul(thstr, NULL, 16); Py_BEGIN_ALLOW_THREADS result = xs_write(xh, th, path, data, data_n); Py_END_ALLOW_THREADS return none(result); } #define xspy_ls_doc "\n" \ "List a directory.\n" \ " transaction [string]: transaction handle\n" \ " path [string]: path to list.\n" \ "\n" \ "Returns: [string array] list of subdirectory names.\n" \ " None if key doesn't exist.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_ls(XsHandle *self, PyObject *args) { struct xs_handle *xh; xs_transaction_t th; char *path; char **xsval; unsigned int xsval_n; if (!parse_transaction_path(self, args, &xh, &th, &path)) return NULL; Py_BEGIN_ALLOW_THREADS xsval = xs_directory(xh, th, path, &xsval_n); Py_END_ALLOW_THREADS if (xsval) { int i; PyObject *val = PyList_New(xsval_n); for (i = 0; i < xsval_n; i++) #if PY_MAJOR_VERSION >= 3 PyList_SetItem(val, i, PyUnicode_FromString(xsval[i])); #else PyList_SetItem(val, i, PyBytes_FromString(xsval[i])); #endif free(xsval); return val; } else { return none(errno == ENOENT); } } #define xspy_mkdir_doc "\n" \ "Make a directory.\n" \ " transaction [string]: transaction handle.\n" \ " path [string] : path to directory to create.\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_mkdir(XsHandle *self, PyObject *args) { struct xs_handle *xh; xs_transaction_t th; char *path; bool result; if (!parse_transaction_path(self, args, &xh, &th, &path)) return NULL; Py_BEGIN_ALLOW_THREADS result = xs_mkdir(xh, th, path); Py_END_ALLOW_THREADS return none(result); } #define xspy_rm_doc "\n" \ "Remove a path.\n" \ " transaction [string]: transaction handle\n" \ " path [string] : path to remove\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_rm(XsHandle *self, PyObject *args) { struct xs_handle *xh; xs_transaction_t th; char *path; bool result; if (!parse_transaction_path(self, args, &xh, &th, &path)) return NULL; Py_BEGIN_ALLOW_THREADS result = xs_rm(xh, th, path); Py_END_ALLOW_THREADS return none(result || errno == ENOENT); } #define xspy_get_permissions_doc "\n" \ "Get the permissions for a path\n" \ " transaction [string]: transaction handle\n" \ " path [string]: xenstore path.\n" \ "\n" \ "Returns: permissions array.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_get_permissions(XsHandle *self, PyObject *args) { static char *arg_spec = "ss"; char *path = NULL; struct xs_handle *xh = xshandle(self); struct xs_permissions *perms; unsigned int perms_n = 0; int i; xs_transaction_t th; char *thstr; if (!xh) return NULL; if (!PyArg_ParseTuple(args, arg_spec, &thstr, &path)) return NULL; th = strtoul(thstr, NULL, 16); Py_BEGIN_ALLOW_THREADS perms = xs_get_permissions(xh, th, path, &perms_n); Py_END_ALLOW_THREADS if (perms) { PyObject *val = PyList_New(perms_n); for (i = 0; i < perms_n; i++) { PyObject *p = Py_BuildValue("{s:i,s:i,s:i}", "dom", perms[i].id, "read", perms[i].perms & XS_PERM_READ, "write", perms[i].perms & XS_PERM_WRITE); PyList_SetItem(val, i, p); } free(perms); return val; } else { PyErr_SetFromErrno(xs_error); return NULL; } } #define xspy_set_permissions_doc "\n" \ "Set the permissions for a path\n" \ " transaction [string]: transaction handle\n" \ " path [string] : xenstore path.\n" \ " perms : permissions.\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_set_permissions(XsHandle *self, PyObject *args) { char *path; PyObject *perms; static char *perm_names[] = { "dom", "read", "write", NULL }; static char *perm_spec = "i|ii"; struct xs_handle *xh = xshandle(self); int i, result; struct xs_permissions *xsperms = NULL; int xsperms_n; PyObject *tuple0 = NULL; xs_transaction_t th; char *thstr; PyObject *ret = NULL; if (!xh) goto exit; if (!PyArg_ParseTuple(args, "ssO", &thstr, &path, &perms)) goto exit; th = strtoul(thstr, NULL, 16); if (!PyList_Check(perms)) { xs_set_error(EINVAL); goto exit; } xsperms_n = PyList_Size(perms); /* NB. alloc +1 so we can change the owner if necessary. */ xsperms = calloc(xsperms_n + 1, sizeof(struct xs_permissions)); if (!xsperms) { xs_set_error(ENOMEM); goto exit; } tuple0 = PyTuple_New(0); if (!tuple0) goto exit; for (i = 0; i < xsperms_n; i++) { /* Read/write perms. Set these. */ int p_read = 0, p_write = 0; PyObject *p = PyList_GetItem(perms, i); if (!PyArg_ParseTupleAndKeywords(tuple0, p, perm_spec, perm_names, &xsperms[i].id, &p_read, &p_write)) goto exit; if (p_read) xsperms[i].perms |= XS_PERM_READ; if (p_write) xsperms[i].perms |= XS_PERM_WRITE; } /* * Is the caller trying to restrict access to the first specified * domain? If so then it cannot be owner, so we force dom0 as owner. */ if (xsperms_n && xsperms[0].perms && xsperms[0].id) { memmove(&xsperms[1], &xsperms[0], xsperms_n * sizeof(*xsperms)); xsperms[0].id = xsperms[0].perms = 0; xsperms_n++; } Py_BEGIN_ALLOW_THREADS result = xs_set_permissions(xh, th, path, xsperms, xsperms_n); Py_END_ALLOW_THREADS if (!result) { PyErr_SetFromErrno(xs_error); goto exit; } Py_INCREF(Py_None); ret = Py_None; exit: Py_XDECREF(tuple0); free(xsperms); return ret; } #define xspy_watch_doc "\n" \ "Watch a path, get notifications when it changes.\n" \ " path [string] : xenstore path.\n" \ " token [string] : returned in watch notification.\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" /* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */ #define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2) static PyObject *xspy_watch(XsHandle *self, PyObject *args) { struct xs_handle *xh = xshandle(self); char *path; PyObject *token; char token_str[MAX_STRLEN(unsigned long) + 1]; int result; int i; if (!xh) return NULL; if (!PyArg_ParseTuple(args, "sO", &path, &token)) return NULL; /* Note that we have to store the watch token in the xs->watches list before registering the watch with xs_watch, otherwise this function races with xs_read_watch. */ for (i = 0; i < PyList_Size(self->watches); i++) { if (PyList_GetItem(self->watches, i) == Py_None) { PySequence_SetItem(self->watches, i, token); break; } } if (i == PyList_Size(self->watches)) PyList_Append(self->watches, token); snprintf(token_str, sizeof(token_str), "%li", (unsigned long)token); Py_BEGIN_ALLOW_THREADS result = xs_watch(xh, path, token_str); Py_END_ALLOW_THREADS if (!result) remove_watch(self, token); return none(result); } #define xspy_read_watch_doc "\n" \ "Read a watch notification.\n" \ "\n" \ "Returns: [tuple] (path, token).\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_read_watch(XsHandle *self, PyObject *args) { struct xs_handle *xh = xshandle(self); PyObject *val = NULL; char **xsval; PyObject *token; int i; unsigned int num; if (!xh) return NULL; again: Py_BEGIN_ALLOW_THREADS xsval = xs_read_watch(xh, &num); Py_END_ALLOW_THREADS if (!xsval) { PyErr_SetFromErrno(xs_error); goto exit; } if (sscanf(xsval[XS_WATCH_TOKEN], "%li", (unsigned long *)&token) != 1) { xs_set_error(EINVAL); goto exit; } for (i = 0; i < PyList_Size(self->watches); i++) { if (token == PyList_GetItem(self->watches, i)) break; } if (i == PyList_Size(self->watches)) { /* We do not have a registered watch for the one that has just fired. Ignore this -- a watch that has been recently deregistered can still have watches in transit. This is a blocking method, so go back to read again. */ free(xsval); goto again; } /* Create tuple (path, token). */ val = Py_BuildValue("(sO)", xsval[XS_WATCH_PATH], token); exit: free(xsval); return val; } #define xspy_unwatch_doc "\n" \ "Stop watching a path.\n" \ " path [string] : xenstore path.\n" \ " token [string] : token from the watch.\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_unwatch(XsHandle *self, PyObject *args) { struct xs_handle *xh = xshandle(self); char *path; PyObject *token; char token_str[MAX_STRLEN(unsigned long) + 1]; int result; if (!xh) return NULL; if (!PyArg_ParseTuple(args, "sO", &path, &token)) return NULL; snprintf(token_str, sizeof(token_str), "%li", (unsigned long)token); Py_BEGIN_ALLOW_THREADS result = xs_unwatch(xh, path, token_str); Py_END_ALLOW_THREADS remove_watch(self, token); return none(result); } #define xspy_transaction_start_doc "\n" \ "Start a transaction.\n" \ "\n" \ "Returns transaction handle on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_transaction_start(XsHandle *self) { struct xs_handle *xh = xshandle(self); xs_transaction_t th; char thstr[MAX_STRLEN(unsigned long) + 1]; if (!xh) return NULL; Py_BEGIN_ALLOW_THREADS th = xs_transaction_start(xh); Py_END_ALLOW_THREADS if (th == XBT_NULL) { PyErr_SetFromErrno(xs_error); return NULL; } snprintf(thstr, sizeof(thstr), "%lX", (unsigned long)th); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(thstr); #else return PyBytes_FromString(thstr); #endif } #define xspy_transaction_end_doc "\n" \ "End the current transaction.\n" \ "Attempts to commit the transaction unless abort is true.\n" \ " transaction [string] : transaction handle.\n" \ " abort [int] : abort flag (default 0).\n" \ "\n" \ "Returns True on success, False if you need to try again.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_transaction_end(XsHandle *self, PyObject *args, PyObject *kwds) { static char *kwd_spec[] = { "transaction", "abort", NULL }; static char *arg_spec = "s|i"; int abort = 0; struct xs_handle *xh = xshandle(self); bool result; xs_transaction_t th; char *thstr; if (!xh) return NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &thstr, &abort)) return NULL; th = strtoul(thstr, NULL, 16); Py_BEGIN_ALLOW_THREADS result = xs_transaction_end(xh, th, abort); Py_END_ALLOW_THREADS if (result) { Py_INCREF(Py_True); return Py_True; } else if (errno == EAGAIN) { Py_INCREF(Py_False); return Py_False; } else { PyErr_SetFromErrno(xs_error); return NULL; } } #define xspy_introduce_domain_doc "\n" \ "Tell xenstore about a domain so it can talk to it.\n" \ " dom [int] : domain id\n" \ " page [long] : address of domain's xenstore page\n" \ " port [int] : port the domain is using for xenstore\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_introduce_domain(XsHandle *self, PyObject *args) { uint32_t dom; unsigned long page; unsigned int port; struct xs_handle *xh = xshandle(self); bool result = 0; if (!xh) return NULL; if (!PyArg_ParseTuple(args, "ili", &dom, &page, &port)) return NULL; Py_BEGIN_ALLOW_THREADS result = xs_introduce_domain(xh, dom, page, port); Py_END_ALLOW_THREADS return none(result); } #define xspy_set_target_doc "\n" \ "Tell xenstore that a domain is targetting another one so it\n" \ "should let it tinker with it.\n" \ " dom [int] : domain id\n" \ " target [int] : domain id of the target\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_set_target(XsHandle *self, PyObject *args) { uint32_t dom; uint32_t target; struct xs_handle *xh = xshandle(self); bool result = 0; if (!xh) return NULL; if (!PyArg_ParseTuple(args, "ii", &dom, &target)) return NULL; Py_BEGIN_ALLOW_THREADS result = xs_set_target(xh, dom, target); Py_END_ALLOW_THREADS return none(result); } #define xspy_resume_domain_doc "\n" \ "Tell xenstore to clear its shutdown flag for a domain.\n" \ "This ensures that a subsequent shutdown will fire the\n" \ "appropriate watches.\n" \ " dom [int]: domain id\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" static PyObject *xspy_resume_domain(XsHandle *self, PyObject *args) { uint32_t dom; struct xs_handle *xh = xshandle(self); bool result = 0; if (!xh) return NULL; if (!PyArg_ParseTuple(args, "i", &dom)) return NULL; Py_BEGIN_ALLOW_THREADS result = xs_resume_domain(xh, dom); Py_END_ALLOW_THREADS return none(result); } #define xspy_release_domain_doc "\n" \ "Tell xenstore to release its channel to a domain.\n" \ "Unless this is done the domain will not be released.\n" \ " dom [int]: domain id\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_release_domain(XsHandle *self, PyObject *args) { uint32_t dom; struct xs_handle *xh = xshandle(self); bool result = 0; if (!xh) return NULL; if (!PyArg_ParseTuple(args, "i", &dom)) return NULL; Py_BEGIN_ALLOW_THREADS result = xs_release_domain(xh, dom); Py_END_ALLOW_THREADS return none(result); } #define xspy_close_doc "\n" \ "Close the connection to xenstore.\n" \ "\n" \ "Returns None on success.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_close(XsHandle *self) { struct xs_handle *xh = xshandle(self); int i; if (!xh) return NULL; for (i = 0; i < PyList_Size(self->watches); i++) { /* TODO: xs_unwatch watches */ PySequence_SetItem(self->watches, i, Py_None); } xs_daemon_close(xh); self->xh = NULL; Py_INCREF(Py_None); return Py_None; } #define xspy_get_domain_path_doc "\n" \ "Return store path of domain, whether or not the domain exists.\n" \ " domid [int]: domain id\n" \ "\n" \ "Returns: [string] domain store path.\n" \ "Raises xen.lowlevel.xs.Error on error.\n" \ "\n" static PyObject *xspy_get_domain_path(XsHandle *self, PyObject *args) { struct xs_handle *xh = xshandle(self); uint32_t domid; char *xsval; if (!xh) return NULL; if (!PyArg_ParseTuple(args, "i", &domid)) return NULL; Py_BEGIN_ALLOW_THREADS xsval = xs_get_domain_path(xh, domid); Py_END_ALLOW_THREADS if (xsval) { #if PY_MAJOR_VERSION >= 3 PyObject *val = PyUnicode_FromString(xsval); #else PyObject *val = PyBytes_FromString(xsval); #endif free(xsval); return val; } else { return none(errno == ENOENT); } } /** * Remove the given token from the watches list belonging to the given * XsHandle, if present. */ static void remove_watch(XsHandle *self, PyObject *token) { int i; for (i = 0; i < PyList_Size(self->watches); i++) { if (PyList_GetItem(self->watches, i) == token) { PySequence_SetItem(self->watches, i, Py_None); return; } } } /** * Parse transaction and path arguments from the given args and kwds, * convert the given self value to an xs_handle, and return all three by * reference. * * @return 1 on success, in which case *xh, *th, and *path are valid, or 0 on * failure. */ static int parse_transaction_path(XsHandle *self, PyObject *args, struct xs_handle **xh, xs_transaction_t *th, char **path) { char *thstr; *xh = xshandle(self); if (!*xh) return 0; if (!PyArg_ParseTuple(args, "ss", &thstr, path)) return 0; *th = strtoul(thstr, NULL, 16); return 1; } static PyObject *none(bool result) { if (result) { Py_INCREF(Py_None); return Py_None; } else { PyErr_SetFromErrno(xs_error); return NULL; } } #define XSPY_METH(_name, _args) { \ .ml_name = #_name, \ .ml_meth = (PyCFunction) xspy_ ## _name, \ .ml_flags = _args, \ .ml_doc = xspy_ ## _name ## _doc } static PyMethodDef xshandle_methods[] = { XSPY_METH(read, METH_VARARGS), XSPY_METH(write, METH_VARARGS), XSPY_METH(ls, METH_VARARGS), XSPY_METH(mkdir, METH_VARARGS), XSPY_METH(rm, METH_VARARGS), XSPY_METH(get_permissions, METH_VARARGS), XSPY_METH(set_permissions, METH_VARARGS), XSPY_METH(watch, METH_VARARGS), XSPY_METH(read_watch, METH_NOARGS), XSPY_METH(unwatch, METH_VARARGS), XSPY_METH(transaction_start, METH_NOARGS), XSPY_METH(transaction_end, METH_VARARGS | METH_KEYWORDS), XSPY_METH(introduce_domain, METH_VARARGS), XSPY_METH(set_target, METH_VARARGS), XSPY_METH(resume_domain, METH_VARARGS), XSPY_METH(release_domain, METH_VARARGS), XSPY_METH(close, METH_NOARGS), XSPY_METH(get_domain_path, METH_VARARGS), { NULL /* Sentinel. */ }, }; static PyObject * xshandle_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { XsHandle *self = (XsHandle *)type->tp_alloc(type, 0); if (self == NULL) return NULL; self->xh = NULL; self->watches = PyList_New(0); if (!self->watches) goto fail; return (PyObject *)self; fail: /* Decreasing the object's reference to 0 will result in xshandle_dealloc being called. */ Py_DECREF(self); return NULL; } static int xshandle_init(XsHandle *self, PyObject *args, PyObject *kwds) { static char *kwd_spec[] = { "readonly", NULL }; static char *arg_spec = "|i"; int readonly = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, arg_spec, kwd_spec, &readonly)) goto fail; self->xh = (readonly ? xs_daemon_open_readonly() : xs_daemon_open()); if (!self->xh) goto fail; return 0; fail: PyErr_SetFromErrno(xs_error); return -1; } static void xshandle_dealloc(XsHandle *self) { if (self->xh) { xs_daemon_close(self->xh); self->xh = NULL; } Py_XDECREF(self->watches); Py_TYPE(self)->tp_free((PyObject *)self); } static PyTypeObject xshandle_type = { #if PY_MAJOR_VERSION >= 3 .ob_base = { PyObject_HEAD_INIT(NULL) }, #else PyObject_HEAD_INIT(NULL) #endif .tp_name = PKG "." CLS, .tp_basicsize = sizeof(XsHandle), .tp_itemsize = 0, .tp_dealloc = (destructor)xshandle_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "Xenstore connections", .tp_methods = xshandle_methods, .tp_init = (initproc)xshandle_init, .tp_new = xshandle_new, }; static PyMethodDef xs_methods[] = { { NULL } }; #if PY_MAJOR_VERSION >= 3 static PyModuleDef xs_module = { PyModuleDef_HEAD_INIT, PKG, /* name */ NULL, /* docstring */ -1, /* size of per-interpreter state, -1 means the module use global variables */ xs_methods }; #endif #if PY_MAJOR_VERSION >= 3 #define INITERROR return NULL PyMODINIT_FUNC PyInit_xs(void) #else #define INITERROR return PyMODINIT_FUNC initxs(void) #endif { PyObject* m; if (PyType_Ready(&xshandle_type) < 0) INITERROR; #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&xs_module); #else m = Py_InitModule(PKG, xs_methods); #endif if (m == NULL) INITERROR; xs_error = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL); if (xs_error == NULL) { Py_DECREF(m); INITERROR; } Py_INCREF(&xshandle_type); PyModule_AddObject(m, CLS, (PyObject *)&xshandle_type); Py_INCREF(xs_error); PyModule_AddObject(m, "Error", xs_error); #if PY_MAJOR_VERSION >= 3 return m; #endif } /* * Local variables: * c-indent-level: 4 * c-basic-offset: 4 * End: */ xen-4.9.2/tools/python/xen/lowlevel/__init__.py0000664000175000017500000000000213256712137017662 0ustar smbsmb xen-4.9.2/tools/python/xen/migration/0000775000175000017500000000000013256712137015721 5ustar smbsmbxen-4.9.2/tools/python/xen/migration/public.py0000664000175000017500000000114613256712137017553 0ustar smbsmb#!/usr/bin/env python # -*- coding: utf-8 -*- """ Xen public ABI constants, used in migration """ HVM_PARAM_STORE_PFN = 1 HVM_PARAM_IOREQ_PFN = 5 HVM_PARAM_BUFIOREQ_PFN = 6 HVM_PARAM_VIRIDIAN = 9 HVM_PARAM_IDENT_PT = 12 HVM_PARAM_VM86_TSS = 15 HVM_PARAM_CONSOLE_PFN = 17 HVM_PARAM_ACPI_IOPORTS_LOCATION = 19 HVM_PARAM_PAGING_RING_PFN = 27 HVM_PARAM_MONITOR_RING_PFN = 28 HVM_PARAM_SHARING_RING_PFN = 29 HVM_PARAM_IOREQ_SERVER_PFN = 32 HVM_PARAM_NR_IOREQ_SERVER_PAGES = 33 HVM_PARAM_VM_GENERATION_ID_ADDR = 34 xen-4.9.2/tools/python/xen/migration/libxl.py0000664000175000017500000001612213256712137017407 0ustar smbsmb#!/usr/bin/env python # -*- coding: utf-8 -*- """ Libxl Migration v2 streams Record structures as per docs/specs/libxl-migration-stream.pandoc, and verification routines. """ import sys from struct import calcsize, unpack, unpack_from from xen.migration.verify import StreamError, RecordError, VerifyBase from xen.migration.libxc import VerifyLibxc # Header HDR_FORMAT = "!QII" HDR_IDENT = 0x4c6962786c466d74 # "LibxlFmt" in ASCII HDR_VERSION = 2 HDR_OPT_BIT_ENDIAN = 0 HDR_OPT_BIT_LEGACY = 1 HDR_OPT_LE = (0 << HDR_OPT_BIT_ENDIAN) HDR_OPT_BE = (1 << HDR_OPT_BIT_ENDIAN) HDR_OPT_LEGACY = (1 << HDR_OPT_BIT_LEGACY) HDR_OPT_RESZ_MASK = 0xfffc # Records RH_FORMAT = "II" REC_TYPE_end = 0x00000000 REC_TYPE_libxc_context = 0x00000001 REC_TYPE_emulator_xenstore_data = 0x00000002 REC_TYPE_emulator_context = 0x00000003 REC_TYPE_checkpoint_end = 0x00000004 REC_TYPE_checkpoint_state = 0x00000005 rec_type_to_str = { REC_TYPE_end : "End", REC_TYPE_libxc_context : "Libxc context", REC_TYPE_emulator_xenstore_data : "Emulator xenstore data", REC_TYPE_emulator_context : "Emulator context", REC_TYPE_checkpoint_end : "Checkpoint end", REC_TYPE_checkpoint_state : "Checkpoint state" } # emulator_* header EMULATOR_HEADER_FORMAT = "II" EMULATOR_ID_unknown = 0x00000000 EMULATOR_ID_qemu_trad = 0x00000001 EMULATOR_ID_qemu_upstream = 0x00000002 emulator_id_to_str = { EMULATOR_ID_unknown : "Unknown", EMULATOR_ID_qemu_trad : "Qemu Traditional", EMULATOR_ID_qemu_upstream : "Qemu Upstream", } # # libxl format # LIBXL_QEMU_SIGNATURE = "DeviceModelRecord0002" LIBXL_QEMU_RECORD_HDR = "=%dsI" % (len(LIBXL_QEMU_SIGNATURE), ) class VerifyLibxl(VerifyBase): """ Verify a Libxl v2 stream """ def __init__(self, info, read): VerifyBase.__init__(self, info, read) def verify(self): """ Verity a libxl stream """ self.verify_hdr() while self.verify_record() != REC_TYPE_end: pass def verify_hdr(self): """ Verify a Header """ ident, version, options = self.unpack_exact(HDR_FORMAT) if ident != HDR_IDENT: raise StreamError("Bad image id: Expected 0x%x, got 0x%x" % (HDR_IDENT, ident)) if version != HDR_VERSION: raise StreamError("Unknown image version: Expected %d, got %d" % (HDR_VERSION, version)) if options & HDR_OPT_RESZ_MASK: raise StreamError("Reserved bits set in image options field: 0x%x" % (options & HDR_OPT_RESZ_MASK)) if ( (sys.byteorder == "little") and ((options & HDR_OPT_BIT_ENDIAN) != HDR_OPT_LE) ): raise StreamError( "Stream is not native endianess - unable to validate") endian = ["little", "big"][options & HDR_OPT_LE] if options & HDR_OPT_LEGACY: self.info("Libxl Header: %s endian, legacy converted" % (endian, )) else: self.info("Libxl Header: %s endian" % (endian, )) 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 %x" % (rtype, )) self.info("Libxl Record: %s, length %d" % (rec_type_to_str[rtype], length)) contentsz = (length + 7) & ~7 content = self.rdexact(contentsz) padding = content[length:] if padding != "\x00" * len(padding): raise StreamError("Padding containing non0 bytes found") if rtype not in record_verifiers: raise RuntimeError("No verification function for libxl 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_libxc_context(self, content): """ Libxc context record """ if len(content) != 0: raise RecordError("Libxc context record with non-zero length") # Verify the libxc stream, as we can't seek forwards through it VerifyLibxc(self.info, self.read).verify() def verify_record_emulator_xenstore_data(self, content): """ Emulator Xenstore Data record """ minsz = calcsize(EMULATOR_HEADER_FORMAT) if len(content) < minsz: raise RecordError("Length must be at least %d bytes, got %d" % (minsz, len(content))) emu_id, emu_idx = unpack(EMULATOR_HEADER_FORMAT, content[:minsz]) if emu_id not in emulator_id_to_str: raise RecordError("Unrecognised emulator id 0x%x" % (emu_id, )) self.info("Emulator Xenstore Data (%s, idx %d)" % (emulator_id_to_str[emu_id], emu_idx)) # Chop off the emulator header content = content[minsz:] if len(content): if content[-1] != '\x00': raise RecordError("Data not NUL terminated") # Split without the final NUL, to get an even number of parts parts = content[:-1].split("\x00") if (len(parts) % 2) != 0: raise RecordError("Expected an even number of strings, got %d" % (len(parts), )) for key, val in zip(parts[0::2], parts[1::2]): self.info(" '%s' = '%s'" % (key, val)) def verify_record_emulator_context(self, content): """ Emulator Context record """ minsz = calcsize(EMULATOR_HEADER_FORMAT) if len(content) < minsz: raise RecordError("Length must be at least %d bytes, got %d" % (minsz, len(content))) emu_id, emu_idx = unpack(EMULATOR_HEADER_FORMAT, content[:minsz]) if emu_id not in emulator_id_to_str: raise RecordError("Unrecognised emulator id 0x%x" % (emu_id, )) self.info(" Index %d, type %s" % (emu_idx, emulator_id_to_str[emu_id])) def verify_record_checkpoint_end(self, content): """ Checkpoint end record """ if len(content) != 0: raise RecordError("Checkpoint end record with non-zero length") def verify_record_checkpoint_state(self, content): """ Checkpoint state """ if len(content) == 0: raise RecordError("Checkpoint state record with zero length") record_verifiers = { REC_TYPE_end: VerifyLibxl.verify_record_end, REC_TYPE_libxc_context: VerifyLibxl.verify_record_libxc_context, REC_TYPE_emulator_xenstore_data: VerifyLibxl.verify_record_emulator_xenstore_data, REC_TYPE_emulator_context: VerifyLibxl.verify_record_emulator_context, REC_TYPE_checkpoint_end: VerifyLibxl.verify_record_checkpoint_end, REC_TYPE_checkpoint_state: VerifyLibxl.verify_record_checkpoint_state, } xen-4.9.2/tools/python/xen/migration/xl.py0000664000175000017500000000026713256712137016723 0ustar smbsmb#!/usr/bin/env python # -*- coding: utf-8 -*- """ XL migration stream format """ MAGIC = "Xen saved domain, xl format\n \0 \r" HEADER_FORMAT = "=IIII" MANDATORY_FLAG_STREAMV2 = 2 xen-4.9.2/tools/python/xen/migration/tests.py0000664000175000017500000000257013256712137017441 0ustar smbsmb#!/usr/bin/env python # -*- coding: utf-8 -*- """ Unit tests for migration v2 streams """ import unittest from struct import calcsize from xen.migration import libxc, libxl class TestLibxc(unittest.TestCase): def test_format_sizes(self): for fmt, sz in ( (libxc.IHDR_FORMAT, 24), (libxc.DHDR_FORMAT, 16), (libxc.RH_FORMAT, 8), (libxc.PAGE_DATA_FORMAT, 8), (libxc.X86_PV_INFO_FORMAT, 8), (libxc.X86_PV_P2M_FRAMES_FORMAT, 8), (libxc.X86_PV_VCPU_HDR_FORMAT, 8), (libxc.TSC_INFO_FORMAT, 24), (libxc.HVM_PARAMS_ENTRY_FORMAT, 16), (libxc.HVM_PARAMS_FORMAT, 8), ): self.assertEqual(calcsize(fmt), sz) class TestLibxl(unittest.TestCase): def test_format_sizes(self): for fmt, sz in ( (libxl.HDR_FORMAT, 16), (libxl.RH_FORMAT, 8), (libxl.EMULATOR_HEADER_FORMAT, 8), ): self.assertEqual(calcsize(fmt), sz) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TestLibxc)) suite.addTest(unittest.makeSuite(TestLibxl)) return suite if __name__ == "__main__": unittest.main() xen-4.9.2/tools/python/xen/migration/__init__.py0000664000175000017500000000000013256712137020020 0ustar smbsmbxen-4.9.2/tools/python/xen/migration/verify.py0000664000175000017500000000144113256712137017577 0ustar smbsmb#!/usr/bin/env python # -*- coding: utf-8 -*- """ Common verification infrastructure for v2 streams """ from struct import calcsize, unpack class StreamError(StandardError): """Error with the stream""" pass class RecordError(StandardError): """Error with a record in the stream""" pass class VerifyBase(object): def __init__(self, info, read): self.info = info self.read = read def rdexact(self, nr_bytes): """Read exactly nr_bytes from the stream""" _ = self.read(nr_bytes) if len(_) != nr_bytes: raise IOError("Stream truncated") return _ def unpack_exact(self, fmt): """Unpack a struct format string from the stream""" sz = calcsize(fmt) return unpack(fmt, self.rdexact(sz)) xen-4.9.2/tools/python/xen/migration/legacy.py0000664000175000017500000002676413256712137017556 0ustar smbsmb#!/usr/bin/env python # -*- coding: utf-8 -*- """ Legacy migration stream information. Documentation and record structures for legacy migration, for both libxc and libxl. """ """ Libxc: SAVE/RESTORE/MIGRATE PROTOCOL ============================= The general form of a stream of chunks is a header followed by a body consisting of a variable number of chunks (terminated by a chunk with type 0) followed by a trailer. For a rolling/checkpoint (e.g. remus) migration then the body and trailer phases can be repeated until an external event (e.g. failure) causes the process to terminate and commit to the most recent complete checkpoint. HEADER ------ unsigned long : p2m_size extended-info (PV-only, optional): If first unsigned long == ~0UL then extended info is present, otherwise unsigned long is part of p2m. Note that p2m_size above does not include the length of the extended info. extended-info: unsigned long : signature == ~0UL uint32_t : number of bytes remaining in extended-info 1 or more extended-info blocks of form: char[4] : block identifier uint32_t : block data size bytes : block data defined extended-info blocks: "vcpu" : VCPU context info containing vcpu_guest_context_t. The precise variant of the context structure (e.g. 32 vs 64 bit) is distinguished by the block size. "extv" : Presence indicates use of extended VCPU context in tail, data size is 0. p2m (PV-only): consists of p2m_size bytes comprising an array of xen_pfn_t sized entries. BODY PHASE - Format A (for live migration or Remus without compression) ---------- A series of chunks with a common header: int : chunk type If the chunk type is +ve then chunk contains guest memory data, and the type contains the number of pages in the batch: unsigned long[] : PFN array, length == number of pages in batch Each entry consists of XEN_DOMCTL_PFINFO_* in bits 31-28 and the PFN number in bits 27-0. page data : PAGE_SIZE bytes for each page marked present in PFN array If the chunk type is -ve then chunk consists of one of a number of metadata types. See definitions of XC_SAVE_ID_* below. If chunk type is 0 then body phase is complete. BODY PHASE - Format B (for Remus with compression) ---------- A series of chunks with a common header: int : chunk type If the chunk type is +ve then chunk contains array of PFNs corresponding to guest memory and type contains the number of PFNs in the batch: unsigned long[] : PFN array, length == number of pages in batch Each entry consists of XEN_DOMCTL_PFINFO_* in bits 31-28 and the PFN number in bits 27-0. If the chunk type is -ve then chunk consists of one of a number of metadata types. See definitions of XC_SAVE_ID_* below. If the chunk type is -ve and equals XC_SAVE_ID_COMPRESSED_DATA, then the chunk consists of compressed page data, in the following format: unsigned long : Size of the compressed chunk to follow compressed data : variable length data of size indicated above. This chunk consists of compressed page data. The number of pages in one chunk depends on the amount of space available in the sender's output buffer. Format of compressed data: compressed_data = * delta = marker = (RUNFLAG|SKIPFLAG) bitwise-or RUNLEN [1 byte marker] RUNFLAG = 0 SKIPFLAG = 1 << 7 RUNLEN = 7-bit unsigned value indicating number of WORDS in the run run = string of bytes of length sizeof(WORD) * RUNLEN If marker contains RUNFLAG, then RUNLEN * sizeof(WORD) bytes of data following the marker is copied into the target page at the appropriate offset indicated by the offset_ptr If marker contains SKIPFLAG, then the offset_ptr is advanced by RUNLEN * sizeof(WORD). If chunk type is 0 then body phase is complete. There can be one or more chunks with type XC_SAVE_ID_COMPRESSED_DATA, containing compressed pages. The compressed chunks are collated to form one single compressed chunk for the entire iteration. The number of pages present in this final compressed chunk will be equal to the total number of valid PFNs specified by the +ve chunks. At the sender side, compressed pages are inserted into the output stream in the same order as they would have been if compression logic was absent. Until last iteration, the BODY is sent in Format A, to maintain live migration compatibility with receivers of older Xen versions. At the last iteration, if Remus compression was enabled, the sender sends a trigger, XC_SAVE_ID_ENABLE_COMPRESSION to tell the receiver to parse the BODY in Format B from the next iteration onwards. An example sequence of chunks received in Format B: +16 +ve chunk unsigned long[16] PFN array +100 +ve chunk unsigned long[100] PFN array +50 +ve chunk unsigned long[50] PFN array XC_SAVE_ID_COMPRESSED_DATA TAG N Length of compressed data N bytes of DATA Decompresses to 166 pages XC_SAVE_ID_* other xc save chunks 0 END BODY TAG Corner case with checkpoint compression: At sender side, after pausing the domain, dirty pages are usually copied out to a temporary buffer. After the domain is resumed, compression is done and the compressed chunk(s) are sent, followed by other XC_SAVE_ID_* chunks. If the temporary buffer gets full while scanning for dirty pages, the sender stops buffering of dirty pages, compresses the temporary buffer and sends the compressed data with XC_SAVE_ID_COMPRESSED_DATA. The sender then resumes the buffering of dirty pages and continues scanning for the dirty pages. For e.g., assume that the temporary buffer can hold 4096 pages and there are 5000 dirty pages. The following is the sequence of chunks that the receiver will see: +1024 +ve chunk unsigned long[1024] PFN array +1024 +ve chunk unsigned long[1024] PFN array +1024 +ve chunk unsigned long[1024] PFN array +1024 +ve chunk unsigned long[1024] PFN array XC_SAVE_ID_COMPRESSED_DATA TAG N Length of compressed data N bytes of DATA Decompresses to 4096 pages +4 +ve chunk unsigned long[4] PFN array XC_SAVE_ID_COMPRESSED_DATA TAG M Length of compressed data M bytes of DATA Decompresses to 4 pages XC_SAVE_ID_* other xc save chunks 0 END BODY TAG In other words, XC_SAVE_ID_COMPRESSED_DATA can be interleaved with +ve chunks arbitrarily. But at the receiver end, the following condition always holds true until the end of BODY PHASE: num(PFN entries +ve chunks) >= num(pages received in compressed form) TAIL PHASE ---------- Content differs for PV and HVM guests. HVM TAIL: "Magic" pages: uint64_t : I/O req PFN uint64_t : Buffered I/O req PFN uint64_t : Store PFN Xen HVM Context: uint32_t : Length of context in bytes bytes : Context data Qemu context: char[21] : Signature: "QemuDeviceModelRecord" : Read Qemu save data until EOF "DeviceModelRecord0002" : uint32_t length field followed by that many bytes of Qemu save data "RemusDeviceModelState" : Currently the same as "DeviceModelRecord0002". PV TAIL: Unmapped PFN list : list of all the PFNs that were not in map at the close unsigned int : Number of unmapped pages unsigned long[] : PFNs of unmapped pages VCPU context data : A series of VCPU records, one per present VCPU Maximum and present map supplied in XC_SAVE_ID_VCPUINFO bytes: : VCPU context structure. Size is determined by size provided in extended-info header bytes[128] : Extended VCPU context (present IFF "extv" block present in extended-info header) Shared Info Page : 4096 bytes of shared info page """ CHUNK_end = 0 CHUNK_enable_verify_mode = -1 CHUNK_vcpu_info = -2 CHUNK_hvm_ident_pt = -3 CHUNK_hvm_vm86_tss = -4 CHUNK_tmem = -5 CHUNK_tmem_extra = -6 CHUNK_tsc_info = -7 CHUNK_hvm_console_pfn = -8 CHUNK_last_checkpoint = -9 CHUNK_hvm_acpi_ioports_location = -10 CHUNK_hvm_viridian = -11 CHUNK_compressed_data = -12 CHUNK_enable_compression = -13 CHUNK_hvm_generation_id_addr = -14 CHUNK_hvm_paging_ring_pfn = -15 CHUNK_hvm_monitor_ring_pfn = -16 CHUNK_hvm_sharing_ring_pfn = -17 CHUNK_toolstack = -18 CHUNK_hvm_ioreq_server_pfn = -19 CHUNK_hvm_nr_ioreq_server_pages = -20 chunk_type_to_str = { CHUNK_end : "end", CHUNK_enable_verify_mode : "enable_verify_mode", CHUNK_vcpu_info : "vcpu_info", CHUNK_hvm_ident_pt : "hvm_ident_pt", CHUNK_hvm_vm86_tss : "hvm_vm86_tss", CHUNK_tmem : "tmem", CHUNK_tmem_extra : "tmem_extra", CHUNK_tsc_info : "tsc_info", CHUNK_hvm_console_pfn : "hvm_console_pfn", CHUNK_last_checkpoint : "last_checkpoint", CHUNK_hvm_acpi_ioports_location : "hvm_acpi_ioports_location", CHUNK_hvm_viridian : "hvm_viridian", CHUNK_compressed_data : "compressed_data", CHUNK_enable_compression : "enable_compression", CHUNK_hvm_generation_id_addr : "hvm_generation_id_addr", CHUNK_hvm_paging_ring_pfn : "hvm_paging_ring_pfn", CHUNK_hvm_monitor_ring_pfn : "hvm_monitor_ring_pfn", CHUNK_hvm_sharing_ring_pfn : "hvm_sharing_ring_pfn", CHUNK_toolstack : "toolstack", CHUNK_hvm_ioreq_server_pfn : "hvm_ioreq_server_pfn", CHUNK_hvm_nr_ioreq_server_pages : "hvm_nr_ioreq_server_pages", } # Up to 1024 pages (4MB) at a time MAX_BATCH = 1024 # Maximum #VCPUs currently supported for save/restore MAX_VCPU_ID = 4095 """ Libxl: Legacy "toolstack" record layout: Version 1: uint32_t version QEMU physmap data: uint32_t count libxl__physmap_info * count The problem is that libxl__physmap_info was declared as: struct libxl__physmap_info { uint64_t phys_offset; uint64_t start_addr; uint64_t size; uint32_t namelen; char name[]; }; Which has 4 bytes of padding at the end in a 64bit build, thus not the same between 32 and 64bit builds. Because of the pointer arithmatic used to construct the record, the 'name' was shifted up to start at the padding, leaving the erronious 4 bytes at the end of the name string, after the NUL terminator. Instead, the information described here has been changed to fit in a new EMULATOR_XENSTORE_DATA record made of NUL terminated strings. """ xen-4.9.2/tools/python/xen/migration/libxc.py0000664000175000017500000003670513256712137017407 0ustar smbsmb#!/usr/bin/env python # -*- 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 # In Python3 long type have been merged into int, 1L syntax is no longer valid if sys.version_info > (3,): long = int # Image Header IHDR_FORMAT = "!QIIHHI" IHDR_MARKER = 0xffffffffffffffff IHDR_IDENT = 0x58454E46 # "XENF" in ASCII IHDR_VERSION = 2 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_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", } # 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_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" } # page_data PAGE_DATA_FORMAT = "II" PAGE_DATA_PFN_MASK = (long(1) << 52) - 1 PAGE_DATA_PFN_RESZ_MASK = ((long(1) << 60) - 1) & ~((long(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 = (long(0x7) << PAGE_DATA_TYPE_SHIFT) PAGE_DATA_TYPE_LTAB_MASK = (long(0xf) << PAGE_DATA_TYPE_SHIFT) PAGE_DATA_TYPE_LPINTAB = (long(0x8) << PAGE_DATA_TYPE_SHIFT) # Pinned pagetable PAGE_DATA_TYPE_NOTAB = (long(0x0) << PAGE_DATA_TYPE_SHIFT) # Regular page PAGE_DATA_TYPE_L1TAB = (long(0x1) << PAGE_DATA_TYPE_SHIFT) # L1 pagetable PAGE_DATA_TYPE_L2TAB = (long(0x2) << PAGE_DATA_TYPE_SHIFT) # L2 pagetable PAGE_DATA_TYPE_L3TAB = (long(0x3) << PAGE_DATA_TYPE_SHIFT) # L3 pagetable PAGE_DATA_TYPE_L4TAB = (long(0x4) << PAGE_DATA_TYPE_SHIFT) # L4 pagetable PAGE_DATA_TYPE_BROKEN = (long(0xd) << PAGE_DATA_TYPE_SHIFT) # Broken PAGE_DATA_TYPE_XALLOC = (long(0xe) << PAGE_DATA_TYPE_SHIFT) # Allocate-only PAGE_DATA_TYPE_XTAB = (long(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" # tsc_info TSC_INFO_FORMAT = "IIQII" # hvm_params HVM_PARAMS_ENTRY_FORMAT = "QQ" HVM_PARAMS_FORMAT = "II" class VerifyLibxc(VerifyBase): """ Verify a Libxc v2 stream """ def __init__(self, info, read): VerifyBase.__init__(self, info, read) 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 version != IHDR_VERSION: raise StreamError("Unknown image version: Expected %d, got %d" % (IHDR_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: %s endian" % (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 != "\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 """ if len(content) != 4096: raise RecordError("Length expected to be 4906 bytes, not %d" % (len(content), )) def verify_record_tsc_info(self, content): """ tsc info record """ sz = calcsize(TSC_INFO_FORMAT) if len(content) != sz: raise RecordError("Length should be %u bytes" % (sz, )) mode, khz, nsec, incarn, res1 = unpack(TSC_INFO_FORMAT, content) if res1 != 0: raise StreamError("Reserved bits set in 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") 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, } xen-4.9.2/tools/python/xen/__init__.py0000664000175000017500000000000213256712137016031 0ustar smbsmb xen-4.9.2/tools/python/README0000664000175000017500000000033613256712137014020 0ustar smbsmbThe file test.py here is from the Zope project, and is Copyright (c) 2001, 2002 Zope Corporation and Contributors. This file is released under the Zope Public License, version 2.0, a copy of which is in the file ZPL-2.0. xen-4.9.2/tools/python/install-wrap0000775000175000017500000000201313256712137015475 0ustar smbsmb#!/bin/sh # usage: # .../install-wrap $(PYTHON_PATH) install ... # where # PYTHON_PATH is what to put after #! and may be `/usr/bin/env python' # # Used via $(INSTALL_PYTHON_PROG) in Rules.mk; PYTHON_PATH comes from $(PYTHON) set -e if test $# -lt 2; then echo >&2 "${0##*/}: too few arguments" exit 1 fi pythonpath="$1" shift install="$1" shift srcs="" while [ $# != 0 ]; do case "$1" in -|--) install=`echo "${install} $1"` shift break ;; -*) install=`echo "${install} $1"` shift ;; *) break ;; esac done while test $# -gt 1; do srcs=`echo "${srcs} $1"` shift done dest="$1" shift destf="$dest" for srcf in ${srcs}; do if test -d "$dest"; then destf="$dest/${srcf%%*/}" fi org="$(sed -n '2q; /^#! *\/usr\/bin\/env python *$/p' $srcf)" if test "x$org" = x; then eval "${install} $srcf $destf" continue fi tmpf="$destf.tmp" eval "${install} $srcf $tmpf" printf >"$tmpf" "#!%s\n" "$pythonpath" sed -e 1d "$srcf" >>"$tmpf" mv -f "$tmpf" "$destf" done exit 0 xen-4.9.2/tools/python/scripts/0000775000175000017500000000000013256712137014625 5ustar smbsmbxen-4.9.2/tools/python/scripts/verify-stream-v20000775000175000017500000001141513256712137017677 0ustar smbsmb#!/usr/bin/env python # -*- coding: utf-8 -*- """ Verify a v2 format migration stream """ import sys import struct import os, os.path import syslog import traceback from xen.migration.verify import StreamError, RecordError from xen.migration.libxc import VerifyLibxc from xen.migration.libxl import VerifyLibxl fin = None # Input file/fd log_to_syslog = False # Boolean - Log to syslog instead of stdout/err? verbose = False # Boolean - Summarise stream contents quiet = False # Boolean - Suppress error printing def info(msg): """Info message, routed to appropriate destination""" if not quiet and verbose: if log_to_syslog: for line in msg.split("\n"): syslog.syslog(syslog.LOG_INFO, line) else: print msg def err(msg): """Error message, routed to appropriate destination""" if not quiet: if log_to_syslog: for line in msg.split("\n"): syslog.syslog(syslog.LOG_ERR, line) print >> sys.stderr, msg def stream_read(_ = None): """Read from input""" return fin.read(_) 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 skip_xl_header(): """Skip over an xl header in the stream""" hdr = rdexact(32) if hdr != "Xen saved domain, xl format\n \0 \r": raise StreamError("No xl header") _, mflags, _, optlen = unpack_exact("=IIII") _ = rdexact(optlen) info("Processed xl header") if mflags & 2: # XL_MANDATORY_FLAG_STREAMv2 return "libxl" else: return "libxc" def read_stream(fmt): """ Read an entire stream """ try: if fmt == "xl": fmt = skip_xl_header() if fmt == "libxc": VerifyLibxc(info, stream_read).verify() else: VerifyLibxl(info, stream_read).verify() except (IOError, StreamError, RecordError): err("Stream Error:") err(traceback.format_exc()) return 1 except StandardError: err("Script Error:") err(traceback.format_exc()) err("Please fix me") return 2 return 0 def open_file_or_fd(val, mode, buffering): """ 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, buffering) else: return open(val, mode, buffering) except StandardError, e: if fd != -1: err("Unable to open fd %d: %s: %s" % (fd, e.__class__.__name__, e)) else: err("Unable to open file '%s': %s: %s" % (val, e.__class__.__name__, e)) raise SystemExit(2) def main(): """ main """ from optparse import OptionParser global fin, quiet, verbose # Change stdout to be line-buffered. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) parser = OptionParser(usage = "%prog [options]", description = "Verify a stream according to the v2 spec") # Optional options parser.add_option("-i", "--in", dest = "fin", metavar = "", default = "0", help = "Stream to verify (defaults to stdin)") parser.add_option("-v", "--verbose", action = "store_true", default = False, help = "Summarise stream contents") parser.add_option("-q", "--quiet", action = "store_true", default = False, help = "Suppress all logging/errors") parser.add_option("-f", "--format", dest = "format", metavar = "", default = "libxc", choices = ["libxc", "libxl", "xl"], help = "Format of the incoming stream (defaults to libxc)") parser.add_option("--syslog", action = "store_true", default = False, help = "Log to syslog instead of stdout") opts, _ = parser.parse_args() if opts.syslog: global log_to_syslog syslog.openlog("verify-stream-v2", syslog.LOG_PID) log_to_syslog = True verbose = opts.verbose quiet = opts.quiet fin = open_file_or_fd(opts.fin, "rb", 0) return read_stream(opts.format) if __name__ == "__main__": try: sys.exit(main()) except SystemExit, e: sys.exit(e.code) except KeyboardInterrupt: sys.exit(2) xen-4.9.2/tools/python/scripts/convert-legacy-stream0000775000175000017500000005411513256712137020774 0ustar smbsmb#!/usr/bin/env python # -*- coding: utf-8 -*- """ Convert a legacy migration stream to a v2 stream. """ import sys import os, os.path import syslog import traceback from struct import calcsize, unpack, pack from xen.migration import legacy, public, libxc, libxl, xl __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) qemu = True # Boolean - process qemu record? log_to_syslog = False # Boolean - Log to syslog instead of stdout/err? verbose = False # Boolean - Summarise stream contents def stream_read(_ = None): """Read from the input""" return fin.read(_) def stream_write(_): """Write to the output""" return fout.write(_) def info(msg): """Info message, routed to appropriate destination""" if verbose: if log_to_syslog: for line in msg.split("\n"): syslog.syslog(syslog.LOG_INFO, line) else: print msg def err(msg): """Error message, routed to appropriate destination""" if log_to_syslog: for line in msg.split("\n"): syslog.syslog(syslog.LOG_ERR, line) print >> sys.stderr, msg class StreamError(StandardError): """Error with the incoming migration stream""" pass class VM(object): """Container of VM parameters""" def __init__(self, fmt): # 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 # libxl self.libxl = fmt == "libxl" self.emu_xenstore = "" # NUL terminated key&val pairs from "toolstack" records def write_libxc_ihdr(): stream_write(pack(libxc.IHDR_FORMAT, libxc.IHDR_MARKER, # Marker libxc.IHDR_IDENT, # Ident libxc.IHDR_VERSION, # Version libxc.IHDR_OPT_LE, # Options 0, 0)) # Reserved def write_libxc_dhdr(): if pv: dtype = libxc.DHDR_TYPE_x86_pv else: dtype = libxc.DHDR_TYPE_x86_hvm stream_write(pack(libxc.DHDR_FORMAT, dtype, # Type 12, # Page size 0, # Reserved 0, # Xen major (converted) __version__)) # Xen minor (converted) def write_libxl_hdr(): stream_write(pack(libxl.HDR_FORMAT, libxl.HDR_IDENT, # Ident libxl.HDR_VERSION, # Version 2 libxl.HDR_OPT_LE | # Options libxl.HDR_OPT_LEGACY # Little Endian and Legacy )) def write_record(rt, *argl): alldata = ''.join(argl) length = len(alldata) record = pack(libxc.RH_FORMAT, rt, length) + alldata plen = (8 - (length & 7)) & 7 record += '\x00' * plen stream_write(record) def write_libxc_pv_info(vm): write_record(libxc.REC_TYPE_x86_pv_info, pack(libxc.X86_PV_INFO_FORMAT, vm.width, vm.levels, 0, 0)) def write_libxc_pv_p2m_frames(vm, pfns): write_record(libxc.REC_TYPE_x86_pv_p2m_frames, pack(libxc.X86_PV_P2M_FRAMES_FORMAT, 0, vm.p2m_size - 1), pack("Q" * len(pfns), *pfns)) def write_libxc_pv_vcpu_basic(vcpu_id, data): write_record(libxc.REC_TYPE_x86_pv_vcpu_basic, pack(libxc.X86_PV_VCPU_HDR_FORMAT, vcpu_id, 0), data) def write_libxc_pv_vcpu_extd(vcpu_id, data): write_record(libxc.REC_TYPE_x86_pv_vcpu_extended, pack(libxc.X86_PV_VCPU_HDR_FORMAT, vcpu_id, 0), data) def write_libxc_pv_vcpu_xsave(vcpu_id, data): write_record(libxc.REC_TYPE_x86_pv_vcpu_xsave, pack(libxc.X86_PV_VCPU_HDR_FORMAT, vcpu_id, 0), data) def write_page_data(pfns, pages): if fout is None: # Save 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(pack(libxc.RH_FORMAT, libxc.REC_TYPE_page_data, 8 + (len(new_pfns) * 8) + len(pages))) stream_write(pack(libxc.PAGE_DATA_FORMAT, len(new_pfns), 0)) stream_write(pack("Q" * len(new_pfns), *new_pfns)) stream_write(pages) def write_libxc_tsc_info(mode, khz, nsec, incarn): write_record(libxc.REC_TYPE_tsc_info, pack(libxc.TSC_INFO_FORMAT, mode, khz, nsec, incarn, 0)) def write_libxc_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(libxc.REC_TYPE_hvm_params, pack(libxc.HVM_PARAMS_FORMAT, len(params) / 2, 0), pack("Q" * len(params), *params)) def write_libxl_end(): write_record(libxl.REC_TYPE_end, "") def write_libxl_libxc_context(): write_record(libxl.REC_TYPE_libxc_context, "") def write_libxl_emulator_xenstore_data(data): write_record(libxl.REC_TYPE_emulator_xenstore_data, pack(libxl.EMULATOR_HEADER_FORMAT, libxl.EMULATOR_ID_unknown, 0) + data) def write_libxl_emulator_context(blob): write_record(libxl.REC_TYPE_emulator_context, pack(libxl.EMULATOR_HEADER_FORMAT, libxl.EMULATOR_ID_unknown, 0) + blob) 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 = calcsize(fmt) return 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 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 info("Extended Info: length 0x%x" % (total_length, )) while so_far < total_length: blkid, datasz = unpack_exact("=4sI") so_far += 8 info(" 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 info(" 64bit domain, 4 levels") elif datasz == 0xaf0: vm.width = 4 vm.levels = 3 info(" 32bit domain, 3 levels") else: raise StreamError("Unable to determine guest width/level") write_libxc_pv_info(vm) elif blkid == "extv": vm.extd = True elif blkid == "xcnt": vm.xsave_len, = unpack("I", data[:4]) info("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 info("P2M frames: fpp %d, p2m_frame_len %d" % (fpp, p2m_frame_len)) write_libxc_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) info("discarding %d bogus 'unmapped pfns'" % (nr_unmapped_pfns, )) for vcpu_id in vm.online_vcpu_map: basic = rdexact(vm.basic_len) info("Got VCPU basic (size 0x%x)" % (vm.basic_len, )) write_libxc_pv_vcpu_basic(vcpu_id, basic) if vm.extd: extd = rdexact(128) info("Got VCPU extd (size 0x%x)" % (128, )) write_libxc_pv_vcpu_extd(vcpu_id, extd) if vm.xsave_len: mask, size = unpack_exact("QQ") assert vm.xsave_len - 16 == size xsave = rdexact(size) info("Got VCPU xsave (mask 0x%x, size 0x%x)" % (mask, size)) write_libxc_pv_vcpu_xsave(vcpu_id, xsave) shinfo = rdexact(4096) info("Got shinfo") write_record(libxc.REC_TYPE_shared_info, shinfo) write_record(libxc.REC_TYPE_end, "") def read_libxl_toolstack(vm, data): if len(data) < 8: raise StreamError("Overly short libxl toolstack data") ver, count = unpack("=II", data[:8]) data = data[8:] if ver != 1: raise StreamError("Cannot decode libxl toolstack version %u" % (ver, )) info(" Version %u, count %u" % (ver, count)) for x in range(count): if len(data) < 28: raise StreamError("Remaining data too short for physmap header") phys, start, size, namelen = unpack("=QQQI", data[:28]) data = data[28:] if namelen == 0: raise StreamError("No physmap info name") # 64bit leaked 4 bytes of padding onto the end of name if twidth == 64: namelen += 4 if len(data) < namelen: raise StreamError("Remaining data too short for physmap name") name = data[:namelen] data = data[namelen:] # Strip padding off the end of name if twidth == 64: name = name[:-4] if name[-1] != '\x00': raise StreamError("physmap name not NUL terminated") root = "physmap/%x" % (phys,) kv = [root + "/start_addr", "%x" % (start, ), root + "/size", "%x" % (size, ), root + "/name", name[:-1]] for key, val in zip(kv[0::2], kv[1::2]): info(" '%s' = '%s'" % (key, val)) vm.emu_xenstore += '\x00'.join(kv) + '\x00' def read_chunks(vm): hvm_params = [] while True: marker, = unpack_exact("=i") if marker <= 0: info("Chunk: %d - %s" % (marker, legacy.chunk_type_to_str.get(marker, "unknown"))) if marker == legacy.CHUNK_end: info(" End") if hvm_params: write_libxc_hvm_params(hvm_params) return elif marker > 0: if marker > legacy.MAX_BATCH: raise StreamError("Page batch (%d) exceeded MAX_BATCH (%d)" % (marker, legacy.MAX_BATCH)) 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") nr_pages = len([x for x in pfns if (x & 0xf0000000) < 0xd0000000]) pages = rdexact(nr_pages * 4096) write_page_data(pfns, pages) elif marker == legacy.CHUNK_enable_verify_mode: info("This is a debug stream") elif marker == legacy.CHUNK_vcpu_info: max_id, = unpack_exact("i") if max_id > legacy.MAX_VCPU_ID: raise StreamError("Vcpu max_id out of range: %d > %d" % (max_id, legacy.MAX_VCPU_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 info(" Vcpu info: max_id %d, online map %s" % (vm.max_vcpu_id, vm.online_vcpu_map)) elif marker == legacy.CHUNK_hvm_ident_pt: _, ident_pt = unpack_exact("=IQ") info(" EPT Identity Pagetable: 0x%x" % (ident_pt, )) hvm_params.extend([public.HVM_PARAM_IDENT_PT, ident_pt]) elif marker == legacy.CHUNK_hvm_vm86_tss: _, vm86_tss = unpack_exact("=IQ") info(" VM86 TSS: 0x%x" % (vm86_tss, )) hvm_params.extend([public.HVM_PARAM_VM86_TSS, vm86_tss]) elif marker == legacy.CHUNK_tmem: raise RuntimeError("todo") elif marker == legacy.CHUNK_tmem_extra: raise RuntimeError("todo") elif marker == legacy.CHUNK_tsc_info: mode, nsec, khz, incarn = unpack_exact("=IQII") info(" TSC_INFO: mode %s, %d ns, %d khz, %d incarn" % (mode, nsec, khz, incarn)) write_libxc_tsc_info(mode, khz, nsec, incarn) elif marker == legacy.CHUNK_hvm_console_pfn: _, console_pfn = unpack_exact("=IQ") info(" Console pfn: 0x%x" % (console_pfn, )) hvm_params.extend([public.HVM_PARAM_CONSOLE_PFN, console_pfn]) elif marker == legacy.CHUNK_last_checkpoint: info(" Last Checkpoint") # Nothing to do elif marker == legacy.CHUNK_hvm_acpi_ioports_location: _, loc = unpack_exact("=IQ") info(" ACPI ioport location: 0x%x" % (loc, )) hvm_params.extend([public.HVM_PARAM_ACPI_IOPORTS_LOCATION, loc]) elif marker == legacy.CHUNK_hvm_viridian: _, loc = unpack_exact("=IQ") info(" Viridian location: 0x%x" % (loc, )) hvm_params.extend([public.HVM_PARAM_VIRIDIAN, loc]) elif marker == legacy.CHUNK_compressed_data: sz, = unpack_exact("I") data = rdexact(sz) info(" Compressed Data: sz 0x%x" % (sz, )) raise RuntimeError("todo") elif marker == legacy.CHUNK_enable_compression: raise RuntimeError("todo") elif marker == legacy.CHUNK_hvm_generation_id_addr: _, genid_loc = unpack_exact("=IQ") info(" Generation ID Address: 0x%x" % (genid_loc, )) hvm_params.extend( [public.HVM_PARAM_VM_GENERATION_ID_ADDR, genid_loc]) elif marker == legacy.CHUNK_hvm_paging_ring_pfn: _, pfn = unpack_exact("=IQ") info(" Paging ring pfn: 0x%x" % (pfn, )) hvm_params.extend([public.HVM_PARAM_PAGING_RING_PFN, pfn]) elif marker == legacy.CHUNK_hvm_monitor_ring_pfn: _, pfn = unpack_exact("=IQ") info(" Monitor ring pfn: 0x%x" % (pfn, )) hvm_params.extend([public.HVM_PARAM_MONITOR_RING_PFN, pfn]) elif marker == legacy.CHUNK_hvm_sharing_ring_pfn: _, pfn = unpack_exact("=IQ") info(" Sharing ring pfn: 0x%x" % (pfn, )) hvm_params.extend([public.HVM_PARAM_SHARING_RING_PFN, pfn]) elif marker == legacy.CHUNK_toolstack: sz, = unpack_exact("I") if sz: data = rdexact(sz) info(" Toolstack Data: sz 0x%x" % (sz, )) if vm.libxl: read_libxl_toolstack(vm, data) else: info(" Discarding") elif marker == legacy.CHUNK_hvm_ioreq_server_pfn: _, pfn = unpack_exact("=IQ") info(" IOREQ server pfn: 0x%x" % (pfn, )) hvm_params.extend([public.HVM_PARAM_IOREQ_SERVER_PFN, pfn]) elif marker == legacy.CHUNK_hvm_nr_ioreq_server_pages: _, nr_pages = unpack_exact("=IQ") info(" IOREQ server pages: %d" % (nr_pages, )) hvm_params.extend( [public.HVM_PARAM_NR_IOREQ_SERVER_PAGES, nr_pages]) else: raise StreamError("Unrecognised chunk %d" % (marker,)) def read_hvm_tail(vm): io, bufio, store = unpack_exact("QQQ") info("Magic pfns: 0x%x 0x%x 0x%x" % (io, bufio, store)) write_libxc_hvm_params([public.HVM_PARAM_IOREQ_PFN, io, public.HVM_PARAM_BUFIOREQ_PFN, bufio, public.HVM_PARAM_STORE_PFN, store]) blobsz, = unpack_exact("I") info("Got HVM Context (0x%x bytes)" % (blobsz, )) blob = rdexact(blobsz) write_record(libxc.REC_TYPE_hvm_context, blob) write_record(libxc.REC_TYPE_end, "") def read_qemu(vm): rawsig = rdexact(21) sig, = unpack("21s", rawsig) info("Qemu signature: %s" % (sig, )) if sig == "DeviceModelRecord0002": rawsz = rdexact(4) sz, = unpack("I", rawsz) qdata = rdexact(sz) if vm.libxl: write_libxl_emulator_context(qdata) else: stream_write(rawsig) stream_write(rawsz) stream_write(qdata) else: raise RuntimeError("Unrecognised Qemu sig '%s'" % (sig, )) def skip_xl_header(fmt): """Skip over an xl header in the stream""" hdr = rdexact(len(xl.MAGIC)) if hdr != xl.MAGIC: raise StreamError("No xl header") byteorder, mflags, oflags, optlen = unpack_exact(xl.HEADER_FORMAT) if fmt == "libxl": mflags |= xl.MANDATORY_FLAG_STREAMV2 opts = pack(xl.HEADER_FORMAT, byteorder, mflags, oflags, optlen) optdata = rdexact(optlen) info("Processed xl header") stream_write(hdr) stream_write(opts) stream_write(optdata) def read_legacy_stream(vm): try: vm.p2m_size, = unpack_ulongs(1) info("P2M Size: 0x%x" % (vm.p2m_size,)) if vm.libxl: write_libxl_hdr() write_libxl_libxc_context() write_libxc_ihdr() write_libxc_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) if vm.libxl and len(vm.emu_xenstore): write_libxl_emulator_xenstore_data(vm.emu_xenstore) if not pv and (vm.libxl or qemu): read_qemu(vm) if vm.libxl: write_libxl_end() except (IOError, StreamError): err("Stream Error:") err(traceback.format_exc()) return 1 except RuntimeError: err("Script Error:") err(traceback.format_exc()) err("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, 0) else: return open(val, mode, 0) except StandardError, e: if fd != -1: err("Unable to open fd %d: %s: %s" % (fd, e.__class__.__name__, e)) else: err("Unable to open file '%s': %s: %s" % (val, e.__class__.__name__, e)) raise SystemExit(1) def main(): from optparse import OptionParser global fin, fout, twidth, pv, qemu, verbose # Change stdout to be line-buffered. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) parser = OptionParser(version = __version__, usage = ("%prog [options] -i INPUT -o OUTPUT" " -w WIDTH -g GUEST"), description = "Convert a legacy stream to a v2 stream") # Required options parser.add_option("-i", "--in", dest = "fin", metavar = "", help = "Legacy input to convert") parser.add_option("-o", "--out", dest = "fout", metavar = "", help = "v2 format output") parser.add_option("-w", "--width", dest = "twidth", metavar = "<32/64>", choices = ["32", "64"], help = "Legacy toolstack bitness") parser.add_option("-g", "--guest-type", dest = "gtype", metavar = "", choices = ["pv", "hvm"], help = "Type of guest in stream") # Optional options parser.add_option("-f", "--format", dest = "format", metavar = "", default = "libxc", choices = ["libxc", "libxl"], help = "Desired format of the outgoing stream " \ "(defaults to libxc)") parser.add_option("-v", "--verbose", action = "store_true", default = False, help = "Summarise stream contents") parser.add_option("-x", "--xl", action = "store_true", default = False, help = ("Is an `xl` header present in the stream?" " (default no)")) parser.add_option("--skip-qemu", action = "store_true", default = False, help = ("Skip processing of the qemu tail?" " (default no)")) parser.add_option("--syslog", action = "store_true", default = False, help = "Log to syslog instead of stdout") opts, _ = 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) if opts.syslog: global log_to_syslog syslog.openlog("convert-legacy-stream", syslog.LOG_PID) log_to_syslog = True fin = open_file_or_fd(opts.fin, "rb") fout = open_file_or_fd(opts.fout, "wb") twidth = int(opts.twidth) pv = opts.gtype == "pv" verbose = opts.verbose if opts.skip_qemu: qemu = False if opts.xl: skip_xl_header(opts.format) rc = read_legacy_stream(VM(opts.format)) fout.close() return rc if __name__ == "__main__": try: sys.exit(main()) except SystemExit, e: sys.exit(e.code) except KeyboardInterrupt: sys.exit(1) xen-4.9.2/tools/xcutils/0000775000175000017500000000000013256712137013310 5ustar smbsmbxen-4.9.2/tools/xcutils/readnotes.c0000664000175000017500000001737613256712137015456 0ustar smbsmb#include #include #include #include #include #include #include #include #include #include #include #include #include /* gunzip bits */ #include static xc_interface *xch; /* According to the implemation of xc_dom_probe_bzimage_kernel() function */ /* We add support of bzImage kernel */ /* Copied from tools/libxc/xc_doom_bzImageloader.c */ struct setup_header { uint8_t _pad0[0x1f1]; /* skip uninteresting stuff */ uint8_t setup_sects; uint16_t root_flags; uint32_t syssize; uint16_t ram_size; uint16_t vid_mode; uint16_t root_dev; uint16_t boot_flag; uint16_t jump; uint32_t header; #define HDR_MAGIC "HdrS" #define HDR_MAGIC_SZ 4 uint16_t version; #define VERSION(h,l) (((h)<<8) | (l)) uint32_t realmode_swtch; uint16_t start_sys; uint16_t kernel_version; uint8_t type_of_loader; uint8_t loadflags; uint16_t setup_move_size; uint32_t code32_start; uint32_t ramdisk_image; uint32_t ramdisk_size; uint32_t bootsect_kludge; uint16_t heap_end_ptr; uint16_t _pad1; uint32_t cmd_line_ptr; uint32_t initrd_addr_max; uint32_t kernel_alignment; uint8_t relocatable_kernel; uint8_t _pad2[3]; uint32_t cmdline_size; uint32_t hardware_subarch; uint64_t hardware_subarch_data; uint32_t payload_offset; uint32_t payload_length; } __attribute__((packed)); static void print_string_note(const char *prefix, struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) { printf("%s: %s\n", prefix, elf_strfmt(elf, elf_note_desc(elf, note))); } static void print_numeric_note(const char *prefix, struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) { uint64_t value = elf_note_numeric(elf, note); unsigned descsz = elf_uval(elf, note, descsz); printf("%s: %#*" PRIx64 " (%d bytes)\n", prefix, 2+2*descsz, value, descsz); } static void print_l1_mfn_valid_note(const char *prefix, struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) { unsigned descsz = elf_uval(elf, note, descsz); elf_ptrval desc = elf_note_desc(elf, note); /* XXX should be able to cope with a list of values. */ switch ( descsz / 2 ) { case 8: printf("%s: mask=%#"PRIx64" value=%#"PRIx64"\n", prefix, elf_access_unsigned(elf, desc, 0, 8), elf_access_unsigned(elf, desc, 8, 8)); break; case 4: printf("%s: mask=%#"PRIx32" value=%#"PRIx32"\n", prefix, (uint32_t)elf_access_unsigned(elf, desc, 0, 4), (uint32_t)elf_access_unsigned(elf, desc, 4, 4)); break; } } static unsigned print_notes(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) start, ELF_HANDLE_DECL(elf_note) end) { ELF_HANDLE_DECL(elf_note) note; unsigned notes_found = 0; const char *this_note_name; for ( note = start; ELF_HANDLE_PTRVAL(note) < ELF_HANDLE_PTRVAL(end); note = elf_note_next(elf, note) ) { this_note_name = elf_note_name(elf, note); if (NULL == this_note_name) continue; if (0 != strcmp(this_note_name, "Xen")) continue; notes_found++; switch(elf_uval(elf, note, type)) { case XEN_ELFNOTE_INFO: print_string_note("INFO", elf , note); break; case XEN_ELFNOTE_ENTRY: print_numeric_note("ENTRY", elf , note); break; case XEN_ELFNOTE_HYPERCALL_PAGE: print_numeric_note("HYPERCALL_PAGE", elf , note); break; case XEN_ELFNOTE_VIRT_BASE: print_numeric_note("VIRT_BASE", elf , note); break; case XEN_ELFNOTE_PADDR_OFFSET: print_numeric_note("PADDR_OFFSET", elf , note); break; case XEN_ELFNOTE_XEN_VERSION: print_string_note("XEN_VERSION", elf , note); break; case XEN_ELFNOTE_GUEST_OS: print_string_note("GUEST_OS", elf , note); break; case XEN_ELFNOTE_GUEST_VERSION: print_string_note("GUEST_VERSION", elf , note); break; case XEN_ELFNOTE_LOADER: print_string_note("LOADER", elf , note); break; case XEN_ELFNOTE_PAE_MODE: print_string_note("PAE_MODE", elf , note); break; case XEN_ELFNOTE_FEATURES: print_string_note("FEATURES", elf , note); break; case XEN_ELFNOTE_HV_START_LOW: print_numeric_note("HV_START_LOW", elf , note); break; case XEN_ELFNOTE_SUSPEND_CANCEL: print_numeric_note("SUSPEND_CANCEL", elf, note); break; case XEN_ELFNOTE_L1_MFN_VALID: print_l1_mfn_valid_note("L1_MFN_VALID", elf , note); break; case XEN_ELFNOTE_PHYS32_ENTRY: print_numeric_note("PHYS32_ENTRY", elf , note); break; default: printf("unknown note type %#x\n", (unsigned)elf_uval(elf, note, type)); break; } } return notes_found; } int main(int argc, char **argv) { const char *f; int fd; unsigned h,size,usize,count; void *image,*tmp; struct stat st; struct elf_binary elf; ELF_HANDLE_DECL(elf_shdr) shdr; unsigned notes_found = 0; struct setup_header *hdr; uint64_t payload_offset, payload_length; if (argc != 2) { fprintf(stderr, "Usage: readnotes \n"); return 1; } f = argv[1]; xch = xc_interface_open(0,0,XC_OPENFLAG_DUMMY); fd = open(f, O_RDONLY); if (fd == -1) { fprintf(stderr, "Unable to open %s: %s\n", f, strerror(errno)); return 1; } if (fstat(fd, &st) == -1) { fprintf(stderr, "Unable to determine size of %s: %s\n", f, strerror(errno)); return 1; } image = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (image == MAP_FAILED) { fprintf(stderr, "Unable to map %s: %s\n", f, strerror(errno)); return 1; } /* Check the magic of bzImage kernel */ hdr = (struct setup_header *)image; if ( memcmp(&hdr->header, HDR_MAGIC, HDR_MAGIC_SZ) == 0 ) { if ( hdr->version < VERSION(2,8) ) { printf("%s: boot protocol too old (%04x)", __FUNCTION__, hdr->version); return 1; } /* upcast to 64 bits to avoid overflow */ /* setup_sects is u8 and so cannot overflow */ payload_offset = (hdr->setup_sects + 1) * 512; payload_offset += hdr->payload_offset; payload_length = hdr->payload_length; if ( payload_offset >= st.st_size ) { printf("%s: payload offset overflow", __FUNCTION__); return 1; } if ( (payload_offset + payload_length) > st.st_size ) { printf("%s: payload length overflow", __FUNCTION__); return 1; } image = image + payload_offset; size = payload_length; } else { size = st.st_size; } usize = xc_dom_check_gzip(xch, image, size); if (usize) { tmp = malloc(usize); xc_dom_do_gunzip(xch, image, size, tmp, usize); image = tmp; size = usize; } if (0 != elf_init(&elf, image, size)) { fprintf(stderr, "File %s is not an ELF image\n", f); return 1; } xc_elf_set_logfile(xch, &elf, 0); count = elf_phdr_count(&elf); for ( h=0; h < count; h++) { ELF_HANDLE_DECL(elf_phdr) phdr; phdr = elf_phdr_by_index(&elf, h); if (elf_uval(&elf, phdr, p_type) != PT_NOTE) continue; /* Some versions of binutils do not correctly set * p_offset for note segments. */ if (elf_uval(&elf, phdr, p_offset) == 0) continue; notes_found = print_notes(&elf, ELF_MAKE_HANDLE(elf_note, elf_segment_start(&elf, phdr)), ELF_MAKE_HANDLE(elf_note, elf_segment_end(&elf, phdr))); } if ( notes_found == 0 ) { count = elf_shdr_count(&elf); for ( h=0; h < count; h++) { ELF_HANDLE_DECL(elf_shdr) shdr; shdr = elf_shdr_by_index(&elf, h); if (elf_uval(&elf, shdr, sh_type) != SHT_NOTE) continue; notes_found = print_notes(&elf, ELF_MAKE_HANDLE(elf_note, elf_section_start(&elf, shdr)), ELF_MAKE_HANDLE(elf_note, elf_section_end(&elf, shdr))); if ( notes_found ) fprintf(stderr, "using notes from SHT_NOTE section\n"); } } shdr = elf_shdr_by_name(&elf, "__xen_guest"); if (ELF_HANDLE_VALID(shdr)) printf("__xen_guest: %s\n", elf_strfmt(&elf, elf_section_start(&elf, shdr))); if (elf_check_broken(&elf)) printf("warning: broken ELF: %s\n", elf_check_broken(&elf)); return 0; } xen-4.9.2/tools/xcutils/Makefile0000664000175000017500000000211113256712137014743 0ustar smbsmb# # tools/xcutils/Makefile # # This file is subject to the terms and conditions of the GNU General # Public License. See the file "COPYING" in the main directory of # this archive for more details. # # Copyright (C) 2005 by Christian Limpach # XEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk PROGRAMS = readnotes lsevtchn CFLAGS += -Werror # incorrectly uses libxc internals CFLAGS_readnotes.o := $(CFLAGS_libxenevtchn) $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest) -I$(XEN_ROOT)/tools/libxc $(CFLAGS_libxencall) CFLAGS_lsevtchn.o := $(CFLAGS_libxenevtchn) $(CFLAGS_libxenctrl) .PHONY: all all: build .PHONY: build build: $(PROGRAMS) readnotes: readnotes.o $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(APPEND_LDFLAGS) lsevtchn: lsevtchn.o $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS) .PHONY: install install: build $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN) $(INSTALL_PROG) $(PROGRAMS) $(DESTDIR)$(LIBEXEC_BIN) .PHONY: clean clean: $(RM) *.o $(PROGRAMS) $(RM) $(DEPS) .PHONY: distclean distclean: clean -include $(DEPS) xen-4.9.2/tools/xcutils/lsevtchn.c0000664000175000017500000000301613256712137015302 0ustar smbsmb#include #include #include #include #include #include int main(int argc, char **argv) { xc_interface *xch; int domid, port, rc; xc_evtchn_status_t status; domid = (argc > 1) ? strtol(argv[1], NULL, 10) : 0; xch = xc_interface_open(0,0,0); if ( !xch ) errx(1, "failed to open control interface"); for ( port = 0; ; port++ ) { status.dom = domid; status.port = port; rc = xc_evtchn_status(xch, &status); if ( rc < 0 ) break; if ( status.status == EVTCHNSTAT_closed ) continue; printf("%4d: VCPU %u: ", port, status.vcpu); switch ( status.status ) { case EVTCHNSTAT_unbound: printf("Interdomain (Waiting connection) - Remote Domain %u", status.u.unbound.dom); break; case EVTCHNSTAT_interdomain: printf("Interdomain (Connected) - Remote Domain %u, Port %u", status.u.interdomain.dom, status.u.interdomain.port); break; case EVTCHNSTAT_pirq: printf("Physical IRQ %u", status.u.pirq); break; case EVTCHNSTAT_virq: printf("Virtual IRQ %u", status.u.virq); break; case EVTCHNSTAT_ipi: printf("IPI"); break; default: printf("Unknown"); break; } printf("\n"); } xc_interface_close(xch); return 0; } xen-4.9.2/tools/golang/0000775000175000017500000000000013256712137013064 5ustar smbsmbxen-4.9.2/tools/golang/Makefile0000664000175000017500000000140313256712137014522 0ustar smbsmbXEN_ROOT=$(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk # In order to link against a package in Go, the package must live in a # directory tree in the way that Go expects. To make this possible, # there must be a directory such that we can set GOPATH=${dir}, and # the package will be under $GOPATH/src/${full-package-path}. # So we set XEN_GOPATH to $XEN_ROOT/tools/golang. The xenlight # "package build" directory ($PWD/xenlight) will create the "package # source" directory in the proper place. Go programs can use this # package by setting GOPATH=$(XEN_GOPATH). SUBDIRS-y = xenlight .PHONY: build all all build: subdirs-all .PHONY: install install: subdirs-install .PHONY: clean clean: subdirs-clean $(RM) -r src pkg .PHONY: distclean distclean: clean xen-4.9.2/tools/golang/xenlight/0000775000175000017500000000000013256712137014706 5ustar smbsmbxen-4.9.2/tools/golang/xenlight/Makefile0000664000175000017500000000315713256712137016354 0ustar smbsmbXEN_ROOT=$(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk # Standing boldly against convention, we insist on installing the # package source under $(prefix)/share/gocode GOCODE_DIR ?= $(prefix)/share/gocode/ GOXL_PKG_DIR = /src/$(XEN_GOCODE_URL)/xenlight/ GOXL_INSTALL_DIR = $(GOCODE_DIR)$(GOXL_PKG_DIR) # PKGSOURCES: Files which comprise the distributed source package PKGSOURCES = xenlight.go GO ?= go .PHONY: all all: build .PHONY: package package: $(XEN_GOPATH)$(GOXL_PKG_DIR)$(PKGSOURCES) $(XEN_GOPATH)/src/$(XEN_GOCODE_URL)/xenlight/$(PKGSOURCES): $(PKGSOURCES) $(INSTALL_DIR) $(XEN_GOPATH)$(GOXL_PKG_DIR) $(INSTALL_DATA) $(PKGSOURCES) $(XEN_GOPATH)$(GOXL_PKG_DIR) # Go will do its own dependency checking, and not actuall go through # with the build if none of the input files have changed. # # NB that because the users of this library need to be able to # recompile the library from source, it needs to include '-lxenlight' # in the LDFLAGS; and thus we need to add -L$(XEN_XENLIGHT) here # so that it can find the actual library. .PHONY: build build: package CGO_CFLAGS="$(CFLAGS_libxenlight) $(CFLAGS_libxentoollog)" CGO_LDFLAGS="$(LDLIBS_libxenlight) $(LDLIBS_libxentoollog) -L$(XEN_XENLIGHT) -L$(XEN_LIBXENTOOLLOG)" GOPATH=$(XEN_GOPATH) $(GO) install -x $(XEN_GOCODE_URL)/xenlight .PHONY: install install: build $(INSTALL_DIR) $(DESTDIR)$(GOXL_INSTALL_DIR) $(INSTALL_DATA) $(XEN_GOPATH)$(GOXL_PKG_DIR)$(PKGSOURCES) $(DESTDIR)$(GOXL_INSTALL_DIR) .PHONY: clean clean: $(RM) -r $(XEN_GOPATH)$(GOXL_PKG_DIR) $(RM) $(XEN_GOPATH)/pkg/*/$(XEN_GOCODE_URL)/xenlight.a .PHONY: distclean distclean: clean -include $(DEPS) xen-4.9.2/tools/golang/xenlight/xenlight.go0000664000175000017500000006744313256712137017075 0ustar smbsmb/* * Copyright (C) 2016 George W. Dunlap, Citrix Systems UK Ltd * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ package xenlight /* #cgo LDFLAGS: -lxenlight -lyajl -lxentoollog #include #include */ import "C" /* * Other flags that may be needed at some point: * -lnl-route-3 -lnl-3 * * To get back to static linking: * #cgo LDFLAGS: -lxenlight -lyajl_s -lxengnttab -lxenstore -lxenguest -lxentoollog -lxenevtchn -lxenctrl -lblktapctl -lxenforeignmemory -lxencall -lz -luuid -lutil */ import ( "fmt" "time" "unsafe" ) /* * Errors */ type Error int const ( ErrorNonspecific = Error(-C.ERROR_NONSPECIFIC) ErrorVersion = Error(-C.ERROR_VERSION) ErrorFail = Error(-C.ERROR_FAIL) ErrorNi = Error(-C.ERROR_NI) ErrorNomem = Error(-C.ERROR_NOMEM) ErrorInval = Error(-C.ERROR_INVAL) ErrorBadfail = Error(-C.ERROR_BADFAIL) ErrorGuestTimedout = Error(-C.ERROR_GUEST_TIMEDOUT) ErrorTimedout = Error(-C.ERROR_TIMEDOUT) ErrorNoparavirt = Error(-C.ERROR_NOPARAVIRT) ErrorNotReady = Error(-C.ERROR_NOT_READY) ErrorOseventRegFail = Error(-C.ERROR_OSEVENT_REG_FAIL) ErrorBufferfull = Error(-C.ERROR_BUFFERFULL) ErrorUnknownChild = Error(-C.ERROR_UNKNOWN_CHILD) ErrorLockFail = Error(-C.ERROR_LOCK_FAIL) ErrorJsonConfigEmpty = Error(-C.ERROR_JSON_CONFIG_EMPTY) ErrorDeviceExists = Error(-C.ERROR_DEVICE_EXISTS) ErrorCheckpointDevopsDoesNotMatch = Error(-C.ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH) ErrorCheckpointDeviceNotSupported = Error(-C.ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED) ErrorVnumaConfigInvalid = Error(-C.ERROR_VNUMA_CONFIG_INVALID) ErrorDomainNotfound = Error(-C.ERROR_DOMAIN_NOTFOUND) ErrorAborted = Error(-C.ERROR_ABORTED) ErrorNotfound = Error(-C.ERROR_NOTFOUND) ErrorDomainDestroyed = Error(-C.ERROR_DOMAIN_DESTROYED) ErrorFeatureRemoved = Error(-C.ERROR_FEATURE_REMOVED) ) var errors = [...]string{ ErrorNonspecific: "Non-specific error", ErrorVersion: "Wrong version", ErrorFail: "Failed", ErrorNi: "Not Implemented", ErrorNomem: "No memory", ErrorInval: "Invalid argument", ErrorBadfail: "Bad Fail", ErrorGuestTimedout: "Guest timed out", ErrorTimedout: "Timed out", ErrorNoparavirt: "No Paravirtualization", ErrorNotReady: "Not ready", ErrorOseventRegFail: "OS event registration failed", ErrorBufferfull: "Buffer full", ErrorUnknownChild: "Unknown child", ErrorLockFail: "Lock failed", ErrorJsonConfigEmpty: "JSON config empty", ErrorDeviceExists: "Device exists", ErrorCheckpointDevopsDoesNotMatch: "Checkpoint devops does not match", ErrorCheckpointDeviceNotSupported: "Checkpoint device not supported", ErrorVnumaConfigInvalid: "VNUMA config invalid", ErrorDomainNotfound: "Domain not found", ErrorAborted: "Aborted", ErrorNotfound: "Not found", ErrorDomainDestroyed: "Domain destroyed", ErrorFeatureRemoved: "Feature removed", } func (e Error) Error() string { if 0 < int(e) && int(e) < len(errors) { s := errors[e] if s != "" { return s } } return fmt.Sprintf("libxl error: %d", -e) } /* * Types: Builtins */ type Domid uint32 type MemKB uint64 type Uuid C.libxl_uuid type Context struct { ctx *C.libxl_ctx logger *C.xentoollog_logger_stdiostream } type Hwcap []C.uint32_t func (chwcap C.libxl_hwcap) toGo() (ghwcap Hwcap) { // Alloc a Go slice for the bytes size := 8 ghwcap = make([]C.uint32_t, size) // Make a slice pointing to the C array mapslice := (*[1 << 30]C.uint32_t)(unsafe.Pointer(&chwcap[0]))[:size:size] // And copy the C array into the Go array copy(ghwcap, mapslice) return } // typedef struct { // uint32_t size; /* number of bytes in map */ // uint8_t *map; // } libxl_bitmap; // Implement the Go bitmap type such that the underlying data can // easily be copied in and out. NB that we still have to do copies // both directions, because cgo runtime restrictions forbid passing to // a C function a pointer to a Go-allocated structure which contains a // pointer. type Bitmap struct { bitmap []C.uint8_t } /* * Types: IDL * * FIXME: Generate these automatically from the IDL */ type Physinfo struct { ThreadsPerCore uint32 CoresPerSocket uint32 MaxCpuId uint32 NrCpus uint32 CpuKhz uint32 TotalPages uint64 FreePages uint64 ScrubPages uint64 OutstandingPages uint64 SharingFreedPages uint64 SharingUsedFrames uint64 NrNodes uint32 HwCap Hwcap CapHvm bool CapHvmDirectio bool } func (cphys *C.libxl_physinfo) toGo() (physinfo *Physinfo) { physinfo = &Physinfo{} physinfo.ThreadsPerCore = uint32(cphys.threads_per_core) physinfo.CoresPerSocket = uint32(cphys.cores_per_socket) physinfo.MaxCpuId = uint32(cphys.max_cpu_id) physinfo.NrCpus = uint32(cphys.nr_cpus) physinfo.CpuKhz = uint32(cphys.cpu_khz) physinfo.TotalPages = uint64(cphys.total_pages) physinfo.FreePages = uint64(cphys.free_pages) physinfo.ScrubPages = uint64(cphys.scrub_pages) physinfo.ScrubPages = uint64(cphys.scrub_pages) physinfo.SharingFreedPages = uint64(cphys.sharing_freed_pages) physinfo.SharingUsedFrames = uint64(cphys.sharing_used_frames) physinfo.NrNodes = uint32(cphys.nr_nodes) physinfo.HwCap = cphys.hw_cap.toGo() physinfo.CapHvm = bool(cphys.cap_hvm) physinfo.CapHvmDirectio = bool(cphys.cap_hvm_directio) return } type VersionInfo struct { XenVersionMajor int XenVersionMinor int XenVersionExtra string Compiler string CompileBy string CompileDomain string CompileDate string Capabilities string Changeset string VirtStart uint64 Pagesize int Commandline string BuildId string } func (cinfo *C.libxl_version_info) toGo() (info *VersionInfo) { info = &VersionInfo{} info.XenVersionMajor = int(cinfo.xen_version_major) info.XenVersionMinor = int(cinfo.xen_version_minor) info.XenVersionExtra = C.GoString(cinfo.xen_version_extra) info.Compiler = C.GoString(cinfo.compiler) info.CompileBy = C.GoString(cinfo.compile_by) info.CompileDomain = C.GoString(cinfo.compile_domain) info.CompileDate = C.GoString(cinfo.compile_date) info.Capabilities = C.GoString(cinfo.capabilities) info.Changeset = C.GoString(cinfo.changeset) info.VirtStart = uint64(cinfo.virt_start) info.Pagesize = int(cinfo.pagesize) info.Commandline = C.GoString(cinfo.commandline) info.BuildId = C.GoString(cinfo.build_id) return } type ShutdownReason int32 const ( ShutdownReasonUnknown = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_UNKNOWN) ShutdownReasonPoweroff = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_POWEROFF) ShutdownReasonReboot = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_REBOOT) ShutdownReasonSuspend = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_SUSPEND) ShutdownReasonCrash = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_CRASH) ShutdownReasonWatchdog = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_WATCHDOG) ShutdownReasonSoftReset = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_SOFT_RESET) ) func (sr ShutdownReason) String() (str string) { cstr := C.libxl_shutdown_reason_to_string(C.libxl_shutdown_reason(sr)) str = C.GoString(cstr) return } type DomainType int32 const ( DomainTypeInvalid = DomainType(C.LIBXL_DOMAIN_TYPE_INVALID) DomainTypeHvm = DomainType(C.LIBXL_DOMAIN_TYPE_HVM) DomainTypePv = DomainType(C.LIBXL_DOMAIN_TYPE_PV) ) func (dt DomainType) String() (str string) { cstr := C.libxl_domain_type_to_string(C.libxl_domain_type(dt)) str = C.GoString(cstr) return } type Dominfo struct { Uuid Uuid Domid Domid Ssidref uint32 SsidLabel string Running bool Blocked bool Paused bool Shutdown bool Dying bool NeverStop bool ShutdownReason int32 OutstandingMemkb MemKB CurrentMemkb MemKB SharedMemkb MemKB PagedMemkb MemKB MaxMemkb MemKB CpuTime time.Duration VcpuMaxId uint32 VcpuOnline uint32 Cpupool uint32 DomainType int32 } func (cdi *C.libxl_dominfo) toGo() (di *Dominfo) { di = &Dominfo{} di.Uuid = Uuid(cdi.uuid) di.Domid = Domid(cdi.domid) di.Ssidref = uint32(cdi.ssidref) di.SsidLabel = C.GoString(cdi.ssid_label) di.Running = bool(cdi.running) di.Blocked = bool(cdi.blocked) di.Paused = bool(cdi.paused) di.Shutdown = bool(cdi.shutdown) di.Dying = bool(cdi.dying) di.NeverStop = bool(cdi.never_stop) di.ShutdownReason = int32(cdi.shutdown_reason) di.OutstandingMemkb = MemKB(cdi.outstanding_memkb) di.CurrentMemkb = MemKB(cdi.current_memkb) di.SharedMemkb = MemKB(cdi.shared_memkb) di.PagedMemkb = MemKB(cdi.paged_memkb) di.MaxMemkb = MemKB(cdi.max_memkb) di.CpuTime = time.Duration(cdi.cpu_time) di.VcpuMaxId = uint32(cdi.vcpu_max_id) di.VcpuOnline = uint32(cdi.vcpu_online) di.Cpupool = uint32(cdi.cpupool) di.DomainType = int32(cdi.domain_type) return } // # Consistent with values defined in domctl.h // # Except unknown which we have made up // libxl_scheduler = Enumeration("scheduler", [ // (0, "unknown"), // (4, "sedf"), // (5, "credit"), // (6, "credit2"), // (7, "arinc653"), // (8, "rtds"), // ]) type Scheduler int var ( SchedulerUnknown Scheduler = C.LIBXL_SCHEDULER_UNKNOWN SchedulerSedf Scheduler = C.LIBXL_SCHEDULER_SEDF SchedulerCredit Scheduler = C.LIBXL_SCHEDULER_CREDIT SchedulerCredit2 Scheduler = C.LIBXL_SCHEDULER_CREDIT2 SchedulerArinc653 Scheduler = C.LIBXL_SCHEDULER_ARINC653 SchedulerRTDS Scheduler = C.LIBXL_SCHEDULER_RTDS ) // const char *libxl_scheduler_to_string(libxl_scheduler p); func (s Scheduler) String() string { cs := C.libxl_scheduler_to_string(C.libxl_scheduler(s)) // No need to free const return value return C.GoString(cs) } // int libxl_scheduler_from_string(const char *s, libxl_scheduler *e); func (s *Scheduler) FromString(gstr string) (err error) { *s, err = SchedulerFromString(gstr) return } func SchedulerFromString(name string) (s Scheduler, err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) var cs C.libxl_scheduler ret := C.libxl_scheduler_from_string(cname, &cs) if ret != 0 { err = Error(-ret) return } s = Scheduler(cs) return } // libxl_cpupoolinfo = Struct("cpupoolinfo", [ // ("poolid", uint32), // ("pool_name", string), // ("sched", libxl_scheduler), // ("n_dom", uint32), // ("cpumap", libxl_bitmap) // ], dir=DIR_OUT) type CpupoolInfo struct { Poolid uint32 PoolName string Scheduler Scheduler DomainCount int Cpumap Bitmap } func (cci C.libxl_cpupoolinfo) toGo() (gci CpupoolInfo) { gci.Poolid = uint32(cci.poolid) gci.PoolName = C.GoString(cci.pool_name) gci.Scheduler = Scheduler(cci.sched) gci.DomainCount = int(cci.n_dom) gci.Cpumap = cci.cpumap.toGo() return } // libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out); // void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool); func (Ctx *Context) ListCpupool() (list []CpupoolInfo) { err := Ctx.CheckOpen() if err != nil { return } var nbPool C.int c_cpupool_list := C.libxl_list_cpupool(Ctx.ctx, &nbPool) defer C.libxl_cpupoolinfo_list_free(c_cpupool_list, nbPool) if int(nbPool) == 0 { return } // Magic cpupoolListSlice := (*[1 << 30]C.libxl_cpupoolinfo)(unsafe.Pointer(c_cpupool_list))[:nbPool:nbPool] for i := range cpupoolListSlice { info := cpupoolListSlice[i].toGo() list = append(list, info) } return } // int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid); func (Ctx *Context) CpupoolInfo(Poolid uint32) (pool CpupoolInfo) { err := Ctx.CheckOpen() if err != nil { return } var c_cpupool C.libxl_cpupoolinfo ret := C.libxl_cpupool_info(Ctx.ctx, &c_cpupool, C.uint32_t(Poolid)) if ret != 0 { err = Error(-ret) return } defer C.libxl_cpupoolinfo_dispose(&c_cpupool) pool = c_cpupool.toGo() return } // int libxl_cpupool_create(libxl_ctx *ctx, const char *name, // libxl_scheduler sched, // libxl_bitmap cpumap, libxl_uuid *uuid, // uint32_t *poolid); // FIXME: uuid // FIXME: Setting poolid func (Ctx *Context) CpupoolCreate(Name string, Scheduler Scheduler, Cpumap Bitmap) (err error, Poolid uint32) { err = Ctx.CheckOpen() if err != nil { return } poolid := C.uint32_t(C.LIBXL_CPUPOOL_POOLID_ANY) name := C.CString(Name) defer C.free(unsafe.Pointer(name)) // For now, just do what xl does, and make a new uuid every time we create the pool var uuid C.libxl_uuid C.libxl_uuid_generate(&uuid) cbm := Cpumap.toC() defer C.libxl_bitmap_dispose(&cbm) ret := C.libxl_cpupool_create(Ctx.ctx, name, C.libxl_scheduler(Scheduler), cbm, &uuid, &poolid) if ret != 0 { err = Error(-ret) return } Poolid = uint32(poolid) return } // int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid); func (Ctx *Context) CpupoolDestroy(Poolid uint32) (err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_cpupool_destroy(Ctx.ctx, C.uint32_t(Poolid)) if ret != 0 { err = Error(-ret) return } return } // int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu); func (Ctx *Context) CpupoolCpuadd(Poolid uint32, Cpu int) (err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_cpupool_cpuadd(Ctx.ctx, C.uint32_t(Poolid), C.int(Cpu)) if ret != 0 { err = Error(-ret) return } return } // int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid, // const libxl_bitmap *cpumap); func (Ctx *Context) CpupoolCpuaddCpumap(Poolid uint32, Cpumap Bitmap) (err error) { err = Ctx.CheckOpen() if err != nil { return } cbm := Cpumap.toC() defer C.libxl_bitmap_dispose(&cbm) ret := C.libxl_cpupool_cpuadd_cpumap(Ctx.ctx, C.uint32_t(Poolid), &cbm) if ret != 0 { err = Error(-ret) return } return } // int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu); func (Ctx *Context) CpupoolCpuremove(Poolid uint32, Cpu int) (err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_cpupool_cpuremove(Ctx.ctx, C.uint32_t(Poolid), C.int(Cpu)) if ret != 0 { err = Error(-ret) return } return } // int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid, // const libxl_bitmap *cpumap); func (Ctx *Context) CpupoolCpuremoveCpumap(Poolid uint32, Cpumap Bitmap) (err error) { err = Ctx.CheckOpen() if err != nil { return } cbm := Cpumap.toC() defer C.libxl_bitmap_dispose(&cbm) ret := C.libxl_cpupool_cpuremove_cpumap(Ctx.ctx, C.uint32_t(Poolid), &cbm) if ret != 0 { err = Error(-ret) return } return } // int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid); func (Ctx *Context) CpupoolRename(Name string, Poolid uint32) (err error) { err = Ctx.CheckOpen() if err != nil { return } name := C.CString(Name) defer C.free(unsafe.Pointer(name)) ret := C.libxl_cpupool_rename(Ctx.ctx, name, C.uint32_t(Poolid)) if ret != 0 { err = Error(-ret) return } return } // int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); func (Ctx *Context) CpupoolCpuaddNode(Poolid uint32, Node int) (Cpus int, err error) { err = Ctx.CheckOpen() if err != nil { return } ccpus := C.int(0) ret := C.libxl_cpupool_cpuadd_node(Ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus) if ret != 0 { err = Error(-ret) return } Cpus = int(ccpus) return } // int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); func (Ctx *Context) CpupoolCpuremoveNode(Poolid uint32, Node int) (Cpus int, err error) { err = Ctx.CheckOpen() if err != nil { return } ccpus := C.int(0) ret := C.libxl_cpupool_cpuremove_node(Ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus) if ret != 0 { err = Error(-ret) return } Cpus = int(ccpus) return } // int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid); func (Ctx *Context) CpupoolMovedomain(Poolid uint32, Id Domid) (err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_cpupool_movedomain(Ctx.ctx, C.uint32_t(Poolid), C.uint32_t(Id)) if ret != 0 { err = Error(-ret) return } return } // // Utility functions // func (Ctx *Context) CpupoolFindByName(name string) (info CpupoolInfo, found bool) { plist := Ctx.ListCpupool() for i := range plist { if plist[i].PoolName == name { found = true info = plist[i] return } } return } func (Ctx *Context) CpupoolMakeFree(Cpumap Bitmap) (err error) { plist := Ctx.ListCpupool() for i := range plist { var Intersection Bitmap Intersection = Cpumap.And(plist[i].Cpumap) if !Intersection.IsEmpty() { err = Ctx.CpupoolCpuremoveCpumap(plist[i].Poolid, Intersection) if err != nil { return } } } return } /* * Bitmap operations */ // Return a Go bitmap which is a copy of the referred C bitmap. func (cbm C.libxl_bitmap) toGo() (gbm Bitmap) { // Alloc a Go slice for the bytes size := int(cbm.size) gbm.bitmap = make([]C.uint8_t, size) // Make a slice pointing to the C array mapslice := (*[1 << 30]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size] // And copy the C array into the Go array copy(gbm.bitmap, mapslice) return } // Must be C.libxl_bitmap_dispose'd of afterwards func (gbm Bitmap) toC() (cbm C.libxl_bitmap) { C.libxl_bitmap_init(&cbm) size := len(gbm.bitmap) cbm._map = (*C.uint8_t)(C.malloc(C.size_t(size))) cbm.size = C.uint32_t(size) if cbm._map == nil { panic("C.calloc failed!") } // Make a slice pointing to the C array mapslice := (*[1 << 30]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size] // And copy the Go array into the C array copy(mapslice, gbm.bitmap) return } func (bm *Bitmap) Test(bit int) bool { ubit := uint(bit) if bit > bm.Max() || bm.bitmap == nil { return false } return (bm.bitmap[bit/8] & (1 << (ubit & 7))) != 0 } func (bm *Bitmap) Set(bit int) { ibit := bit / 8 if ibit+1 > len(bm.bitmap) { bm.bitmap = append(bm.bitmap, make([]C.uint8_t, ibit+1-len(bm.bitmap))...) } bm.bitmap[ibit] |= 1 << (uint(bit) & 7) } func (bm *Bitmap) SetRange(start int, end int) { for i := start; i <= end; i++ { bm.Set(i) } } func (bm *Bitmap) Clear(bit int) { ubit := uint(bit) if bit > bm.Max() || bm.bitmap == nil { return } bm.bitmap[bit/8] &= ^(1 << (ubit & 7)) } func (bm *Bitmap) ClearRange(start int, end int) { for i := start; i <= end; i++ { bm.Clear(i) } } func (bm *Bitmap) Max() int { return len(bm.bitmap)*8 - 1 } func (bm *Bitmap) IsEmpty() bool { for i := 0; i < len(bm.bitmap); i++ { if bm.bitmap[i] != 0 { return false } } return true } func (a Bitmap) And(b Bitmap) (c Bitmap) { var max, min int if len(a.bitmap) > len(b.bitmap) { max = len(a.bitmap) min = len(b.bitmap) } else { max = len(b.bitmap) min = len(a.bitmap) } c.bitmap = make([]C.uint8_t, max) for i := 0; i < min; i++ { c.bitmap[i] = a.bitmap[i] & b.bitmap[i] } return } func (bm Bitmap) String() (s string) { lastOnline := false crange := false printed := false var i int /// --x-xxxxx-x -> 2,4-8,10 /// --x-xxxxxxx -> 2,4-10 for i = 0; i <= bm.Max(); i++ { if bm.Test(i) { if !lastOnline { // Switching offline -> online, print this cpu if printed { s += "," } s += fmt.Sprintf("%d", i) printed = true } else if !crange { // last was online, but we're not in a range; print - crange = true s += "-" } else { // last was online, we're in a range, nothing else to do } lastOnline = true } else { if lastOnline { // Switching online->offline; do we need to end a range? if crange { s += fmt.Sprintf("%d", i-1) } } lastOnline = false crange = false } } if lastOnline { // Switching online->offline; do we need to end a range? if crange { s += fmt.Sprintf("%d", i-1) } } return } /* * Context */ var Ctx Context func (Ctx *Context) IsOpen() bool { return Ctx.ctx != nil } func (Ctx *Context) Open() (err error) { if Ctx.ctx != nil { return } Ctx.logger = C.xtl_createlogger_stdiostream(C.stderr, C.XTL_ERROR, 0) if Ctx.logger == nil { err = fmt.Errorf("Cannot open stdiostream") return } ret := C.libxl_ctx_alloc(&Ctx.ctx, C.LIBXL_VERSION, 0, unsafe.Pointer(Ctx.logger)) if ret != 0 { err = Error(-ret) } return } func (Ctx *Context) Close() (err error) { ret := C.libxl_ctx_free(Ctx.ctx) Ctx.ctx = nil if ret != 0 { err = Error(-ret) } C.xtl_logger_destroy(unsafe.Pointer(Ctx.logger)) return } func (Ctx *Context) CheckOpen() (err error) { if Ctx.ctx == nil { err = fmt.Errorf("Context not opened") } return } //int libxl_get_max_cpus(libxl_ctx *ctx); func (Ctx *Context) GetMaxCpus() (maxCpus int, err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_get_max_cpus(Ctx.ctx) if ret < 0 { err = Error(-ret) return } maxCpus = int(ret) return } //int libxl_get_online_cpus(libxl_ctx *ctx); func (Ctx *Context) GetOnlineCpus() (onCpus int, err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_get_online_cpus(Ctx.ctx) if ret < 0 { err = Error(-ret) return } onCpus = int(ret) return } //int libxl_get_max_nodes(libxl_ctx *ctx); func (Ctx *Context) GetMaxNodes() (maxNodes int, err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_get_max_nodes(Ctx.ctx) if ret < 0 { err = Error(-ret) return } maxNodes = int(ret) return } //int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb); func (Ctx *Context) GetFreeMemory() (memkb uint64, err error) { err = Ctx.CheckOpen() if err != nil { return } var cmem C.uint64_t ret := C.libxl_get_free_memory(Ctx.ctx, &cmem) if ret < 0 { err = Error(-ret) return } memkb = uint64(cmem) return } //int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo) func (Ctx *Context) GetPhysinfo() (physinfo *Physinfo, err error) { err = Ctx.CheckOpen() if err != nil { return } var cphys C.libxl_physinfo C.libxl_physinfo_init(&cphys) defer C.libxl_physinfo_dispose(&cphys) ret := C.libxl_get_physinfo(Ctx.ctx, &cphys) if ret < 0 { err = Error(ret) return } physinfo = cphys.toGo() return } //const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx); func (Ctx *Context) GetVersionInfo() (info *VersionInfo, err error) { err = Ctx.CheckOpen() if err != nil { return } var cinfo *C.libxl_version_info cinfo = C.libxl_get_version_info(Ctx.ctx) info = cinfo.toGo() return } func (Ctx *Context) DomainInfo(Id Domid) (di *Dominfo, err error) { err = Ctx.CheckOpen() if err != nil { return } var cdi C.libxl_dominfo C.libxl_dominfo_init(&cdi) defer C.libxl_dominfo_dispose(&cdi) ret := C.libxl_domain_info(Ctx.ctx, &cdi, C.uint32_t(Id)) if ret != 0 { err = Error(-ret) return } di = cdi.toGo() return } func (Ctx *Context) DomainUnpause(Id Domid) (err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_domain_unpause(Ctx.ctx, C.uint32_t(Id)) if ret != 0 { err = Error(-ret) } return } //int libxl_domain_pause(libxl_ctx *ctx, uint32_t domain); func (Ctx *Context) DomainPause(id Domid) (err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_domain_pause(Ctx.ctx, C.uint32_t(id)) if ret != 0 { err = Error(-ret) } return } //int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid); func (Ctx *Context) DomainShutdown(id Domid) (err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_domain_shutdown(Ctx.ctx, C.uint32_t(id)) if ret != 0 { err = Error(-ret) } return } //int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid); func (Ctx *Context) DomainReboot(id Domid) (err error) { err = Ctx.CheckOpen() if err != nil { return } ret := C.libxl_domain_reboot(Ctx.ctx, C.uint32_t(id)) if ret != 0 { err = Error(-ret) } return } //libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out); //void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain); func (Ctx *Context) ListDomain() (glist []Dominfo) { err := Ctx.CheckOpen() if err != nil { return } var nbDomain C.int clist := C.libxl_list_domain(Ctx.ctx, &nbDomain) defer C.libxl_dominfo_list_free(clist, nbDomain) if int(nbDomain) == 0 { return } gslice := (*[1 << 30]C.libxl_dominfo)(unsafe.Pointer(clist))[:nbDomain:nbDomain] for i := range gslice { info := gslice[i].toGo() glist = append(glist, *info) } return } type Vcpuinfo struct { Vcpuid uint32 Cpu uint32 Online bool Blocked bool Running bool VCpuTime time.Duration Cpumap Bitmap CpumapSoft Bitmap } func (cvci C.libxl_vcpuinfo) toGo() (gvci Vcpuinfo) { gvci.Vcpuid = uint32(cvci.vcpuid) gvci.Cpu = uint32(cvci.cpu) gvci.Online = bool(cvci.online) gvci.Blocked = bool(cvci.blocked) gvci.Running = bool(cvci.running) gvci.VCpuTime = time.Duration(cvci.vcpu_time) gvci.Cpumap = cvci.cpumap.toGo() gvci.CpumapSoft = cvci.cpumap_soft.toGo() return } //libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid, // int *nb_vcpu, int *nr_cpus_out); //void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus); func (Ctx *Context) ListVcpu(id Domid) (glist []Vcpuinfo) { err := Ctx.CheckOpen() if err != nil { return } var nbVcpu C.int var nrCpu C.int clist := C.libxl_list_vcpu(Ctx.ctx, C.uint32_t(id), &nbVcpu, &nrCpu) defer C.libxl_vcpuinfo_list_free(clist, nbVcpu) if int(nbVcpu) == 0 { return } gslice := (*[1 << 30]C.libxl_vcpuinfo)(unsafe.Pointer(clist))[:nbVcpu:nbVcpu] for i := range gslice { info := gslice[i].toGo() glist = append(glist, info) } return } type ConsoleType int const ( ConsoleTypeUnknown = ConsoleType(C.LIBXL_CONSOLE_TYPE_UNKNOWN) ConsoleTypeSerial = ConsoleType(C.LIBXL_CONSOLE_TYPE_SERIAL) ConsoleTypePV = ConsoleType(C.LIBXL_CONSOLE_TYPE_PV) ) func (ct ConsoleType) String() (str string) { cstr := C.libxl_console_type_to_string(C.libxl_console_type(ct)) str = C.GoString(cstr) return } //int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num, //libxl_console_type type, char **path); func (Ctx *Context) ConsoleGetTty(id Domid, consNum int, conType ConsoleType) (path string, err error) { err = Ctx.CheckOpen() if err != nil { return } var cpath *C.char ret := C.libxl_console_get_tty(Ctx.ctx, C.uint32_t(id), C.int(consNum), C.libxl_console_type(conType), &cpath) if ret != 0 { err = Error(-ret) return } defer C.free(cpath) path = C.GoString(cpath) return } //int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, // char **path); func (Ctx *Context) PrimaryConsoleGetTty(domid uint32) (path string, err error) { err = Ctx.CheckOpen() if err != nil { return } var cpath *C.char ret := C.libxl_primary_console_get_tty(Ctx.ctx, C.uint32_t(domid), &cpath) if ret != 0 { err = Error(-ret) return } defer C.free(cpath) path = C.GoString(cpath) return } xen-4.9.2/tools/flask/0000775000175000017500000000000013256712137012715 5ustar smbsmbxen-4.9.2/tools/flask/policy/0000775000175000017500000000000013256712137014214 5ustar smbsmbxen-4.9.2/tools/flask/policy/policy/0000775000175000017500000000000013256712137015513 5ustar smbsmbxen-4.9.2/tools/flask/policy/policy/support/0000775000175000017500000000000013256712137017227 5ustar smbsmbxen-4.9.2/tools/flask/policy/policy/support/mls_macros.spt0000664000175000017500000000242513256712137022121 0ustar smbsmb######################################## # # gen_cats(N) # # declares categores c0 to c(N-1) # define(`decl_cats',`dnl category c$1; ifelse(`$1',`$2',,`decl_cats(incr($1),$2)')dnl ') define(`gen_cats',`decl_cats(0,decr($1))') ######################################## # # gen_sens(N) # # declares sensitivites s0 to s(N-1) with dominance # in increasing numeric order with s0 lowest, s(N-1) highest # define(`decl_sens',`dnl sensitivity s$1; ifelse(`$1',`$2',,`decl_sens(incr($1),$2)')dnl ') define(`gen_dominance',`s$1 ifelse(`$1',`$2',,`gen_dominance(incr($1),$2)')') define(`gen_sens',` # Each sensitivity has a name and zero or more aliases. decl_sens(0,decr($1)) # Define the ordering of the sensitivity levels (least to greatest) dominance { gen_dominance(0,decr($1)) } ') ######################################## # # gen_levels(N,M) # # levels from s0 to (N-1) with categories c0 to (M-1) # define(`decl_levels',`dnl level s$1:c0.c$3; ifelse(`$1',`$2',,`decl_levels(incr($1),$2,$3)')dnl ') define(`gen_levels',`decl_levels(0,decr($1),decr($2))') ######################################## # # Basic level names for system low and high # define(`mls_systemlow',`s0') define(`mls_systemhigh',`s`'decr(mls_num_sens):c0.c`'decr(mls_num_cats)') define(`mls_allcats',`c0.c`'decr(mls_num_cats)') xen-4.9.2/tools/flask/policy/policy/support/misc_macros.spt0000664000175000017500000000267113256712137022264 0ustar smbsmb ######################################## # # Helper macros # # # shiftn(num,list...) # # shift the list num times # define(`shiftn',`ifelse($1,0,`shift($*)',`shiftn(decr($1),shift(shift($*)))')') # # ifndef(expr,true_block,false_block) # # m4 does not have this. # define(`ifndef',`ifdef(`$1',`$3',`$2')') # # __endline__ # # dummy macro to insert a newline. used for # errprint, so the close parentheses can be # indented correctly. # define(`__endline__',` ') ######################################## # # refpolwarn(message) # # print a warning message # define(`refpolicywarn',`errprint(__file__:__line__: Warning: `$1'__endline__)') ######################################## # # refpolerr(message) # # print an error message. does not # make anything fail. # define(`refpolicyerr',`errprint(__file__:__line__: Error: `$1'__endline__)') ######################################## # # gen_user(username, prefix, role_set, mls_defaultlevel, mls_range) # define(`gen_user',`define(`gen_all_users', gen_all_users `dnl user $1 roles { $3 }`'ifdef(`enable_mls', ` level $4 range $5')`'; ')') define(`gen_all_users',`') ######################################## # # gen_context(context,mls_sensitivity,[mcs_categories]) # define(`gen_context',`$1`'ifdef(`enable_mls',`:$2')`'') define(`dflt_or_overr',`ifdef(`$1',$1,$2)') ######################################## # # gen_bool(name,default_value) # define(`gen_bool',` bool $1 dflt_or_overr(`$1'_conf,$2); ') xen-4.9.2/tools/flask/policy/policy/security_classes0000664000175000017500000000044313256712137021023 0ustar smbsmb# Locally defined security classes # # These classes are not used by the hypervisor, but may be used by domains or # daemons that need to make access control decisions using the hypervisor's # security policy. # # Access vectors for these classes must be defined in the access_vectors file. xen-4.9.2/tools/flask/policy/policy/device_contexts0000664000175000017500000000232213256712137020623 0ustar smbsmb############################################################################### # # Label devices for delegation # # The PCI, IRQ, memory, and I/O port ranges are hardware-specific. # ############################################################################### # label e1000e nic #pirqcon 33 system_u:object_r:nic_dev_t #pirqcon 55 system_u:object_r:nic_dev_t #iomemcon 0xfebe0-0xfebff system_u:object_r:nic_dev_t #iomemcon 0xfebd9 system_u:object_r:nic_dev_t #ioportcon 0xecc0-0xecdf system_u:object_r:nic_dev_t #pcidevicecon 0xc800 system_u:object_r:nic_dev_t # label e100 nic #pirqcon 16 system_u:object_r:nic_dev_t #iomemcon 0xfe5df system_u:object_r:nic_dev_t #iomemcon 0xfe5e0-0xfe5ff system_u:object_r:nic_dev_t #iomemcon 0xc2000-0xc200f system_u:object_r:nic_dev_t #ioportcon 0xccc0-0xcd00 system_u:object_r:nic_dev_t # label usb 1d.0-2 1d.7 #pirqcon 23 system_u:object_r:nic_dev_t #pirqcon 17 system_u:object_r:nic_dev_t #pirqcon 18 system_u:object_r:nic_dev_t #ioportcon 0xff80-0xFF9F system_u:object_r:nic_dev_t #ioportcon 0xff60-0xff7f system_u:object_r:nic_dev_t #ioportcon 0xff40-0xff5f system_u:object_r:nic_dev_t #iomemcon 0xff980 system_u:object_r:nic_dev_t #ioportcon 0xff00-0xff1f system_u:object_r:nic_dev_t xen-4.9.2/tools/flask/policy/policy/access_vectors0000664000175000017500000000027713256712137020452 0ustar smbsmb# Locally defined access vectors # # Define access vectors for the security classes defined in security_classes. # Access vectors defined in this file should not be used by the hypervisor. # xen-4.9.2/tools/flask/policy/policy/global_tunables0000664000175000017500000000022113256712137020566 0ustar smbsmb# # This file is for the declaration of global policy tunables, booleans, # and other components not defined within a specific policy module. # xen-4.9.2/tools/flask/policy/policy/initial_sids0000664000175000017500000000160613256712137020114 0ustar smbsmb# Labels for initial SIDs. These initial SIDs are used by the hypervisor for # objects created before the policy is loaded or for objects that do not have a # label defined in some other manner. sid xen gen_context(system_u:system_r:xen_t,s0) sid dom0 gen_context(system_u:system_r:dom0_t,s0) sid domxen gen_context(system_u:system_r:domxen_t,s0) sid domio gen_context(system_u:system_r:domio_t,s0) sid unlabeled gen_context(system_u:system_r:unlabeled_t,s0) sid security gen_context(system_u:system_r:security_t,s0) sid irq gen_context(system_u:object_r:irq_t,s0) sid iomem gen_context(system_u:object_r:iomem_t,s0) sid ioport gen_context(system_u:object_r:ioport_t,s0) sid device gen_context(system_u:object_r:device_t,s0) # Initial SIDs used by the toolstack for domains without defined labels sid domU gen_context(system_u:system_r:domU_t,s0) sid domDM gen_context(system_u:system_r:dm_dom_t,s0) xen-4.9.2/tools/flask/policy/policy/mls0000664000175000017500000000436113256712137016235 0ustar smbsmbifdef(`enable_mls',` # # Define sensitivities # # Domination of sensitivities is in increasin # numerical order, with s0 being the lowest gen_sens(mls_num_sens) # # Define the categories # # Generate declarations gen_cats(mls_num_cats) # # Each MLS level specifies a sensitivity and zero or more categories which may # be associated with that sensitivity. # # Generate levels from all sensitivities # with all categories gen_levels(mls_num_sens,mls_num_cats) # # Define the MLS policy # # mlsconstrain class_set perm_set expression ; # # mlsvalidatetrans class_set expression ; # # expression : ( expression ) # | not expression # | expression and expression # | expression or expression # | u1 op u2 # | r1 role_mls_op r2 # | t1 op t2 # | l1 role_mls_op l2 # | l1 role_mls_op h2 # | h1 role_mls_op l2 # | h1 role_mls_op h2 # | l1 role_mls_op h1 # | l2 role_mls_op h2 # | u1 op names # | u2 op names # | r1 op names # | r2 op names # | t1 op names # | t2 op names # | u3 op names (NOTE: this is only available for mlsvalidatetrans) # | r3 op names (NOTE: this is only available for mlsvalidatetrans) # | t3 op names (NOTE: this is only available for mlsvalidatetrans) # # op : == | != # role_mls_op : == | != | eq | dom | domby | incomp # # names : name | { name_list } # name_list : name | name_list name # # # MLS policy for the domain class # # new domain labels must be dominated by the calling subject clearance # and sensitivity level changes require privilege mlsconstrain domain transition (( h1 dom h2 ) and (( l1 eq l2 ) or (t1 == mls_priv))); # all the domain "read" ops mlsconstrain domain { getaffinity getdomaininfo getvcpuinfo getvcpucontext getaddrsize getextvcpucontext } ((l1 dom l2) or (t1 == mls_priv)); # all the domain "write" ops mlsconstrain domain { setvcpucontext pause unpause resume create max_vcpus destroy setaffinity scheduler setdomainmaxmem setdomainhandle setdebugging hypercall settime set_target shutdown setaddrsize trigger setextvcpucontext } ((l1 eq l2) or (t1 == mls_priv)); # This is incomplete - similar constraints must be written for all classes # and permissions for which MLS enforcement is desired. ') dnl end enable_mls xen-4.9.2/tools/flask/policy/policy/users0000664000175000017500000000002013256712137016567 0ustar smbsmbgen_all_users() xen-4.9.2/tools/flask/policy/Makefile.common0000664000175000017500000001001613256712137017141 0ustar smbsmb# This file is referenced by both hypervisor build and tools build # so there shouldn't be any tools specific things here. XEN_ROOT=$(CURDIR)/../../.. ifeq ($(FLASK_BUILD_DIR),) $(error FLASK_BUILD_DIR not set) endif ######################################## # # Configurable portions of the Makefile # ######################################## CONFIG_MLS ?= n # Number of available MLS sensitivities and categories. # The sensitivities will be s0 to s(MLS_SENS-1). Dominance will be in # increasing numerical order with s0 being lowest. MLS_SENS ?= 16 # The categories will be c0 to c(MLS_CATS-1). MLS_CATS ?= 256 # executable paths CHECKPOLICY ?= checkpolicy M4 ?= m4 # Output security policy version. Leave unset to autodetect. OUTPUT_POLICY ?= $(BEST_POLICY_VER) ######################################## # # End of configuration options # ######################################## POLICY_FILENAME = $(FLASK_BUILD_DIR)/xenpolicy-$(shell $(MAKE) -C $(XEN_ROOT)/xen xenversion --no-print-directory) POLICY_LOADPATH = /boot # List of policy versions supported by the hypervisor POLICY_VER_LIST_HV = 24 30 # policy source layout POLDIR := policy MODDIR := modules # Classes and access vectors defined in the hypervisor. Changes to these require # a recompile of both the hypervisor and security policy. FLASKDIR := ../../../xen/xsm/flask/policy SECCLASS := $(FLASKDIR)/security_classes ISID_DECLS := $(FLASKDIR)/initial_sids AVS := $(FLASKDIR)/access_vectors # Additional classes and access vectors defined by local policy SECCLASS += $(POLDIR)/security_classes AVS += $(POLDIR)/access_vectors # Other policy components M4SUPPORT := $(wildcard $(POLDIR)/support/*.spt) MLSSUPPORT := $(POLDIR)/mls USERS := $(POLDIR)/users ISID_DEFS := $(POLDIR)/initial_sids DEV_OCONS := $(POLDIR)/device_contexts # config file paths GLOBALTUN := $(POLDIR)/global_tunables MOD_CONF := $(MODDIR)/modules.conf # checkpolicy can use the #line directives provided by -s for error reporting: M4PARAM := -D self_contained_policy -s # The output of checkpolicy -V is "30 (compatibility range 30-15)", and the # first word of the output is the maximum policy version supported. CHECKPOLICY_VER_MAX := $(firstword $(shell $(CHECKPOLICY) -V)) # Find the highest version supported by both the hypervisor and checkpolicy BEST_POLICY_VER := $(shell best=24; for ver in $(POLICY_VER_LIST_HV); do if test $$ver -le $(CHECKPOLICY_VER_MAX); then best=$$ver; fi; done; echo $$best) CHECKPOLICY_PARAM := -t Xen -c $(OUTPUT_POLICY) # enable MLS if requested. ifneq ($(CONFIG_MLS),n) M4PARAM += -D enable_mls CHECKPOLICY_PARAM += -M endif # Always define these because they are referenced even in non-MLS policy M4PARAM += -D mls_num_sens=$(MLS_SENS) -D mls_num_cats=$(MLS_CATS) # modules.conf setting for policy configuration MODENABLED := on # extract settings from modules.conf ENABLED_LIST := $(shell awk '/^[ \t]*[a-z]/{ if ($$3 == "$(MODENABLED)") print $$1 }' $(MOD_CONF) 2> /dev/null) # Modules must provide a .te file, although it could be empty ALL_MODULES := $(foreach mod,$(ENABLED_LIST),$(MODDIR)/$(mod).te) # Modules may also provide interfaces and constraint definitions ALL_INTERFACES := $(wildcard $(ALL_MODULES:.te=.if)) ALL_CONSTRAINTS := $(wildcard $(ALL_MODULES:.te=.cons)) # The order of these files is important POLICY_SECTIONS := $(SECCLASS) $(ISID_DECLS) $(AVS) POLICY_SECTIONS += $(M4SUPPORT) $(MLSSUPPORT) POLICY_SECTIONS += $(ALL_INTERFACES) POLICY_SECTIONS += $(GLOBALTUN) POLICY_SECTIONS += $(ALL_MODULES) POLICY_SECTIONS += $(USERS) POLICY_SECTIONS += $(ALL_CONSTRAINTS) POLICY_SECTIONS += $(ISID_DEFS) $(DEV_OCONS) all: $(POLICY_FILENAME) install: $(POLICY_FILENAME) $(INSTALL_DIR) $(DESTDIR)/$(POLICY_LOADPATH) $(INSTALL_DATA) $^ $(DESTDIR)/$(POLICY_LOADPATH) $(POLICY_FILENAME): $(FLASK_BUILD_DIR)/policy.conf $(CHECKPOLICY) $(CHECKPOLICY_PARAM) $^ -o $@ $(FLASK_BUILD_DIR)/policy.conf: $(POLICY_SECTIONS) $(MOD_CONF) $(M4) $(M4PARAM) $(POLICY_SECTIONS) > $@ clean: $(RM) $(FLASK_BUILD_DIR)/policy.conf $(POLICY_FILENAME) distclean: clean .PHONY: all install clean distclean xen-4.9.2/tools/flask/policy/modules/0000775000175000017500000000000013256712137015664 5ustar smbsmbxen-4.9.2/tools/flask/policy/modules/all_system_role.te0000664000175000017500000000023713256712137021415 0ustar smbsmb# Allow all domains to use system_r so that systems that are not using the # user/role separation feature will work properly. role system_r types domain_type; xen-4.9.2/tools/flask/policy/modules/xen.te0000664000175000017500000000561313256712137017015 0ustar smbsmb################################################################################ # # Attributes for types # # An attribute may be used in a rule as shorthand for all types with that # attribute. # ################################################################################ attribute xen_type; attribute domain_type; attribute domain_self_type; attribute domain_target_type; attribute resource_type; attribute event_type; attribute mls_priv; ################################################################################ # # Types for the initial SIDs # # These types are used internally for objects created during Xen startup or for # devices that have not yet been labeled # ################################################################################ # The hypervisor itself type xen_t, xen_type, mls_priv; # Domain 0 declare_singleton_domain(dom0_t, mls_priv); # I/O memory (DOMID_IO pseudo-domain) type domio_t, xen_type; # Xen heap (DOMID_XEN pseudo-domain) type domxen_t, xen_type; # Unlabeled objects type unlabeled_t, xen_type; # The XSM/FLASK security server type security_t, xen_type; # Unlabeled device resources # Note: don't allow access to these types directly; see below for how to label # devices and use that label for allow rules type irq_t, resource_type; type ioport_t, resource_type; type iomem_t, resource_type; type device_t, resource_type; # Domain destruction can result in some access checks for actions performed by # the hypervisor. These should always be allowed. allow xen_t resource_type : resource { remove_irq remove_ioport remove_iomem }; ################################################################################ # # Policy constraints # # Neverallow rules will cause the policy build to fail if an allow rule exists # that violates the expression. This is used to ensure proper labeling of # objects. # ################################################################################ # Domains must be declared using domain_type neverallow * ~domain_type:domain { create transition }; # Resources must be declared using resource_type neverallow * ~resource_type:resource use; # Events must use event_type (see create_channel for a template) neverallow ~event_type *:event bind; neverallow * ~event_type:event { create send status }; ################################################################################ # # Users and Roles # ################################################################################ # The object role (object_r) is used for devices, resources, and event channels; # it does not need to be defined here and should not be used for domains. # The system user and role are used for utility domains and pseudo-domains. In # systems where users and roles are not being used for separation, all domains # can use the system user and role. gen_user(system_u,, system_r, s0, s0 - mls_systemhigh) role system_r; role system_r types { xen_type dom0_t }; xen-4.9.2/tools/flask/policy/modules/prot_domU.te0000664000175000017500000000076713256712137020200 0ustar smbsmb# This is an alternative to nomigrate_t: a policy boolean controls the ability # to create or migrate a domain of type prot_domU_t. If disabled, dom0 cannot # map memory belonging to those domains. gen_bool(prot_doms_locked, false) declare_domain(prot_domU_t) if (!prot_doms_locked) { create_domain(dom0_t, prot_domU_t) migrate_domain_out(dom0_t, prot_domU_t) } domain_comms(dom0_t, prot_domU_t) domain_comms(domU_t, prot_domU_t) domain_comms(prot_domU_t, prot_domU_t) domain_self_comms(prot_domU_t) xen-4.9.2/tools/flask/policy/modules/nomigrate.te0000664000175000017500000000051513256712137020204 0ustar smbsmb# Domains of type nomigrate_t must be built via the nomigrate_t_building label; # once built, dom0 cannot read their memory. declare_domain(nomigrate_t) declare_build_label(nomigrate_t) create_domain_build_label(dom0_t, nomigrate_t) manage_domain(dom0_t, nomigrate_t) domain_comms(dom0_t, nomigrate_t) domain_self_comms(nomigrate_t) xen-4.9.2/tools/flask/policy/modules/vm_role.cons0000664000175000017500000000152113256712137020212 0ustar smbsmb# # Constraints are defined by: # # constrain class_set perm_set expression ; # # expression : ( expression ) # | not expression # | expression and expression # | expression or expression # | u1 op u2 # | r1 role_op r2 # | t1 op t2 # | u1 op names # | u2 op names # | r1 op names # | r2 op names # | t1 op names # | t2 op names # # op : == | != # role_op : == | != | eq | dom | domby | incomp # # names : name | { name_list } # name_list : name | name_list name # # Prevent event channels and grants between different users. This could be # further limited to only restricting those domains using the vm_r role. constrain event bind ( u1 == system_u or u2 == system_u or u1 == u2 ); constrain grant { map_read map_write copy } ( u1 == system_u or u2 == system_u or u1 == u2 ); xen-4.9.2/tools/flask/policy/modules/xenstore.te0000664000175000017500000000201413256712137020062 0ustar smbsmb################################################################################ # # Xenstore stubdomain # ################################################################################ declare_singleton_domain(xenstore_t) create_domain(dom0_t, xenstore_t) manage_domain(dom0_t, xenstore_t) # Xenstore requires the global VIRQ for domain destroy operations allow dom0_t xenstore_t:domain set_virq_handler; # Current xenstore stubdom uses the hypervisor console, not "xl console" allow xenstore_t xen_t:xen writeconsole; # Xenstore queries domaininfo on all domains allow xenstore_t domain_type:domain getdomaininfo; # As a shortcut, the following 3 rules are used instead of adding a domain_comms # rule between xenstore_t and every domain type that talks to xenstore create_channel(xenstore_t, domain_type, xenstore_t_channel) allow event_type xenstore_t: event bind; allow xenstore_t domain_type:grant { map_read map_write unmap }; # Xenstore is a utility domain, so it should use the system role role system_r types xenstore_t; xen-4.9.2/tools/flask/policy/modules/isolated_domU.te0000664000175000017500000000034413256712137021007 0ustar smbsmbdeclare_domain(isolated_domU_t) create_domain(dom0_t, isolated_domU_t) manage_domain(dom0_t, isolated_domU_t) domain_comms(dom0_t, isolated_domU_t) migrate_domain_out(dom0_t, isolated_domU_t) domain_self_comms(isolated_domU_t) xen-4.9.2/tools/flask/policy/modules/guest_features.te0000664000175000017500000000231113256712137021240 0ustar smbsmb# Allow all domains to use (unprivileged parts of) the tmem hypercall allow domain_type xen_t:xen tmem_op; # Allow all domains to use PMU (but not to change its settings --- that's what # pmu_ctrl is for) allow domain_type xen_t:xen2 pmu_use; # Allow guest console output to the serial console. This is used by PV Linux # and stub domains for early boot output, so don't audit even when we deny it. # Without XSM, this is enabled only if the Xen was compiled in debug mode. gen_bool(guest_writeconsole, true) if (guest_writeconsole) { allow domain_type xen_t : xen writeconsole; } else { dontaudit domain_type xen_t : xen writeconsole; } # For normal guests, allow all queries except XENVER_commandline. allow domain_type xen_t:version { xen_extraversion xen_compile_info xen_capabilities xen_changeset xen_pagesize xen_guest_handle }; # Version queries don't need auditing when denied. They can be # encountered in normal operation by xl or by reading sysfs files in # Linux, so without this they will show up in the logs. Since these # operations return valid responses (like "denied"), hiding the denials # should not break anything. dontaudit domain_type xen_t:version { xen_commandline xen_build_id }; xen-4.9.2/tools/flask/policy/modules/nic_dev.te0000664000175000017500000000076613256712137017636 0ustar smbsmb############################################################################### # # Device delegation # # This requires that the device be labeled with a type defined here. You can # use flask-label-pci to dynamically label devices on each boot or define the # labels statically in tools/flask/policy/policy/device_contexts # ############################################################################### type nic_dev_t, resource_type; admin_device(dom0_t, nic_dev_t) use_device(domU_t, nic_dev_t) xen-4.9.2/tools/flask/policy/modules/dom0.te0000664000175000017500000000550613256712137017063 0ustar smbsmb################################################################################ # # Allow dom0 access to all sysctls, devices, and the security server. # # While this could be written more briefly using wildcards, the permissions are # listed out to make removing specific permissions simpler. # ################################################################################ allow dom0_t xen_t:xen { settime tbufcontrol readconsole clearconsole perfcontrol mtrr_add mtrr_del mtrr_read microcode physinfo quirk writeconsole readapic writeapic privprofile nonprivprofile kexec firmware sleep frequency getidle debug getcpuinfo heap pm_op mca_op lockprof cpupool_op tmem_op tmem_control getscheduler setscheduler }; allow dom0_t xen_t:xen2 { resource_op psr_cmt_op psr_cat_op pmu_ctrl get_symbol get_cpu_levelling_caps get_cpu_featureset livepatch_op gcov_op }; # Allow dom0 to use all XENVER_ subops that have checks. # Note that dom0 is part of domain_type so this has duplicates. allow dom0_t xen_t:version { xen_extraversion xen_compile_info xen_capabilities xen_changeset xen_pagesize xen_guest_handle xen_commandline xen_build_id }; allow dom0_t xen_t:mmu memorymap; # Allow dom0 to use these domctls on itself. For domctls acting on other # domains, see the definitions of create_domain and manage_domain. allow dom0_t dom0_t:domain { setvcpucontext max_vcpus setaffinity getaffinity getscheduler getdomaininfo getvcpuinfo getvcpucontext setdomainmaxmem setdomainhandle setdebugging hypercall settime setaddrsize getaddrsize trigger getpodtarget setpodtarget set_misc_info set_virq_handler }; allow dom0_t dom0_t:domain2 { set_cpuid gettsc settsc setscheduler set_max_evtchn set_vnumainfo get_vnumainfo psr_cmt_op psr_cat_op }; allow dom0_t dom0_t:resource { add remove }; # These permissions allow using the FLASK security server to compute access # checks locally, which could be used by a domain or service (such as xenstore) # that does not have its own security server to make access decisions based on # Xen's security policy. allow dom0_t security_t:security { compute_av compute_create compute_member compute_relabel }; # Allow string/SID conversions (for "xl list -Z" and similar) allow dom0_t security_t:security check_context; # Allow flask-label-pci to add and change labels allow dom0_t security_t:security { add_ocontext del_ocontext }; # Allow performance parameters of the security server to be tweaked allow dom0_t security_t:security setsecparam; # Allow changing the security policy allow dom0_t security_t:security { load_policy setenforce setbool }; # Audit policy change events even when they are allowed auditallow dom0_t security_t:security { load_policy setenforce setbool }; admin_device(dom0_t, device_t) admin_device(dom0_t, irq_t) admin_device(dom0_t, ioport_t) admin_device(dom0_t, iomem_t) domain_comms(dom0_t, dom0_t) xen-4.9.2/tools/flask/policy/modules/vm_role.te0000664000175000017500000000101513256712137017656 0ustar smbsmb# The vm role is used as part of user separation. Allow all domain types to use # this role except dom0. role vm_r; role vm_r types { domain_type -dom0_t }; # Define some users that must use this role (full type "user_1:vm_r:domU_t"). gen_user(user_1,, vm_r, s0, s0) gen_user(user_2,, vm_r, s0, s0) gen_user(user_3,, vm_r, s0, s0) # Alternate: define and use a macro to generate 1000 users define(`def_vm_users',`gen_user(user_$1,, vm_r, s0, s0) ifelse(`$1',`$2',,`def_vm_users(incr($1),$2)')dnl ') # def_vm_users(0,999) xen-4.9.2/tools/flask/policy/modules/xen.if0000664000175000017500000001505513256712137017004 0ustar smbsmb# Macro definitions for FLASK policy ################################################################################ # # Domain creation and setup # ################################################################################ define(`declare_domain_common', ` allow $1 $2:grant { query setup }; allow $1 $2:mmu { adjust physmap map_read map_write stat pinpage updatemp mmuext_op }; allow $1 $2:hvm { getparam setparam altp2mhvm_op }; allow $1 $2:domain2 get_vnumainfo; ') # declare_domain(type, attrs...) # Declare a domain type, along with associated _self and _channel types # Allow the domain to perform basic operations on itself define(`declare_domain', ` type $1, domain_type`'ifelse(`$#', `1', `', `,shift($@)'); type $1_self, domain_type, domain_self_type; type_transition $1 $1:domain $1_self; type $1_channel, event_type; type_transition $1 domain_type:event $1_channel; declare_domain_common($1, $1_self) ') # declare_singleton_domain(type, attrs...) # Declare a domain type and associated _channel types. # Note: Because the domain can perform basic operations on itself and any # other domain of the same type, this constructor should be used for types # containing at most one domain. This is not enforced by policy. define(`declare_singleton_domain', ` type $1, domain_type`'ifelse(`$#', `1', `', `,shift($@)'); define(`$1_self', `$1') type $1_channel, event_type; type_transition $1 domain_type:event $1_channel; declare_domain_common($1, $1) ') # declare_build_label(type) # Declare a paired _building type for the given domain type define(`declare_build_label', ` type $1_building, domain_type; type_transition $1_building domain_type:event $1_channel; allow $1_building $1 : domain transition; ') define(`create_domain_common', ` allow $1 $2:domain { create max_vcpus setdomainmaxmem setaddrsize getdomaininfo hypercall setvcpucontext getscheduler getvcpuinfo getaddrsize getaffinity setaffinity settime setdomainhandle getvcpucontext set_misc_info }; allow $1 $2:domain2 { set_cpuid settsc setscheduler setclaim set_max_evtchn set_vnumainfo get_vnumainfo cacheflush psr_cmt_op psr_cat_op soft_reset }; allow $1 $2:security check_context; allow $1 $2:shadow enable; allow $1 $2:mmu { map_read map_write adjust memorymap physmap pinpage mmuext_op updatemp }; allow $1 $2:grant setup; allow $1 $2:hvm { cacheattr getparam hvmctl sethvmc setparam nested altp2mhvm altp2mhvm_op dm }; ') # create_domain(priv, target) # Allow a domain to be created directly define(`create_domain', ` create_domain_common($1, $2) allow $1 $2_channel:event create; ') # create_domain_build_label(priv, target) # Allow a domain to be created via its domain build label define(`create_domain_build_label', ` create_domain_common($1, $2_building) allow $1 $2_channel:event create; allow $1 $2_building:domain2 relabelfrom; allow $1 $2:domain2 relabelto; allow $2_building $2:domain transition; ') # manage_domain(priv, target) # Allow managing a running domain define(`manage_domain', ` allow $1 $2:domain { getdomaininfo getvcpuinfo getaffinity getaddrsize pause unpause trigger shutdown destroy setaffinity setdomainmaxmem getscheduler resume setpodtarget getpodtarget }; allow $1 $2:domain2 set_vnumainfo; ') # migrate_domain_out(priv, target) # Allow creation of a snapshot or migration image from a domain # (inbound migration is the same as domain creation) define(`migrate_domain_out', ` allow $1 domxen_t:mmu map_read; allow $1 $2:hvm { gethvmc getparam }; allow $1 $2:mmu { stat pageinfo map_read }; allow $1 $2:domain { getaddrsize getvcpucontext pause destroy }; allow $1 $2:domain2 gettsc; allow $1 $2:shadow { enable disable logdirty }; ') ################################################################################ # # Inter-domain communication # ################################################################################ # create_channel(source, dest, chan-label) # This allows an event channel to be created from domains with labels # to and will label it define(`create_channel', ` allow $1 $3:event { create send status }; allow $3 $2:event { bind }; ') # domain_event_comms(dom1, dom2) # Allow two domain types to communicate using event channels define(`domain_event_comms', ` create_channel($1, $2, $1_channel) create_channel($2, $1, $2_channel) ') # domain_comms(dom1, dom2) # Allow two domain types to communicate using grants and event channels define(`domain_comms', ` domain_event_comms($1, $2) allow $1 $2:grant { map_read map_write copy unmap }; allow $2 $1:grant { map_read map_write copy unmap }; ') # domain_self_comms(domain) # Allow a non-singleton domain type to communicate with itself using grants # and event channels define(`domain_self_comms', ` create_channel($1, $1_self, $1_channel) allow $1 $1_self:grant { map_read map_write copy unmap }; ') # device_model(dm_dom, hvm_dom) # Define how a device model domain interacts with its target define(`device_model', ` type $2_target, domain_type, domain_target_type; type_transition $2 $1:domain $2_target; allow $1 $2:domain set_target; type_transition $2_target domain_type:event $2_channel; create_channel($1, $2_target, $1_channel) create_channel($2, $1, $2_channel) allow $1 $2_channel:event create; allow $1 $2_target:domain { getdomaininfo shutdown }; allow $1 $2_target:mmu { map_read map_write adjust physmap target_hack }; allow $1 $2_target:hvm { getparam setparam hvmctl cacheattr dm }; ') # make_device_model(priv, dm_dom, hvm_dom) # Allow creation of a device model and HVM domain pair define(`make_device_model', ` device_model($2, $3) allow $1 $2:domain2 make_priv_for; allow $1 $3:domain2 set_as_target; ') ################################################################################ # # Device types and delegation (PCI passthrough) # ################################################################################ # use_device(domain, device) # Allow a device to be used by a domain define(`use_device', ` allow $1 $1_self:mmu exchange; allow $1 $2:resource use; allow $1 domio_t:mmu { map_read map_write }; ') # admin_device(domain, device) # Allow a device to be used and delegated by a domain define(`admin_device', ` allow $1 $2:resource { setup stat_device add_device add_irq add_iomem add_ioport remove_device remove_irq remove_iomem remove_ioport plug unplug }; allow $1 $2:hvm bind_irq; use_device($1, $2) ') # delegate_devices(priv-domain, target-domain) # Allow devices to be delegated define(`delegate_devices', ` allow $1 $2:resource { add remove }; ') xen-4.9.2/tools/flask/policy/modules/domU.te0000664000175000017500000000165613256712137017132 0ustar smbsmb############################################################################### # # Domain creation # ############################################################################### declare_domain(domU_t) domain_self_comms(domU_t) create_domain(dom0_t, domU_t) manage_domain(dom0_t, domU_t) domain_comms(dom0_t, domU_t) domain_comms(domU_t, domU_t) migrate_domain_out(dom0_t, domU_t) domain_self_comms(domU_t) # Device model for domU_t. You can define distinct types for device models for # domains of other types, or add more make_device_model lines for this type. declare_domain(dm_dom_t) create_domain(dom0_t, dm_dom_t) manage_domain(dom0_t, dm_dom_t) domain_comms(dom0_t, dm_dom_t) make_device_model(dom0_t, dm_dom_t, domU_t) # This is required for PCI (or other device) passthrough delegate_devices(dom0_t, domU_t) # Both of these domain types can be created using the default (system) role role system_r types { domU_t dm_dom_t }; xen-4.9.2/tools/flask/policy/modules/modules.conf0000664000175000017500000000323713256712137020210 0ustar smbsmb# # This file contains a listing of available modules. # # To prevent a module from being used in policy creation, set the module name # to "off"; otherwise, set the module name on "on". # # The order the modules appear in this file is the order they will be parsed; # this can be important if you plan to use types defined in one file in another. # # Basic types and classes for the Xen hypervisor. This module is required. xen = on # Permissions for domain 0. Most of these are required to boot. dom0 = on # Allow all domains the ability to use access-controlled features and hypercalls # that are not restricted when XSM is disabled. guest_features = on # The default domain type (domU_t) and its device model (dm_dom_t). The domain # is created and managed by dom0_t, and has no special restrictions. # # This is required if you want to be able to create domains without specifying # their XSM label in the configuration. domU = on # Example types with restrictions isolated_domU = on prot_domU = on nomigrate = on # Example device policy. Also see policy/device_contexts. nic_dev = on # Xenstore stub domain (see init-xenstore-domain). xenstore = on # This allows any domain type to be created using the system_r role. When it is # disabled, domains not using the default types (dom0_t, domU_t, dm_dom_t) must # use another role (such as vm_r from the vm_role module below). all_system_role = on # Example users, roles, and constraints for user-based separation. # # The three users defined here can set up grant/event channel communication # (vchan, device frontend/backend) between their own VMs, but cannot set up a # channel to a VM under a different user. vm_role = on xen-4.9.2/tools/flask/policy/Makefile0000664000175000017500000000017513256712137015657 0ustar smbsmbXEN_ROOT=$(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk FLASK_BUILD_DIR=$(CURDIR) include $(CURDIR)/Makefile.common xen-4.9.2/tools/flask/Makefile0000664000175000017500000000030413256712137014352 0ustar smbsmbXEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk SUBDIRS-y := utils SUBDIRS-$(FLASK_POLICY) += policy .PHONY: all clean install distclean all clean install distclean: %: subdirs-% xen-4.9.2/tools/flask/utils/0000775000175000017500000000000013256712137014055 5ustar smbsmbxen-4.9.2/tools/flask/utils/loadpolicy.c0000664000175000017500000000557213256712137016371 0ustar smbsmb/* * * Authors: Michael LeMay, * George Coker, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #define USE_MMAP static void usage (int argCnt, const char *args[]) { fprintf(stderr, "Usage: %s \n", args[0]); exit(1); } int main (int argCnt, const char *args[]) { const char *polFName; int polFd = 0; void *polMem = NULL; void *polMemCp = NULL; struct stat info; int ret; xc_interface *xch = 0; if (argCnt != 2) usage(argCnt, args); polFName = args[1]; polFd = open(polFName, O_RDONLY); if ( polFd < 0 ) { fprintf(stderr, "Error occurred opening policy file '%s': %s\n", polFName, strerror(errno)); ret = -1; goto cleanup; } ret = stat(polFName, &info); if ( ret < 0 ) { fprintf(stderr, "Error occurred retrieving information about" "policy file '%s': %s\n", polFName, strerror(errno)); goto cleanup; } polMemCp = malloc(info.st_size); #ifdef USE_MMAP polMem = mmap(NULL, info.st_size, PROT_READ, MAP_SHARED, polFd, 0); if ( polMem == MAP_FAILED ) { fprintf(stderr, "Error occurred mapping policy file in memory: %s\n", strerror(errno)); ret = -1; goto cleanup; } xch = xc_interface_open(0,0,0); if ( !xch ) { fprintf(stderr, "Unable to create interface to xenctrl: %s\n", strerror(errno)); ret = -1; goto cleanup; } memcpy(polMemCp, polMem, info.st_size); #else ret = read(polFd, polMemCp, info.st_size); if ( ret < 0 ) { fprintf(stderr, "Unable to read new Flask policy file: %s\n", strerror(errno)); goto cleanup; } else { printf("Read %d bytes from policy file '%s'.\n", ret, polFName); } #endif ret = xc_flask_load(xch, polMemCp, info.st_size); if ( ret < 0 ) { errno = -ret; fprintf(stderr, "Unable to load new Flask policy: %s\n", strerror(errno)); ret = -1; goto cleanup; } else { printf("Successfully loaded policy.\n"); } done: free(polMemCp); if ( polMem ) { ret = munmap(polMem, info.st_size); if ( ret < 0 ) fprintf(stderr, "Unable to unmap policy memory: %s\n", strerror(errno)); } if ( polFd >= 0 ) close(polFd); if ( xch ) xc_interface_close(xch); return ret; cleanup: goto done; } xen-4.9.2/tools/flask/utils/getenforce.c0000664000175000017500000000244513256712137016347 0ustar smbsmb/* * * Author: Machon Gregory, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include static void usage (int argCnt, const char *args[]) { fprintf(stderr, "Usage: %s\n", args[0]); exit(1); } int main (int argCnt, const char *args[]) { int ret; xc_interface *xch = 0; if (argCnt != 1) usage(argCnt, args); xch = xc_interface_open(0,0,0); if ( !xch ) { fprintf(stderr, "Unable to create interface to xenctrl: %s\n", strerror(errno)); ret = -1; goto done; } ret = xc_flask_getenforce(xch); if ( ret < 0 ) { errno = -ret; fprintf(stderr, "Unable to get enforcing mode: %s\n", strerror(errno)); ret = -1; goto done; } else { if(ret) printf("Enforcing\n"); else printf("Permissive\n"); } done: if ( xch ) xc_interface_close(xch); return ret; } xen-4.9.2/tools/flask/utils/setenforce.c0000664000175000017500000000322413256712137016357 0ustar smbsmb/* * * Authors: Machon Gregory, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include static void usage (int argCnt, const char *args[]) { fprintf(stderr, "Usage: %s [ (Enforcing|1) | (Permissive|0) ]\n", args[0]); exit(1); } int main (int argCnt, const char *args[]) { int ret = 0; xc_interface *xch = 0; long mode = 0; char *end; if (argCnt != 2) usage(argCnt, args); xch = xc_interface_open(0,0,0); if ( !xch ) { fprintf(stderr, "Unable to create interface to xenctrl: %s\n", strerror(errno)); ret = -1; goto done; } if( strlen(args[1]) == 1 && (args[1][0] == '0' || args[1][0] == '1')){ mode = strtol(args[1], &end, 10); ret = xc_flask_setenforce(xch, mode); } else { if( strcasecmp(args[1], "enforcing") == 0 ){ ret = xc_flask_setenforce(xch, 1); } else if( strcasecmp(args[1], "permissive") == 0 ){ ret = xc_flask_setenforce(xch, 0); } else { usage(argCnt, args); } } if ( ret < 0 ) { errno = -ret; fprintf(stderr, "Unable to get enforcing mode: %s\n", strerror(errno)); ret = -1; goto done; } done: if ( xch ) xc_interface_close(xch); return ret; } xen-4.9.2/tools/flask/utils/get-bool.c0000664000175000017500000000337113256712137015735 0ustar smbsmb/* * Author: Daniel De Graaf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include static void usage(char **argv) { fprintf(stderr, "Usage: %s {name|-a}\n", argv[0]); exit(1); } static int all_bools(xc_interface *xch) { int err = 0, i = 0, curr, pend; char name[256]; while (1) { err = xc_flask_getbool_byid(xch, i, name, sizeof name, &curr, &pend); if (err < 0) { if (errno == ENOENT) return 0; fprintf(stderr, "xc_flask_getbool: Unable to get boolean #%d: %s (%d)", i, strerror(errno), err); return 2; } if (curr == pend) printf("%s: %d\n", name, curr); else printf("%s: %d (pending %d)\n", name, curr, pend); i++; } } int main(int argc, char **argv) { int err = 0; xc_interface *xch; int curr, pend; if (argc != 2) usage(argv); xch = xc_interface_open(0,0,0); if ( !xch ) { fprintf(stderr, "Unable to create interface to xenctrl: %s\n", strerror(errno)); err = 1; goto done; } if (!strcmp(argv[1], "-a")) { err = all_bools(xch); goto done; } err = xc_flask_getbool_byname(xch, argv[1], &curr, &pend); if (err) { fprintf(stderr, "xc_flask_getbool: Unable to get boolean %s: %s (%d)", argv[1], strerror(errno), err); err = 2; goto done; } if (curr == pend) printf("%s: %d\n", argv[1], curr); else printf("%s: %d (pending %d)\n", argv[1], curr, pend); done: if ( xch ) xc_interface_close(xch); return err; } xen-4.9.2/tools/flask/utils/Makefile0000664000175000017500000000250013256712137015512 0ustar smbsmbXEN_ROOT=$(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk CFLAGS += -Werror CFLAGS += $(CFLAGS_libxenctrl) TESTDIR = testsuite/tmp TESTFLAGS= -DTESTING TESTENV = XENSTORED_ROOTDIR=$(TESTDIR) XENSTORED_RUNDIR=$(TESTDIR) CLIENTS := flask-loadpolicy flask-setenforce flask-getenforce flask-label-pci flask-get-bool flask-set-bool CLIENTS_SRCS := $(patsubst flask-%,%.c,$(CLIENTS)) CLIENTS_OBJS := $(patsubst flask-%,%.o,$(CLIENTS)) .PHONY: all all: $(CLIENTS) flask-loadpolicy: loadpolicy.o $(CC) $(LDFLAGS) $< $(LDLIBS) $(LDLIBS_libxenctrl) -o $@ flask-setenforce: setenforce.o $(CC) $(LDFLAGS) $< $(LDLIBS) $(LDLIBS_libxenctrl) -o $@ flask-getenforce: getenforce.o $(CC) $(LDFLAGS) $< $(LDLIBS) $(LDLIBS_libxenctrl) -o $@ flask-label-pci: label-pci.o $(CC) $(LDFLAGS) $< $(LDLIBS) $(LDLIBS_libxenctrl) -o $@ flask-get-bool: get-bool.o $(CC) $(LDFLAGS) $< $(LDLIBS) $(LDLIBS_libxenctrl) -o $@ flask-set-bool: set-bool.o $(CC) $(LDFLAGS) $< $(LDLIBS) $(LDLIBS_libxenctrl) -o $@ .PHONY: clean clean: rm -f *.o *.opic *.so rm -f $(CLIENTS) $(RM) $(DEPS) .PHONY: distclean distclean: clean .PHONY: print-dir print-dir: @echo -n tools/flask/utils: .PHONY: print-end print-end: @echo .PHONY: install install: all $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_PROG) $(CLIENTS) $(DESTDIR)$(sbindir) -include $(DEPS) xen-4.9.2/tools/flask/utils/set-bool.c0000664000175000017500000000272413256712137015752 0ustar smbsmb/* * Author: Daniel De Graaf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include static void usage(char **argv) { fprintf(stderr, "Usage: %s name value\n", argv[0]); exit(1); } static int str2bool(const char *str) { if (str[0] == '0' || str[0] == '1') return (str[0] == '1'); if (!strcasecmp(str, "enabled") || !strcasecmp(str, "on") || !strcasecmp(str, "y")) return 1; if (!strcasecmp(str, "disabled") || !strcasecmp(str, "off") || !strcasecmp(str, "n")) return 0; fprintf(stderr, "Unknown value %s\n", str); exit(1); } int main(int argc, char **argv) { int err = 0; xc_interface *xch; int value; if (argc != 3) usage(argv); value = str2bool(argv[2]); xch = xc_interface_open(0,0,0); if ( !xch ) { fprintf(stderr, "Unable to create interface to xenctrl: %s\n", strerror(errno)); err = 1; goto done; } err = xc_flask_setbool(xch, argv[1], value, 1); if (err) { fprintf(stderr, "xc_flask_setbool: Unable to set boolean %s=%s: %s (%d)", argv[1], argv[2], strerror(errno), err); err = 2; goto done; } done: if ( xch ) xc_interface_close(xch); return err; } xen-4.9.2/tools/flask/utils/label-pci.c0000664000175000017500000000564713256712137016065 0ustar smbsmb/* * Author: Daniel De Graaf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include /* Pulled from linux/include/linux/ioport.h */ #define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */ #define IORESOURCE_IO 0x00000100 #define IORESOURCE_MEM 0x00000200 #define IORESOURCE_IRQ 0x00000400 #define IORESOURCE_DMA 0x00000800 #define IORESOURCE_BUS 0x00001000 static void usage (int argCnt, char *argv[]) { fprintf(stderr, "Usage: %s SBDF label\n", argv[0]); exit(1); } int main (int argCnt, char *argv[]) { int ret, err = 0; xc_interface *xch = 0; int seg, bus, dev, fn; uint32_t sbdf; uint64_t start, end, flags; char buf[1024]; FILE *f; if (argCnt != 3) usage(argCnt, argv); xch = xc_interface_open(0,0,0); if ( !xch ) { fprintf(stderr, "Unable to create interface to xenctrl: %s\n", strerror(errno)); err = 1; goto done; } sscanf(argv[1], "%x:%x:%x.%d", &seg, &bus, &dev, &fn); sbdf = (seg << 16) | (bus << 8) | (dev << 3) | fn; snprintf(buf, sizeof(buf), "/sys/bus/pci/devices/%04x:%02x:%02x.%d/resource", seg, bus, dev, fn); f = fopen(buf, "r"); if (!f) { fprintf(stderr, "Unable to find device %s: %s\n", argv[1], strerror(errno)); err = 1; goto done; } ret = xc_flask_add_device(xch, sbdf, argv[2]); if (ret) { fprintf(stderr, "xc_flask_add_device: Unable to set context of PCI device %s (0x%x) to %s: %d\n", argv[1], sbdf, argv[2], ret); err = 2; goto done; } while (fscanf(f, "0x%"SCNx64" 0x%"SCNx64" 0x%"SCNx64"\n", &start, &end, &flags) == 3) { if (flags & IORESOURCE_IO) { // printf("Port %"PRIx64"-%"PRIx64"\n", start, end); ret = xc_flask_add_ioport(xch, start, end, argv[2]); if (ret) { fprintf(stderr, "xc_flask_add_ioport %"PRIx64"-%"PRIx64" failed: %d\n", start, end, ret); err = 2; } } else if (flags & IORESOURCE_MEM) { start >>= 12; end >>= 12; // printf("IOMEM %"PRIx64"-%"PRIx64"\n", start, end); ret = xc_flask_add_iomem(xch, start, end, argv[2]); if (ret) { fprintf(stderr, "xc_flask_add_iomem %"PRIx64"-%"PRIx64" failed: %d\n", start, end, ret); err = 2; } } } fclose(f); snprintf(buf, sizeof(buf), "/sys/bus/pci/devices/%04x:%02x:%02x.%d/irq", seg, bus, dev, fn); f = fopen(buf, "r"); if (!f) goto done; if (fscanf(f, "%" SCNu64, &start) != 1) start = 0; if (start) { ret = xc_flask_add_pirq(xch, start, argv[2]); if (ret) { fprintf(stderr, "xc_flask_add_pirq %"PRIu64" failed: %d\n", start, ret); err = 2; } } fclose(f); done: if ( xch ) xc_interface_close(xch); return err; } xen-4.9.2/tools/console/0000775000175000017500000000000013256712137013257 5ustar smbsmbxen-4.9.2/tools/console/client/0000775000175000017500000000000013256712137014535 5ustar smbsmbxen-4.9.2/tools/console/client/main.c0000664000175000017500000002726013256712137015634 0ustar smbsmb/*\ * Copyright (C) International Business Machines Corp., 2005 * Author(s): Anthony Liguori * * Xen Console Daemon * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . \*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __sun__ #include #endif #include #include "xenctrl.h" #include "_paths.h" #define ESCAPE_CHARACTER 0x1d static volatile sig_atomic_t received_signal = 0; static char lockfile[sizeof (XEN_LOCK_DIR "/xenconsole.") + 8] = { 0 }; static int lockfd = -1; static void sighandler(int signum) { received_signal = 1; } static bool write_sync(int fd, const void *data, size_t size) { size_t offset = 0; ssize_t len; while (offset < size) { len = write(fd, data + offset, size - offset); if (len < 1) { return false; } offset += len; } return true; } static void usage(const char *program) { printf("Usage: %s [OPTION] DOMID\n" "Attaches to a virtual domain console\n" "\n" " -h, --help display this help and exit\n" " -n, --num N use console number N\n" " --type TYPE console type. must be 'pv' or 'serial'\n" " --start-notify-fd N file descriptor used to notify parent\n" , program); } #ifdef __sun__ void cfmakeraw(struct termios *termios_p) { termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); termios_p->c_oflag &= ~OPOST; termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); termios_p->c_cflag &= ~(CSIZE|PARENB); termios_p->c_cflag |= CS8; termios_p->c_cc[VMIN] = 0; termios_p->c_cc[VTIME] = 0; } #endif static int get_pty_fd(struct xs_handle *xs, char *path, int seconds) /* Check for a pty in xenstore, open it and return its fd. * Assumes there is already a watch set in the store for this path. */ { struct timeval tv; fd_set watch_fdset; int xs_fd = xs_fileno(xs), pty_fd = -1; int start, now; unsigned int len = 0; char *pty_path, **watch_paths; start = now = time(NULL); do { tv.tv_usec = 0; tv.tv_sec = (start + seconds) - now; FD_ZERO(&watch_fdset); FD_SET(xs_fd, &watch_fdset); if (select(xs_fd + 1, &watch_fdset, NULL, NULL, &tv)) { /* Read the watch to drain the buffer */ watch_paths = xs_read_watch(xs, &len); free(watch_paths); /* We only watch for one thing, so no need to * disambiguate: just read the pty path */ pty_path = xs_read(xs, XBT_NULL, path, &len); if (pty_path != NULL && pty_path[0] != '\0') { pty_fd = open(pty_path, O_RDWR | O_NOCTTY); if (pty_fd == -1) warn("Could not open tty `%s'", pty_path); } free(pty_path); } } while (pty_fd == -1 && (now = time(NULL)) < start + seconds); #ifdef __sun__ if (pty_fd != -1) { struct termios term; /* * The pty may come from either xend (with pygrub) or * xenconsoled. It may have tty semantics set up, or not. * While it isn't strictly necessary to have those * semantics here, it is good to have a consistent * state that is the same as under Linux. * * If tcgetattr fails, they have not been set up, * so go ahead and set them up now, by pushing the * ptem and ldterm streams modules. */ if (tcgetattr(pty_fd, &term) < 0) { ioctl(pty_fd, I_PUSH, "ptem"); ioctl(pty_fd, I_PUSH, "ldterm"); } } #endif return pty_fd; } /* don't worry too much if setting terminal attributes fail */ static void init_term(int fd, struct termios *old) { struct termios new_term; if (tcgetattr(fd, old) == -1) return; new_term = *old; cfmakeraw(&new_term); tcsetattr(fd, TCSANOW, &new_term); } static void restore_term(int fd, struct termios *old) { tcsetattr(fd, TCSANOW, old); } static int console_loop(int fd, struct xs_handle *xs, char *pty_path, bool interactive) { int ret, xs_fd = xs_fileno(xs), max_fd = -1; do { fd_set fds; FD_ZERO(&fds); if (interactive) { FD_SET(STDIN_FILENO, &fds); max_fd = STDIN_FILENO; } FD_SET(xs_fd, &fds); if (xs_fd > max_fd) max_fd = xs_fd; if (fd != -1) FD_SET(fd, &fds); if (fd > max_fd) max_fd = fd; ret = select(max_fd + 1, &fds, NULL, NULL, NULL); if (ret == -1) { if (errno == EINTR || errno == EAGAIN) { continue; } return -1; } if (FD_ISSET(xs_fileno(xs), &fds)) { int newfd = get_pty_fd(xs, pty_path, 0); if (fd != -1) close(fd); if (newfd == -1) /* Console PTY has become invalid */ return 0; fd = newfd; continue; } if (FD_ISSET(STDIN_FILENO, &fds)) { ssize_t len; char msg[60]; len = read(STDIN_FILENO, msg, sizeof(msg)); if (len == 1 && msg[0] == ESCAPE_CHARACTER) { return 0; } if (len == 0 || len == -1) { if (len == -1 && (errno == EINTR || errno == EAGAIN)) { continue; } return -1; } if (!write_sync(fd, msg, len)) { close(fd); fd = -1; continue; } } if (fd != -1 && FD_ISSET(fd, &fds)) { ssize_t len; char msg[512]; len = read(fd, msg, sizeof(msg)); if (len == 0 || len == -1) { if (len == -1 && (errno == EINTR || errno == EAGAIN)) { continue; } close(fd); fd = -1; continue; } if (!write_sync(STDOUT_FILENO, msg, len)) { perror("write() failed"); return -1; } } } while (received_signal == 0); return 0; } typedef enum { CONSOLE_INVAL, CONSOLE_PV, CONSOLE_SERIAL, } console_type; static struct termios stdin_old_attr; static void restore_term_stdin(void) { restore_term(STDIN_FILENO, &stdin_old_attr); } /* The following locking strategy is based on that from * libxl__domain_userdata_lock(), with the difference that we want to fail if we * cannot acquire the lock rather than wait indefinitely. */ static void console_lock(int domid) { struct stat stab, fstab; int fd; snprintf(lockfile, sizeof lockfile, "%s%d", XEN_LOCK_DIR "/xenconsole.", domid); while (true) { fd = open(lockfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) err(errno, "Could not open %s", lockfile); while (flock(fd, LOCK_EX | LOCK_NB)) { if (errno == EINTR) continue; else err(errno, "Could not lock %s", lockfile); } if (fstat(fd, &fstab)) err(errno, "Could not fstat %s", lockfile); if (stat(lockfile, &stab)) { if (errno != ENOENT) err(errno, "Could not stat %s", lockfile); } else { if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino) break; } close(fd); } lockfd = fd; return; } static void console_unlock(void) { if (lockfile[0] && lockfd != -1) { unlink(lockfile); close(lockfd); } } int main(int argc, char **argv) { struct termios attr; int domid; char *sopt = "hn:"; int ch; unsigned int num = 0; int opt_ind=0; int start_notify_fd = -1; struct option lopt[] = { { "type", 1, 0, 't' }, { "num", 1, 0, 'n' }, { "help", 0, 0, 'h' }, { "start-notify-fd", 1, 0, 's' }, { 0 }, }; char *dom_path = NULL, *path = NULL, *test = NULL; int spty, xsfd; struct xs_handle *xs; char *end; console_type type = CONSOLE_INVAL; bool interactive = 0; if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) interactive = 1; while((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch(ch) { case 'h': usage(argv[0]); exit(0); break; case 'n': num = atoi(optarg); break; case 't': if (!strcmp(optarg, "serial")) type = CONSOLE_SERIAL; else if (!strcmp(optarg, "pv")) type = CONSOLE_PV; else { fprintf(stderr, "Invalid type argument\n"); fprintf(stderr, "Console types supported are: serial, pv\n"); exit(EINVAL); } break; case 's': start_notify_fd = atoi(optarg); break; default: fprintf(stderr, "Invalid argument\n"); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); exit(EINVAL); } } if (optind >= argc) { fprintf(stderr, "DOMID should be specified\n"); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); exit(EINVAL); } domid = strtol(argv[optind], &end, 10); if (end && *end) { fprintf(stderr, "Invalid DOMID `%s'\n", argv[optind]); fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); exit(EINVAL); } xs = xs_daemon_open(); if (xs == NULL) { err(errno, "Could not contact XenStore"); } signal(SIGTERM, sighandler); dom_path = xs_get_domain_path(xs, domid); if (dom_path == NULL) err(errno, "xs_get_domain_path()"); if (type == CONSOLE_INVAL) { xc_dominfo_t xcinfo; xc_interface *xc_handle = xc_interface_open(0,0,0); if (xc_handle == NULL) err(errno, "Could not open xc interface"); if ( (xc_domain_getinfo(xc_handle, domid, 1, &xcinfo) != 1) || (xcinfo.domid != domid) ) { xc_interface_close(xc_handle); err(errno, "Failed to get domain information"); } /* default to pv console for pv guests and serial for hvm guests */ if (xcinfo.hvm) type = CONSOLE_SERIAL; else type = CONSOLE_PV; xc_interface_close(xc_handle); } path = malloc(strlen(dom_path) + strlen("/device/console/0/tty") + 5); if (path == NULL) err(ENOMEM, "malloc"); if (type == CONSOLE_SERIAL) { snprintf(path, strlen(dom_path) + strlen("/serial/0/tty") + 5, "%s/serial/%d/tty", dom_path, num); test = xs_read(xs, XBT_NULL, path, NULL); free(test); if (test == NULL) type = CONSOLE_PV; } if (type == CONSOLE_PV) { if (num == 0) snprintf(path, strlen(dom_path) + strlen("/console/tty") + 1, "%s/console/tty", dom_path); else snprintf(path, strlen(dom_path) + strlen("/device/console/%d/tty") + 5, "%s/device/console/%d/tty", dom_path, num); } /* FIXME consoled currently does not assume domain-0 doesn't have a console which is good when we break domain-0 up. To keep us user friendly, we'll bail out here since no data will ever show up on domain-0. */ if (domid == 0) { fprintf(stderr, "Can't specify Domain-0\n"); exit(EINVAL); } console_lock(domid); atexit(console_unlock); /* Set a watch on this domain's console pty */ if (!xs_watch(xs, path, "")) err(errno, "Can't set watch for console pty"); xsfd = xs_fileno(xs); /* Wait a little bit for tty to appear. There is a race condition that occurs after xend creates a domain. This code might be running before consoled has noticed the new domain and setup a pty for it. */ spty = get_pty_fd(xs, path, 5); if (spty == -1) { err(errno, "Could not read tty from store"); } init_term(spty, &attr); if (interactive) { init_term(STDIN_FILENO, &stdin_old_attr); atexit(restore_term_stdin); /* if this fails, oh dear */ } if (start_notify_fd != -1) { /* Write 0x00 to notify parent about client's readiness */ static const char msg[] = { 0x00 }; int r; do { r = write(start_notify_fd, msg, 1); } while ((r == -1 && errno == EINTR) || r == 0); if (r == -1) err(errno, "Could not notify parent with fd %d", start_notify_fd); close(start_notify_fd); } console_loop(spty, xs, path, interactive); free(path); free(dom_path); return 0; } xen-4.9.2/tools/console/daemon/0000775000175000017500000000000013256712137014522 5ustar smbsmbxen-4.9.2/tools/console/daemon/utils.h0000664000175000017500000000251413256712137016035 0ustar smbsmb/*\ * Copyright (C) International Business Machines Corp., 2005 * Author(s): Anthony Liguori * * Xen Console Daemon * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . \*/ #ifndef CONSOLED_UTILS_H #define CONSOLED_UTILS_H #include #include #include #define XC_WANT_COMPAT_MAP_FOREIGN_API #include #include void daemonize(const char *pidfile); bool xen_setup(void); extern struct xs_handle *xs; extern xc_interface *xc; #if 1 #define dolog(val, fmt, ...) do { \ if ((val) == LOG_ERR) \ fprintf(stderr, fmt "\n", ## __VA_ARGS__); \ syslog(val, fmt, ## __VA_ARGS__); \ } while (/* CONSTCOND */0) #else #define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__) #endif #endif xen-4.9.2/tools/console/daemon/io.c0000664000175000017500000006605513256712137015311 0ustar smbsmb/* * Copyright (C) International Business Machines Corp., 2005 * Author(s): Anthony Liguori * * Xen Console Daemon * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #define _GNU_SOURCE #include "utils.h" #include "io.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #elif defined(__linux__) #include #elif defined(__sun__) #include #elif defined(__FreeBSD__) #include #include #endif #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) /* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */ #define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2) /* How many events are allowed in each time period */ #define RATE_LIMIT_ALLOWANCE 30 /* Duration of each time period in ms */ #define RATE_LIMIT_PERIOD 200 extern int log_reload; extern int log_guest; extern int log_hv; extern int log_time_hv; extern int log_time_guest; extern char *log_dir; extern int discard_overflowed_data; static int log_time_hv_needts = 1; static int log_time_guest_needts = 1; static int log_hv_fd = -1; static xengnttab_handle *xgt_handle = NULL; static struct pollfd *fds; static unsigned int current_array_size; static unsigned int nr_fds; #define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1)) struct buffer { char *data; size_t consumed; size_t size; size_t capacity; size_t max_capacity; }; struct domain { int domid; int master_fd; int master_pollfd_idx; int slave_fd; int log_fd; bool is_dead; unsigned last_seen; struct buffer buffer; struct domain *next; char *conspath; int ring_ref; xenevtchn_port_or_error_t local_port; xenevtchn_port_or_error_t remote_port; xenevtchn_handle *xce_handle; int xce_pollfd_idx; struct xencons_interface *interface; int event_count; long long next_period; }; static struct domain *dom_head; static int write_all(int fd, const char* buf, size_t len) { while (len) { ssize_t ret = write(fd, buf, len); if (ret == -1 && errno == EINTR) continue; if (ret <= 0) return -1; len -= ret; buf += ret; } return 0; } static int write_with_timestamp(int fd, const char *data, size_t sz, int *needts) { char ts[32]; time_t now = time(NULL); const struct tm *tmnow = localtime(&now); size_t tslen = strftime(ts, sizeof(ts), "[%Y-%m-%d %H:%M:%S] ", tmnow); const char *last_byte = data + sz - 1; while (data <= last_byte) { const char *nl = memchr(data, '\n', last_byte + 1 - data); int found_nl = (nl != NULL); if (!found_nl) nl = last_byte; if ((*needts && write_all(fd, ts, tslen)) || write_all(fd, data, nl + 1 - data)) return -1; *needts = found_nl; data = nl + 1; if (found_nl) { // If we printed a newline, strip all \r following it while (data <= last_byte && *data == '\r') data++; } } return 0; } static void buffer_append(struct domain *dom) { struct buffer *buffer = &dom->buffer; XENCONS_RING_IDX cons, prod, size; struct xencons_interface *intf = dom->interface; cons = intf->out_cons; prod = intf->out_prod; xen_mb(); size = prod - cons; if ((size == 0) || (size > sizeof(intf->out))) return; if ((buffer->capacity - buffer->size) < size) { buffer->capacity += (size + 1024); buffer->data = realloc(buffer->data, buffer->capacity); if (buffer->data == NULL) { dolog(LOG_ERR, "Memory allocation failed"); exit(ENOMEM); } } while (cons != prod) buffer->data[buffer->size++] = intf->out[ MASK_XENCONS_IDX(cons++, intf->out)]; xen_mb(); intf->out_cons = cons; xenevtchn_notify(dom->xce_handle, dom->local_port); /* Get the data to the logfile as early as possible because if * no one is listening on the console pty then it will fill up * and handle_tty_write will stop being called. */ if (dom->log_fd != -1) { int logret; if (log_time_guest) { logret = write_with_timestamp( dom->log_fd, buffer->data + buffer->size - size, size, &log_time_guest_needts); } else { logret = write_all( dom->log_fd, buffer->data + buffer->size - size, size); } if (logret < 0) dolog(LOG_ERR, "Write to log failed " "on domain %d: %d (%s)\n", dom->domid, errno, strerror(errno)); } if (discard_overflowed_data && buffer->max_capacity && buffer->size > 5 * buffer->max_capacity / 4) { if (buffer->consumed > buffer->max_capacity / 4) { /* Move data up in buffer, since beginning has * been output. Only needed because buffer is * not a ring buffer *sigh* */ memmove(buffer->data, buffer->data + buffer->consumed, buffer->size - buffer->consumed); buffer->size -= buffer->consumed; buffer->consumed = 0; } else { /* Discard the middle of the data. */ size_t over = buffer->size - buffer->max_capacity; memmove(buffer->data + buffer->max_capacity / 2, buffer->data + buffer->max_capacity, over); buffer->size = buffer->max_capacity / 2 + over; } } } static bool buffer_empty(struct buffer *buffer) { return buffer->size == 0; } static void buffer_advance(struct buffer *buffer, size_t len) { buffer->consumed += len; if (buffer->consumed == buffer->size) { buffer->consumed = 0; buffer->size = 0; if (buffer->max_capacity && buffer->capacity > buffer->max_capacity) { buffer->data = realloc(buffer->data, buffer->max_capacity); buffer->capacity = buffer->max_capacity; } } } static bool domain_is_valid(int domid) { bool ret; xc_dominfo_t info; ret = (xc_domain_getinfo(xc, domid, 1, &info) == 1 && info.domid == domid); return ret; } static int create_hv_log(void) { char logfile[PATH_MAX]; int fd; snprintf(logfile, PATH_MAX-1, "%s/hypervisor.log", log_dir); logfile[PATH_MAX-1] = '\0'; fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644); if (fd == -1) dolog(LOG_ERR, "Failed to open log %s: %d (%s)", logfile, errno, strerror(errno)); if (fd != -1 && log_time_hv) { if (write_with_timestamp(fd, "Logfile Opened\n", strlen("Logfile Opened\n"), &log_time_hv_needts) < 0) { dolog(LOG_ERR, "Failed to log opening timestamp " "in %s: %d (%s)", logfile, errno, strerror(errno)); close(fd); return -1; } } return fd; } static int create_domain_log(struct domain *dom) { char logfile[PATH_MAX]; char *namepath, *data, *s; int fd; unsigned int len; namepath = xs_get_domain_path(xs, dom->domid); s = realloc(namepath, strlen(namepath) + 6); if (s == NULL) { free(namepath); return -1; } namepath = s; strcat(namepath, "/name"); data = xs_read(xs, XBT_NULL, namepath, &len); free(namepath); if (!data) return -1; if (!len) { free(data); return -1; } snprintf(logfile, PATH_MAX-1, "%s/guest-%s.log", log_dir, data); free(data); logfile[PATH_MAX-1] = '\0'; fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644); if (fd == -1) dolog(LOG_ERR, "Failed to open log %s: %d (%s)", logfile, errno, strerror(errno)); if (fd != -1 && log_time_guest) { if (write_with_timestamp(fd, "Logfile Opened\n", strlen("Logfile Opened\n"), &log_time_guest_needts) < 0) { dolog(LOG_ERR, "Failed to log opening timestamp " "in %s: %d (%s)", logfile, errno, strerror(errno)); close(fd); return -1; } } return fd; } static void domain_close_tty(struct domain *dom) { if (dom->master_fd != -1) { close(dom->master_fd); dom->master_fd = -1; } if (dom->slave_fd != -1) { close(dom->slave_fd); dom->slave_fd = -1; } } #ifdef __sun__ static int openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp) { const char *slave; int mfd = -1, sfd = -1; *amaster = *aslave = -1; mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY); if (mfd < 0) goto err; if (grantpt(mfd) == -1 || unlockpt(mfd) == -1) goto err; if ((slave = ptsname(mfd)) == NULL) goto err; if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1) goto err; if (ioctl(sfd, I_PUSH, "ptem") == -1) goto err; if (amaster) *amaster = mfd; if (aslave) *aslave = sfd; if (winp) ioctl(sfd, TIOCSWINSZ, winp); if (termp) tcsetattr(sfd, TCSAFLUSH, termp); assert(name == NULL); return 0; err: if (sfd != -1) close(sfd); close(mfd); return -1; } void cfmakeraw(struct termios *termios_p) { termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); termios_p->c_oflag &= ~OPOST; termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); termios_p->c_cflag &= ~(CSIZE|PARENB); termios_p->c_cflag |= CS8; termios_p->c_cc[VMIN] = 0; termios_p->c_cc[VTIME] = 0; } #endif /* __sun__ */ static int domain_create_tty(struct domain *dom) { const char *slave; char *path; int err; bool success; char *data; unsigned int len; struct termios term; assert(dom->slave_fd == -1); assert(dom->master_fd == -1); if (openpty(&dom->master_fd, &dom->slave_fd, NULL, NULL, NULL) < 0) { err = errno; dolog(LOG_ERR, "Failed to create tty for domain-%d " "(errno = %i, %s)", dom->domid, err, strerror(err)); return 0; } if (tcgetattr(dom->slave_fd, &term) < 0) { err = errno; dolog(LOG_ERR, "Failed to get tty attributes for domain-%d " "(errno = %i, %s)", dom->domid, err, strerror(err)); goto out; } cfmakeraw(&term); if (tcsetattr(dom->slave_fd, TCSANOW, &term) < 0) { err = errno; dolog(LOG_ERR, "Failed to set tty attributes for domain-%d " "(errno = %i, %s)", dom->domid, err, strerror(err)); goto out; } if ((slave = ptsname(dom->master_fd)) == NULL) { err = errno; dolog(LOG_ERR, "Failed to get slave name for domain-%d " "(errno = %i, %s)", dom->domid, err, strerror(err)); goto out; } success = asprintf(&path, "%s/limit", dom->conspath) != -1; if (!success) goto out; data = xs_read(xs, XBT_NULL, path, &len); if (data) { dom->buffer.max_capacity = strtoul(data, 0, 0); free(data); } free(path); success = (asprintf(&path, "%s/tty", dom->conspath) != -1); if (!success) goto out; success = xs_write(xs, XBT_NULL, path, slave, strlen(slave)); free(path); if (!success) goto out; if (fcntl(dom->master_fd, F_SETFL, O_NONBLOCK) == -1) goto out; return 1; out: domain_close_tty(dom); return 0; } /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */ static int xs_gather(struct xs_handle *xs, const char *dir, ...) { va_list ap; const char *name; char *path; int ret = 0; va_start(ap, dir); while (ret == 0 && (name = va_arg(ap, char *)) != NULL) { const char *fmt = va_arg(ap, char *); void *result = va_arg(ap, void *); char *p; if (asprintf(&path, "%s/%s", dir, name) == -1) { ret = ENOMEM; break; } p = xs_read(xs, XBT_NULL, path, NULL); free(path); if (p == NULL) { ret = ENOENT; break; } if (fmt) { if (sscanf(p, fmt, result) == 0) ret = EINVAL; free(p); } else *(char **)result = p; } va_end(ap); return ret; } static void domain_unmap_interface(struct domain *dom) { if (dom->interface == NULL) return; if (xgt_handle && dom->ring_ref == -1) xengnttab_unmap(xgt_handle, dom->interface, 1); else munmap(dom->interface, XC_PAGE_SIZE); dom->interface = NULL; dom->ring_ref = -1; } static int domain_create_ring(struct domain *dom) { int err, remote_port, ring_ref, rc; char *type, path[PATH_MAX]; err = xs_gather(xs, dom->conspath, "ring-ref", "%u", &ring_ref, "port", "%i", &remote_port, NULL); if (err) goto out; snprintf(path, sizeof(path), "%s/type", dom->conspath); type = xs_read(xs, XBT_NULL, path, NULL); if (type && strcmp(type, "xenconsoled") != 0) { free(type); return 0; } free(type); /* If using ring_ref and it has changed, remap */ if (ring_ref != dom->ring_ref && dom->ring_ref != -1) domain_unmap_interface(dom); if (!dom->interface && xgt_handle) { /* Prefer using grant table */ dom->interface = xengnttab_map_grant_ref(xgt_handle, dom->domid, GNTTAB_RESERVED_CONSOLE, PROT_READ|PROT_WRITE); dom->ring_ref = -1; } if (!dom->interface) { /* Fall back to xc_map_foreign_range */ dom->interface = xc_map_foreign_range( xc, dom->domid, XC_PAGE_SIZE, PROT_READ|PROT_WRITE, (unsigned long)ring_ref); if (dom->interface == NULL) { err = EINVAL; goto out; } dom->ring_ref = ring_ref; } /* Go no further if port has not changed and we are still bound. */ if (remote_port == dom->remote_port) { xc_evtchn_status_t status = { .dom = DOMID_SELF, .port = dom->local_port }; if ((xc_evtchn_status(xc, &status) == 0) && (status.status == EVTCHNSTAT_interdomain)) goto out; } dom->local_port = -1; dom->remote_port = -1; if (dom->xce_handle != NULL) xenevtchn_close(dom->xce_handle); /* Opening evtchn independently for each console is a bit * wasteful, but that's how the code is structured... */ dom->xce_handle = xenevtchn_open(NULL, 0); if (dom->xce_handle == NULL) { err = errno; goto out; } rc = xenevtchn_bind_interdomain(dom->xce_handle, dom->domid, remote_port); if (rc == -1) { err = errno; xenevtchn_close(dom->xce_handle); dom->xce_handle = NULL; goto out; } dom->local_port = rc; dom->remote_port = remote_port; if (dom->master_fd == -1) { if (!domain_create_tty(dom)) { err = errno; xenevtchn_close(dom->xce_handle); dom->xce_handle = NULL; dom->local_port = -1; dom->remote_port = -1; goto out; } } if (log_guest && (dom->log_fd == -1)) dom->log_fd = create_domain_log(dom); out: return err; } static bool watch_domain(struct domain *dom, bool watch) { char domid_str[3 + MAX_STRLEN(dom->domid)]; bool success; snprintf(domid_str, sizeof(domid_str), "dom%u", dom->domid); if (watch) { success = xs_watch(xs, dom->conspath, domid_str); if (success) domain_create_ring(dom); else xs_unwatch(xs, dom->conspath, domid_str); } else { success = xs_unwatch(xs, dom->conspath, domid_str); } return success; } static struct domain *create_domain(int domid) { struct domain *dom; char *s; struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) { dolog(LOG_ERR, "Cannot get time of day %s:%s:L%d", __FILE__, __FUNCTION__, __LINE__); return NULL; } dom = calloc(1, sizeof *dom); if (dom == NULL) { dolog(LOG_ERR, "Out of memory %s:%s():L%d", __FILE__, __FUNCTION__, __LINE__); exit(ENOMEM); } dom->domid = domid; dom->conspath = xs_get_domain_path(xs, dom->domid); s = realloc(dom->conspath, strlen(dom->conspath) + strlen("/console") + 1); if (s == NULL) goto out; dom->conspath = s; strcat(dom->conspath, "/console"); dom->master_fd = -1; dom->master_pollfd_idx = -1; dom->slave_fd = -1; dom->log_fd = -1; dom->xce_pollfd_idx = -1; dom->next_period = ((long long)ts.tv_sec * 1000) + (ts.tv_nsec / 1000000) + RATE_LIMIT_PERIOD; dom->ring_ref = -1; dom->local_port = -1; dom->remote_port = -1; if (!watch_domain(dom, true)) goto out; dom->next = dom_head; dom_head = dom; dolog(LOG_DEBUG, "New domain %d", domid); return dom; out: free(dom->conspath); free(dom); return NULL; } static struct domain *lookup_domain(int domid) { struct domain *dom; for (dom = dom_head; dom; dom = dom->next) if (dom->domid == domid) return dom; return NULL; } static void remove_domain(struct domain *dom) { struct domain **pp; dolog(LOG_DEBUG, "Removing domain-%d", dom->domid); for (pp = &dom_head; *pp; pp = &(*pp)->next) { if (dom == *pp) { *pp = dom->next; free(dom); break; } } } static void cleanup_domain(struct domain *d) { domain_close_tty(d); if (d->log_fd != -1) { close(d->log_fd); d->log_fd = -1; } free(d->buffer.data); d->buffer.data = NULL; free(d->conspath); d->conspath = NULL; remove_domain(d); } static void shutdown_domain(struct domain *d) { d->is_dead = true; watch_domain(d, false); domain_unmap_interface(d); if (d->xce_handle != NULL) xenevtchn_close(d->xce_handle); d->xce_handle = NULL; } static unsigned enum_pass = 0; static void enum_domains(void) { int domid = 1; xc_dominfo_t dominfo; struct domain *dom; enum_pass++; while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) { dom = lookup_domain(dominfo.domid); if (dominfo.dying) { if (dom) shutdown_domain(dom); } else { if (dom == NULL) dom = create_domain(dominfo.domid); } if (dom) dom->last_seen = enum_pass; domid = dominfo.domid + 1; } } static int ring_free_bytes(struct domain *dom) { struct xencons_interface *intf = dom->interface; XENCONS_RING_IDX cons, prod, space; cons = intf->in_cons; prod = intf->in_prod; xen_mb(); space = prod - cons; if (space > sizeof(intf->in)) return 0; /* ring is screwed: ignore it */ return (sizeof(intf->in) - space); } static void domain_handle_broken_tty(struct domain *dom, int recreate) { domain_close_tty(dom); if (recreate) { domain_create_tty(dom); } else { shutdown_domain(dom); } } static void handle_tty_read(struct domain *dom) { ssize_t len = 0; char msg[80]; int i; struct xencons_interface *intf = dom->interface; XENCONS_RING_IDX prod; if (dom->is_dead) return; len = ring_free_bytes(dom); if (len == 0) return; if (len > sizeof(msg)) len = sizeof(msg); len = read(dom->master_fd, msg, len); /* * Note: on Solaris, len == 0 means the slave closed, and this * is no problem, but Linux can't handle this usefully, so we * keep the slave open for the duration. */ if (len < 0) { domain_handle_broken_tty(dom, domain_is_valid(dom->domid)); } else if (domain_is_valid(dom->domid)) { prod = intf->in_prod; for (i = 0; i < len; i++) { intf->in[MASK_XENCONS_IDX(prod++, intf->in)] = msg[i]; } xen_wmb(); intf->in_prod = prod; xenevtchn_notify(dom->xce_handle, dom->local_port); } else { domain_close_tty(dom); shutdown_domain(dom); } } static void handle_tty_write(struct domain *dom) { ssize_t len; if (dom->is_dead) return; len = write(dom->master_fd, dom->buffer.data + dom->buffer.consumed, dom->buffer.size - dom->buffer.consumed); if (len < 1) { dolog(LOG_DEBUG, "Write failed on domain %d: %zd, %d\n", dom->domid, len, errno); domain_handle_broken_tty(dom, domain_is_valid(dom->domid)); } else { buffer_advance(&dom->buffer, len); } } static void handle_ring_read(struct domain *dom) { xenevtchn_port_or_error_t port; if (dom->is_dead) return; if ((port = xenevtchn_pending(dom->xce_handle)) == -1) return; dom->event_count++; buffer_append(dom); if (dom->event_count < RATE_LIMIT_ALLOWANCE) (void)xenevtchn_unmask(dom->xce_handle, port); } static void handle_xs(void) { char **vec; int domid; struct domain *dom; unsigned int num; vec = xs_read_watch(xs, &num); if (!vec) return; if (!strcmp(vec[XS_WATCH_TOKEN], "domlist")) enum_domains(); else if (sscanf(vec[XS_WATCH_TOKEN], "dom%u", &domid) == 1) { dom = lookup_domain(domid); /* We may get watches firing for domains that have recently been removed, so dom may be NULL here. */ if (dom && dom->is_dead == false) domain_create_ring(dom); } free(vec); } static void handle_hv_logs(xenevtchn_handle *xce_handle, bool force) { static char buffer[1024*16]; char *bufptr = buffer; unsigned int size; static uint32_t index = 0; xenevtchn_port_or_error_t port = -1; if (!force && ((port = xenevtchn_pending(xce_handle)) == -1)) return; do { int logret; size = sizeof(buffer); if (xc_readconsolering(xc, bufptr, &size, 0, 1, &index) != 0 || size == 0) break; if (log_time_hv) logret = write_with_timestamp(log_hv_fd, buffer, size, &log_time_hv_needts); else logret = write_all(log_hv_fd, buffer, size); if (logret < 0) dolog(LOG_ERR, "Failed to write hypervisor log: " "%d (%s)", errno, strerror(errno)); } while (size == sizeof(buffer)); if (port != -1) (void)xenevtchn_unmask(xce_handle, port); } static void handle_log_reload(void) { if (log_guest) { struct domain *d; for (d = dom_head; d; d = d->next) { if (d->log_fd != -1) close(d->log_fd); d->log_fd = create_domain_log(d); } } if (log_hv) { if (log_hv_fd != -1) close(log_hv_fd); log_hv_fd = create_hv_log(); } } /* Returns index inside fds array if succees, -1 if fail */ static int set_fds(int fd, short events) { int ret; if (current_array_size < nr_fds + 1) { struct pollfd *new_fds = NULL; unsigned long newsize; /* Round up to 2^8 boundary, in practice this just * make newsize larger than current_array_size. */ newsize = ROUNDUP(nr_fds + 1, 8); new_fds = realloc(fds, sizeof(struct pollfd)*newsize); if (!new_fds) goto fail; fds = new_fds; memset(&fds[0] + current_array_size, 0, sizeof(struct pollfd) * (newsize-current_array_size)); current_array_size = newsize; } fds[nr_fds].fd = fd; fds[nr_fds].events = events; ret = nr_fds; nr_fds++; return ret; fail: dolog(LOG_ERR, "realloc failed, ignoring fd %d\n", fd); return -1; } static void reset_fds(void) { nr_fds = 0; if (fds) memset(fds, 0, sizeof(struct pollfd) * current_array_size); } void handle_io(void) { int ret; xenevtchn_port_or_error_t log_hv_evtchn = -1; int xce_pollfd_idx = -1; int xs_pollfd_idx = -1; xenevtchn_handle *xce_handle = NULL; if (log_hv) { xce_handle = xenevtchn_open(NULL, 0); if (xce_handle == NULL) { dolog(LOG_ERR, "Failed to open xce handle: %d (%s)", errno, strerror(errno)); goto out; } log_hv_fd = create_hv_log(); if (log_hv_fd == -1) goto out; log_hv_evtchn = xenevtchn_bind_virq(xce_handle, VIRQ_CON_RING); if (log_hv_evtchn == -1) { dolog(LOG_ERR, "Failed to bind to VIRQ_CON_RING: " "%d (%s)", errno, strerror(errno)); goto out; } /* Log the boot dmesg even if VIRQ_CON_RING isn't pending. */ handle_hv_logs(xce_handle, true); } xgt_handle = xengnttab_open(NULL, 0); if (xgt_handle == NULL) { dolog(LOG_DEBUG, "Failed to open xcg handle: %d (%s)", errno, strerror(errno)); } enum_domains(); for (;;) { struct domain *d, *n; int poll_timeout; /* timeout in milliseconds */ struct timespec ts; long long now, next_timeout = 0; reset_fds(); xs_pollfd_idx = set_fds(xs_fileno(xs), POLLIN|POLLPRI); if (log_hv) xce_pollfd_idx = set_fds(xenevtchn_fd(xce_handle), POLLIN|POLLPRI); if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) break; now = ((long long)ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); /* Re-calculate any event counter allowances & unblock domains with new allowance */ for (d = dom_head; d; d = d->next) { /* CS 16257:955ee4fa1345 introduces a 5ms fuzz * for select(), it is not clear poll() has * similar behavior (returning a couple of ms * sooner than requested) as well. Just leave * the fuzz here. Remove it with a separate * patch if necessary */ if ((now+5) > d->next_period) { d->next_period = now + RATE_LIMIT_PERIOD; if (d->event_count >= RATE_LIMIT_ALLOWANCE) { (void)xenevtchn_unmask(d->xce_handle, d->local_port); } d->event_count = 0; } } for (d = dom_head; d; d = d->next) { if (d->event_count >= RATE_LIMIT_ALLOWANCE) { /* Determine if we're going to be the next time slice to expire */ if (!next_timeout || d->next_period < next_timeout) next_timeout = d->next_period; } else if (d->xce_handle != NULL) { if (discard_overflowed_data || !d->buffer.max_capacity || d->buffer.size < d->buffer.max_capacity) { int evtchn_fd = xenevtchn_fd(d->xce_handle); d->xce_pollfd_idx = set_fds(evtchn_fd, POLLIN|POLLPRI); } } if (d->master_fd != -1) { short events = 0; if (!d->is_dead && ring_free_bytes(d)) events |= POLLIN; if (!buffer_empty(&d->buffer)) events |= POLLOUT; if (events) d->master_pollfd_idx = set_fds(d->master_fd, events|POLLPRI); } } /* If any domain has been rate limited, we need to work out what timeout to supply to poll */ if (next_timeout) { long long duration = (next_timeout - now); if (duration <= 0) /* sanity check */ duration = 1; poll_timeout = (int)duration; } ret = poll(fds, nr_fds, next_timeout ? poll_timeout : -1); if (log_reload) { int saved_errno = errno; handle_log_reload(); log_reload = 0; errno = saved_errno; } /* Abort if poll failed, except for EINTR cases which indicate a possible log reload */ if (ret == -1) { if (errno == EINTR) continue; dolog(LOG_ERR, "Failure in poll: %d (%s)", errno, strerror(errno)); break; } if (log_hv && xce_pollfd_idx != -1) { if (fds[xce_pollfd_idx].revents & ~(POLLIN|POLLOUT|POLLPRI)) { dolog(LOG_ERR, "Failure in poll xce_handle: %d (%s)", errno, strerror(errno)); break; } else if (fds[xce_pollfd_idx].revents & POLLIN) handle_hv_logs(xce_handle, false); xce_pollfd_idx = -1; } if (ret <= 0) continue; if (xs_pollfd_idx != -1) { if (fds[xs_pollfd_idx].revents & ~(POLLIN|POLLOUT|POLLPRI)) { dolog(LOG_ERR, "Failure in poll xs_handle: %d (%s)", errno, strerror(errno)); break; } else if (fds[xs_pollfd_idx].revents & POLLIN) handle_xs(); xs_pollfd_idx = -1; } for (d = dom_head; d; d = n) { n = d->next; if (d->event_count < RATE_LIMIT_ALLOWANCE) { if (d->xce_handle != NULL && d->xce_pollfd_idx != -1 && !(fds[d->xce_pollfd_idx].revents & ~(POLLIN|POLLOUT|POLLPRI)) && (fds[d->xce_pollfd_idx].revents & POLLIN)) handle_ring_read(d); } if (d->master_fd != -1 && d->master_pollfd_idx != -1) { if (fds[d->master_pollfd_idx].revents & ~(POLLIN|POLLOUT|POLLPRI)) domain_handle_broken_tty(d, domain_is_valid(d->domid)); else { if (fds[d->master_pollfd_idx].revents & POLLIN) handle_tty_read(d); if (fds[d->master_pollfd_idx].revents & POLLOUT) handle_tty_write(d); } } d->xce_pollfd_idx = d->master_pollfd_idx = -1; if (d->last_seen != enum_pass) shutdown_domain(d); if (d->is_dead) cleanup_domain(d); } } free(fds); current_array_size = 0; out: if (log_hv_fd != -1) { close(log_hv_fd); log_hv_fd = -1; } if (xce_handle != NULL) { xenevtchn_close(xce_handle); xce_handle = NULL; } if (xgt_handle != NULL) { xengnttab_close(xgt_handle); xgt_handle = NULL; } log_hv_evtchn = -1; } /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/console/daemon/utils.c0000664000175000017500000000526713256712137016040 0ustar smbsmb/*\ * Copyright (C) International Business Machines Corp., 2005 * Author(s): Anthony Liguori * * Xen Console Daemon * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . \*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" struct xs_handle *xs; xc_interface *xc; static void child_exit(int sig) { while (waitpid(-1, NULL, WNOHANG) > 0); } void daemonize(const char *pidfile) { pid_t pid; int fd; int len; int i; char buf[100]; if ((pid = fork()) > 0) { exit(0); } else if (pid == -1) { err(errno, "fork() failed"); } setsid(); if ((pid = fork()) > 0) { exit(0); } else if (pid == -1) { err(errno, "fork() failed"); } /* redirect fd 0,1,2 to /dev/null */ if ((fd = open("/dev/null",O_RDWR)) == -1) { exit(1); } for (i = 0; i <= 2; i++) { close(i); dup2(fd, i); } close(fd); umask(027); if (chdir("/") < 0) exit (1); fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR|S_IWUSR); if (fd == -1) { exit(1); } if (lockf(fd, F_TLOCK, 0) == -1) { exit(1); } len = snprintf(buf, sizeof(buf), "%ld\n", (long)getpid()); if (write(fd, buf, len) < 0) exit(1); signal(SIGCHLD, child_exit); signal(SIGTSTP, SIG_IGN); signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); close(fd); } bool xen_setup(void) { xs = xs_daemon_open(); if (xs == NULL) { dolog(LOG_ERR, "Failed to contact xenstore (%m). Is it running?"); goto out; } xc = xc_interface_open(0,0,0); if (!xc) { dolog(LOG_ERR, "Failed to contact hypervisor (%m)"); goto out; } if (!xs_watch(xs, "@introduceDomain", "domlist")) { dolog(LOG_ERR, "xenstore watch on @introduceDomain fails."); goto out; } if (!xs_watch(xs, "@releaseDomain", "domlist")) { dolog(LOG_ERR, "xenstore watch on @releaseDomain fails."); goto out; } return true; out: if (xs) xs_daemon_close(xs); if (xc) xc_interface_close(xc); return false; } xen-4.9.2/tools/console/daemon/io.h0000664000175000017500000000151213256712137015301 0ustar smbsmb/*\ * Copyright (C) International Business Machines Corp., 2005 * Author(s): Anthony Liguori * * Xen Console Daemon * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . \*/ #ifndef CONSOLED_IO_H #define CONSOLED_IO_H void handle_io(void); #endif xen-4.9.2/tools/console/daemon/main.c0000664000175000017500000001212713256712137015615 0ustar smbsmb/*\ * Copyright (C) International Business Machines Corp., 2005 * Author(s): Anthony Liguori * * Xen Console Daemon * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . \*/ #include #include #include #include #include #include #include #include #include #include "xenctrl.h" #include "utils.h" #include "io.h" #include "_paths.h" int log_reload = 0; int log_guest = 0; int log_hv = 0; int log_time_hv = 0; int log_time_guest = 0; char *log_dir = NULL; int discard_overflowed_data = 1; static void handle_hup(int sig) { log_reload = 1; } static void usage(char *name) { printf("Usage: %s [-h] [-V] [-v] [-i] [--log=none|guest|hv|all] [--log-dir=DIR] [--pid-file=PATH] [-t, --timestamp=none|guest|hv|all] [-o, --overflow-data=discard|keep]\n", name); } static void version(char *name) { printf("Xen Console Daemon 3.0\n"); } static void increase_fd_limit(void) { /* * We require many file descriptors: * - per domain: pty master, pty slave, logfile and evtchn * - misc extra: hypervisor log, privcmd, gntdev, std... * * Allow a generous 1000 for misc, and calculate the maximum possible * number of fds which could be used. */ unsigned min_fds = (DOMID_FIRST_RESERVED * 4) + 1000; struct rlimit lim, new = { min_fds, min_fds }; if (getrlimit(RLIMIT_NOFILE, &lim) < 0) { fprintf(stderr, "Failed to obtain fd limit: %s\n", strerror(errno)); exit(1); } /* Do we already have sufficient? Great! */ if (lim.rlim_cur >= min_fds) return; /* Try to increase our limit. */ if (setrlimit(RLIMIT_NOFILE, &new) < 0) syslog(LOG_WARNING, "Unable to increase fd limit from {%llu, %llu} to " "{%llu, %llu}: (%s) - May run out with lots of domains", (unsigned long long)lim.rlim_cur, (unsigned long long)lim.rlim_max, (unsigned long long)new.rlim_cur, (unsigned long long)new.rlim_max, strerror(errno)); } int main(int argc, char **argv) { const char *sopts = "hVvit:o:"; struct option lopts[] = { { "help", 0, 0, 'h' }, { "version", 0, 0, 'V' }, { "verbose", 0, 0, 'v' }, { "interactive", 0, 0, 'i' }, { "log", 1, 0, 'l' }, { "log-dir", 1, 0, 'r' }, { "pid-file", 1, 0, 'p' }, { "timestamp", 1, 0, 't' }, { "overflow-data", 1, 0, 'o'}, { 0 }, }; bool is_interactive = false; int ch; int syslog_option = LOG_CONS; int syslog_mask = LOG_MASK(LOG_WARNING)|LOG_MASK(LOG_ERR)|LOG_MASK(LOG_CRIT)|\ LOG_MASK(LOG_ALERT)|LOG_MASK(LOG_EMERG); int opt_ind = 0; char *pidfile = NULL; while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) { switch (ch) { case 'h': usage(argv[0]); exit(0); case 'V': version(argv[0]); exit(0); case 'v': #ifndef __sun__ syslog_option |= LOG_PERROR; #endif syslog_mask |= LOG_MASK(LOG_NOTICE)|LOG_MASK(LOG_INFO)| \ LOG_MASK(LOG_DEBUG); break; case 'i': is_interactive = true; break; case 'l': if (!strcmp(optarg, "all")) { log_hv = 1; log_guest = 1; } else if (!strcmp(optarg, "hv")) { log_hv = 1; } else if (!strcmp(optarg, "guest")) { log_guest = 1; } break; case 'r': log_dir = strdup(optarg); break; case 'p': pidfile = strdup(optarg); break; case 't': if (!strcmp(optarg, "all")) { log_time_hv = 1; log_time_guest = 1; } else if (!strcmp(optarg, "hv")) { log_time_hv = 1; } else if (!strcmp(optarg, "guest")) { log_time_guest = 1; } else if (!strcmp(optarg, "none")) { log_time_guest = 0; log_time_hv = 0; } break; case 'o': if (!strcmp(optarg, "keep")) { discard_overflowed_data = 0; } else if (!strcmp(optarg, "discard")) { discard_overflowed_data = 1; } break; case '?': fprintf(stderr, "Try `%s --help' for more information\n", argv[0]); exit(EINVAL); } } if (!log_dir) { log_dir = strdup(XEN_LOG_DIR "/console"); } if (geteuid() != 0) { fprintf(stderr, "%s requires root to run.\n", argv[0]); exit(EPERM); } signal(SIGHUP, handle_hup); openlog("xenconsoled", syslog_option, LOG_DAEMON); setlogmask(syslog_mask); increase_fd_limit(); if (!is_interactive) { daemonize(pidfile ? pidfile : XEN_RUN_DIR "/xenconsoled.pid"); } if (!xen_setup()) exit(1); handle_io(); closelog(); free(log_dir); free(pidfile); return 0; } /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/console/Makefile0000664000175000017500000000255213256712137014723 0ustar smbsmbXEN_ROOT=$(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk CFLAGS += -Werror CFLAGS += $(CFLAGS_libxenctrl) CFLAGS += $(CFLAGS_libxenstore) LDLIBS += $(LDLIBS_libxenctrl) LDLIBS += $(LDLIBS_libxenstore) LDLIBS += $(SOCKET_LIBS) LDLIBS_xenconsoled += $(UTIL_LIBS) LDLIBS_xenconsoled += -lrt BIN = xenconsoled xenconsole .PHONY: all all: $(BIN) .PHONY: clean clean: $(RM) *.a *.so *.o *.rpm $(BIN) $(DEPS) $(RM) client/*.o daemon/*.o $(RM) client/_paths.h $(RM) daemon/_paths.h .PHONY: distclean distclean: clean daemon/main.o: daemon/_paths.h daemon/io.o: CFLAGS += $(CFLAGS_libxenevtchn) $(CFLAGS_libxengnttab) xenconsoled: $(patsubst %.c,%.o,$(wildcard daemon/*.c)) $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) $(LDLIBS_libxenevtchn) $(LDLIBS_libxengnttab) $(LDLIBS_xenconsoled) $(APPEND_LDFLAGS) client/main.o: client/_paths.h xenconsole: $(patsubst %.c,%.o,$(wildcard client/*.c)) $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) $(LDLIBS_xenconsole) $(APPEND_LDFLAGS) genpath-target = $(call buildmakevars2header,client/_paths.h) $(eval $(genpath-target)) genpath-target = $(call buildmakevars2header,daemon/_paths.h) $(eval $(genpath-target)) .PHONY: install install: $(BIN) $(INSTALL_DIR) $(DESTDIR)/$(sbindir) $(INSTALL_PROG) xenconsoled $(DESTDIR)/$(sbindir) $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN) $(INSTALL_PROG) xenconsole $(DESTDIR)$(LIBEXEC_BIN) -include $(DEPS) xen-4.9.2/tools/console/testsuite/0000775000175000017500000000000013256712137015310 5ustar smbsmbxen-4.9.2/tools/console/testsuite/procpipe.c0000664000175000017500000000543113256712137017300 0ustar smbsmb/* Written by Anthony Liguori */ #include #include #include #include #include #include #include #include #define PACKAGE_NAME "procpipe" #define PACKAGE_VERSION "0.0.1" #define GPL_SHORT \ "This is free software; see the source for copying conditions. There is NO\n"\ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." #define PACKAGE_BUGS "aliguori@us.ibm.com" #define PACKAGE_AUTHOR "Anthony Liguori" #define PACKAGE_OWNER "IBM, Corp." #define PACKAGE_LICENSE GPL_SHORT static void usage(const char *name) { printf("Usage: %s [OPTIONS]\n" "\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" "\n" "Report bugs to <%s>.\n" , name, PACKAGE_BUGS); } static void version(const char *name) { printf("%s (%s) %s\n" "Written by %s.\n" "\n" "Copyright (C) 2005 %s.\n" "%s\n" , name, PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_AUTHOR, PACKAGE_OWNER, PACKAGE_LICENSE); } static pid_t exec(int stdout, int stdin, const char *cmd) { pid_t pid; pid = fork(); if (pid == 0) { close(STDOUT_FILENO); dup2(stdout, STDOUT_FILENO); close(STDIN_FILENO); dup2(stdin, STDIN_FILENO); execlp("/bin/sh", "sh", "-c", cmd, NULL); } return pid; } int main(int argc, char **argv) { int ch, opt_ind = 0; const char *sopt = "hV"; struct option lopt[] = { { "help", 0, 0, 'h' }, { "version", 0, 0, 'V' }, { 0 } }; int host_stdout[2]; int host_stdin[2]; int res; pid_t pid1, pid2; int status; while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { case 'h': usage(argv[0]); exit(0); case 'V': version(argv[0]); exit(0); case '?': errx(EINVAL, "Try `%s --help' for more information.", argv[0]); } } if ((argc - optind) != 2) { errx(EINVAL, "Two commands are required.\n" "Try `%s --help' for more information.", argv[0]); } res = pipe(host_stdout); if (res == -1) { err(errno, "pipe() failed"); } res = pipe(host_stdin); if (res == -1) { err(errno, "pipe() failed"); } pid1 = exec(host_stdout[1], host_stdin[0], argv[optind]); if (pid1 == -1) { err(errno, "exec(%s)", argv[optind]); } pid2 = exec(host_stdin[1], host_stdout[0], argv[optind + 1]); if (pid2 == -1) { err(errno, "exec(%s)", argv[optind + 1]); } waitpid(pid1, &status, 0); if (WIFEXITED(status)) status = WEXITSTATUS(status); if (status != 0) { printf("Child exited with status %d\n", status); } waitpid(pid2, &status, 0); if (WIFEXITED(status)) status = WEXITSTATUS(status); if (status != 0) { printf("Child2 exited with status %d\n", status); } return 0; } xen-4.9.2/tools/console/testsuite/console-dom0.c0000664000175000017500000000436713256712137017765 0ustar smbsmb/* Written by Anthony Liguori */ #include #include #include #include #include #include #define MIN(a, b) (((a) < (b)) ? (a) : (b)) static void generate_random_buffer(char *buffer, size_t size) { int i; for (i = 0; i < size; i++) { buffer[i] = random() & 0xFF; } } static void canonicalize(char *buffer) { char *reader, *writer; reader = writer = buffer; while (*reader) { *writer = *reader; if (*reader != '\r') writer++; reader++; } *writer = *reader; } int main(int argc, char **argv) { char buffer[4096]; char *line; unsigned int seed; size_t size; int runs; unsigned long long total_bytes = 0; struct termios term; tcgetattr(STDIN_FILENO, &term); cfmakeraw(&term); tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); tcgetattr(STDOUT_FILENO, &term); cfmakeraw(&term); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &term); while ((line = fgets(buffer, sizeof(buffer), stdin))) { canonicalize(line); if (strcmp(line, "!!!XEN Test Begin!!!\n") == 0) { break; } else { fprintf(stderr, "%s", line); } } if (line == NULL) { fprintf(stderr, "Client never sent start string.\n"); return 1; } seed = time(0); printf("%u\n", seed); fflush(stdout); fprintf(stderr, "Waiting for seed acknowledgement\n"); line = fgets(buffer, sizeof(buffer), stdin); if (line == NULL) { fprintf(stderr, "Client never acknowledge seed.\n"); return 1; } canonicalize(line); if (strcmp(line, "Seed Okay.\n") != 0) { fprintf(stderr, "Incorrect seed acknowledgement.\n"); fprintf(stderr, "[%s]", line); return 1; } else { fprintf(stderr, "Processed seed.\n"); } srandom(seed); for (runs = (random() % 100000) + 4096; runs > 0; runs--) { size = random() % 4096; fprintf(stderr, "Writing %d bytes.\n", size); generate_random_buffer(buffer, size); fwrite(buffer, size, 1, stdout); fflush(stdout); do { line = fgets(buffer, sizeof(buffer), stdin); if (line == NULL) { fprintf(stderr, "Premature EOF from client.\n"); return 1; } canonicalize(line); fprintf(stderr, "%s", line); } while (strcmp(line, "Okay.\n") != 0); total_bytes += size; } fprintf(stderr, "PASS: processed %llu byte(s).\n", total_bytes); return 0; } xen-4.9.2/tools/console/testsuite/console-domU.c0000664000175000017500000000254513256712137020026 0ustar smbsmb/* Written by Anthony Liguori */ #include #include #include #include #include static void canonicalize(char *buffer) { char *reader, *writer; reader = writer = buffer; while (*reader) { *writer = *reader; if (*reader != '\r') writer++; reader++; } *writer = *reader; } int main(int argc, char **argv) { char buffer[4096]; char *line; unsigned int seed; size_t size; int i; int runs; struct termios term; tcgetattr(STDIN_FILENO, &term); cfmakeraw(&term); tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); tcgetattr(STDOUT_FILENO, &term); cfmakeraw(&term); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &term); printf("!!!XEN Test Begin!!!\n"); fflush(stdout); line = fgets(buffer, sizeof(buffer), stdin); if (line == NULL) { printf("Failure\n"); fflush(stdout); return 1; } canonicalize(line); seed = strtoul(line, 0, 0); printf("Seed Okay.\n"); fflush(stdout); srandom(seed); for (runs = (random() % 100000) + 4096; runs > 0; runs--) { size = random() % 4096; for (i = 0; i < size; i++) { int ch; int exp; ch = fgetc(stdin); exp = random() & 0xFF; if (ch != exp) { printf("Expected %d got %d\n", exp, ch); fflush(stdout); } printf("Got %d/%d good bytes\n", i, size); } printf("Okay.\n"); fflush(stdout); } return 0; } xen-4.9.2/tools/console/testsuite/Makefile0000664000175000017500000000047613256712137016757 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk LDFLAGS=-static .PHONY: all all: console-dom0 console-domU procpipe console-dom0: console-dom0.o console-domU: console-domU.o procpipe: procpipe.o .PHONY: clean clean: $(RM) *.o console-domU console-dom0 procpipe .PHONY: distclean distclean: clean xen-4.9.2/tools/console/testsuite/README0000664000175000017500000000230313256712137016166 0ustar smbsmbABOUT This tool uses two programs, one that lives in dom0 and one that lives in domU to verify that no data is lost. dom0 and domU share a handshake with each other that they use to exchange a random seed. Both programs then generate a series of random numbers and then writes and reads the numbers via the console. Because each side starts with the same seed they know what data the other side is generating and therefore what should be expected. RUNNNING console-domU should be installed within the guest image. It must be launched from the client automatically. I use a custom initrd image and put it in the /linuxrc. console-dom0 and console-domU will communicate with each other and stress the console code. You can verify it at various levels by invoking it in different ways. procpipe is used to connect the two. I use the following command for testing: ./procpipe ./console-dom0 'xm create -c /etc/xen/xmexample1' xmexample1 has no devices and no root set (this is what triggers /linuxrc). If it freezes, it probably means that console-domU is expecting more data from console-dom0 (which means that some data got dropped). I'd like to add timeouts in the future to handle this more gracefully. xen-4.9.2/tools/xenstore/0000775000175000017500000000000013256712137013464 5ustar smbsmbxen-4.9.2/tools/xenstore/tdb.h0000664000175000017500000001026713256712137014414 0ustar smbsmb#ifndef __TDB_H__ #define __TDB_H__ /* Unix SMB/CIFS implementation. trivial database library Copyright (C) Andrew Tridgell 1999-2004 ** NOTE! The following LGPL license applies to the tdb ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . */ #ifdef __cplusplus extern "C" { #endif /* flags to tdb_store() */ #define TDB_REPLACE 1 #define TDB_INSERT 2 #define TDB_MODIFY 3 /* flags for tdb_open() */ #define TDB_DEFAULT 0 /* just a readability place holder */ #define TDB_CLEAR_IF_FIRST 1 #define TDB_INTERNAL 2 /* don't store on disk */ #define TDB_NOLOCK 4 /* don't do any locking */ #define TDB_NOMMAP 8 /* don't use mmap */ #define TDB_CONVERT 16 /* convert endian (internal use) */ #define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */ #define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret) /* error codes */ enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT, TDB_ERR_NOEXIST}; #ifndef uint32_t #define uint32_t unsigned #endif typedef struct TDB_DATA { char *dptr; size_t dsize; } TDB_DATA; typedef uint32_t tdb_len; typedef uint32_t tdb_off; /* this is stored at the front of every database */ struct tdb_header { char magic_food[32]; /* for /etc/magic */ uint32_t version; /* version of the code */ uint32_t hash_size; /* number of hash entries */ tdb_off rwlocks; tdb_off reserved[31]; }; struct tdb_lock_type { uint32_t count; uint32_t ltype; }; struct tdb_traverse_lock { struct tdb_traverse_lock *next; uint32_t off; uint32_t hash; }; #ifndef PRINTF_ATTRIBUTE #define PRINTF_ATTRIBUTE(a,b) #endif /* this is the context structure that is returned from a db open */ typedef struct tdb_context { char *name; /* the name of the database */ void *map_ptr; /* where it is currently mapped */ int fd; /* open file descriptor for the database */ tdb_len map_size; /* how much space has been mapped */ int read_only; /* opened read-only */ struct tdb_lock_type *locked; /* array of chain locks */ enum TDB_ERROR ecode; /* error code for last tdb error */ struct tdb_header header; /* a cached copy of the header */ uint32_t flags; /* the flags passed to tdb_open */ struct tdb_traverse_lock travlocks; /* current traversal locks */ struct tdb_context *next; /* all tdbs to avoid multiple opens */ dev_t device; /* uniquely identifies this tdb */ ino_t inode; /* uniquely identifies this tdb */ void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...) PRINTF_ATTRIBUTE(3,4); /* logging function */ uint32_t (*hash_fn)(TDB_DATA *key); int open_flags; /* flags used in the open - needed by reopen */ } TDB_CONTEXT; typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *); typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...); typedef uint32_t (*tdb_hash_func)(TDB_DATA *key); TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, tdb_log_func log_fn, tdb_hash_func hash_fn); enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb); const char *tdb_errorstr(TDB_CONTEXT *tdb); TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key); int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key); int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag); int tdb_close(TDB_CONTEXT *tdb); TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb); TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key); int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *); #ifdef __cplusplus } #endif #endif /* tdb.h */ xen-4.9.2/tools/xenstore/xenstored_domain.h0000664000175000017500000000622113256712137017200 0ustar smbsmb/* Domain communications for Xen Store Daemon. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef _XENSTORED_DOMAIN_H #define _XENSTORED_DOMAIN_H void handle_event(void); /* domid, mfn, eventchn, path */ int do_introduce(struct connection *conn, struct buffered_data *in); /* domid */ int do_is_domain_introduced(struct connection *conn, struct buffered_data *in); /* domid */ int do_release(struct connection *conn, struct buffered_data *in); /* domid */ int do_resume(struct connection *conn, struct buffered_data *in); /* domid, target */ int do_set_target(struct connection *conn, struct buffered_data *in); /* domid */ int do_get_domain_path(struct connection *conn, struct buffered_data *in); /* Allow guest to reset all watches */ int do_reset_watches(struct connection *conn, struct buffered_data *in); void domain_init(void); /* Returns the implicit path of a connection (only domains have this) */ const char *get_implicit_path(const struct connection *conn); /* Read existing connection information from store. */ void restore_existing_connections(void); /* Can connection attached to domain read/write. */ bool domain_can_read(struct connection *conn); bool domain_can_write(struct connection *conn); bool domain_is_unprivileged(struct connection *conn); /* Quota manipulation */ void domain_entry_inc(struct connection *conn, struct node *); void domain_entry_dec(struct connection *conn, struct node *); int domain_entry_fix(unsigned int domid, int num, bool update); int domain_entry(struct connection *conn); void domain_watch_inc(struct connection *conn); void domain_watch_dec(struct connection *conn); int domain_watch(struct connection *conn); /* Write rate limiting */ #define WRL_FACTOR 1000 /* for fixed-point arithmetic */ #define WRL_RATE 200 #define WRL_DBURST 10 #define WRL_GBURST 1000 #define WRL_NEWDOMS 5 #define WRL_LOGEVERY 120 /* seconds */ struct wrl_timestampt { time_t sec; int msec; }; extern long wrl_ntransactions; void wrl_gettime_now(struct wrl_timestampt *now_ts); void wrl_domain_new(struct domain *domain); void wrl_domain_destroy(struct domain *domain); void wrl_credit_update(struct domain *domain, struct wrl_timestampt now); void wrl_check_timeout(struct domain *domain, struct wrl_timestampt now, int *ptimeout); void wrl_log_periodic(struct wrl_timestampt now); void wrl_apply_debit_direct(struct connection *conn); void wrl_apply_debit_trans_commit(struct connection *conn); #endif /* _XENSTORED_DOMAIN_H */ xen-4.9.2/tools/xenstore/hashtable.h0000664000175000017500000001650213256712137015574 0ustar smbsmb/* Copyright (C) 2002 Christopher Clark */ /* * There are duplicates of this code in: * - tools/blktap2/drivers/hashtable.h */ #ifndef __HASHTABLE_CWC22_H__ #define __HASHTABLE_CWC22_H__ struct hashtable; /* Example of use: * * struct hashtable *h; * struct some_key *k; * struct some_value *v; * * static unsigned int hash_from_key_fn( void *k ); * static int keys_equal_fn ( void *key1, void *key2 ); * * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); * k = (struct some_key *) malloc(sizeof(struct some_key)); * v = (struct some_value *) malloc(sizeof(struct some_value)); * * (initialise k and v to suitable values) * * if (! hashtable_insert(h,k,v) ) * { exit(-1); } * * if (NULL == (found = hashtable_search(h,k) )) * { printf("not found!"); } * * if (NULL == (found = hashtable_remove(h,k) )) * { printf("Not found\n"); } * */ /* Macros may be used to define type-safe(r) hashtable access functions, with * methods specialized to take known key and value types as parameters. * * Example: * * Insert this at the start of your file: * * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); * * This defines the functions 'insert_some', 'search_some' and 'remove_some'. * These operate just like hashtable_insert etc., with the same parameters, * but their function signatures have 'struct some_key *' rather than * 'void *', and hence can generate compile time errors if your program is * supplying incorrect data as a key (and similarly for value). * * Note that the hash and key equality functions passed to create_hashtable * still take 'void *' parameters instead of 'some key *'. This shouldn't be * a difficult issue as they're only defined and passed once, and the other * functions will ensure that only valid keys are supplied to them. * * The cost for this checking is increased code size and runtime overhead * - if performance is important, it may be worth switching back to the * unsafe methods once your program has been debugged with the safe methods. * This just requires switching to some simple alternative defines - eg: * #define insert_some hashtable_insert * */ /***************************************************************************** * create_hashtable * @name create_hashtable * @param minsize minimum initial size of hashtable * @param hashfunction function for hashing keys * @param key_eq_fn function for determining key equality * @return newly created hashtable or NULL on failure */ struct hashtable * create_hashtable(unsigned int minsize, unsigned int (*hashfunction) (void*), int (*key_eq_fn) (void*,void*)); /***************************************************************************** * hashtable_insert * @name hashtable_insert * @param h the hashtable to insert into * @param k the key - hashtable claims ownership and will free on removal * @param v the value - does not claim ownership * @return non-zero for successful insertion * * This function will cause the table to expand if the insertion would take * the ratio of entries to table size over the maximum load factor. * * This function does not check for repeated insertions with a duplicate key. * The value returned when using a duplicate key is undefined -- when * the hashtable changes size, the order of retrieval of duplicate key * entries is reversed. * If in doubt, remove before insert. */ int hashtable_insert(struct hashtable *h, void *k, void *v); #define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ int fnname (struct hashtable *h, keytype *k, valuetype *v) \ { \ return hashtable_insert(h,k,v); \ } /***************************************************************************** * hashtable_search * @name hashtable_search * @param h the hashtable to search * @param k the key to search for - does not claim ownership * @return the value associated with the key, or NULL if none found */ void * hashtable_search(struct hashtable *h, void *k); #define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ valuetype * fnname (struct hashtable *h, keytype *k) \ { \ return (valuetype *) (hashtable_search(h,k)); \ } /***************************************************************************** * hashtable_remove * @name hashtable_remove * @param h the hashtable to remove the item from * @param k the key to search for - does not claim ownership * @return the value associated with the key, or NULL if none found */ void * /* returns value */ hashtable_remove(struct hashtable *h, void *k); #define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ valuetype * fnname (struct hashtable *h, keytype *k) \ { \ return (valuetype *) (hashtable_remove(h,k)); \ } /***************************************************************************** * hashtable_count * @name hashtable_count * @param h the hashtable * @return the number of items stored in the hashtable */ unsigned int hashtable_count(struct hashtable *h); /***************************************************************************** * hashtable_destroy * @name hashtable_destroy * @param h the hashtable * @param free_values whether to call 'free' on the remaining values */ void hashtable_destroy(struct hashtable *h, int free_values); #endif /* __HASHTABLE_CWC22_H__ */ /* * Copyright (c) 2002, Christopher Clark * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ xen-4.9.2/tools/xenstore/hashtable.c0000664000175000017500000002162713256712137015573 0ustar smbsmb/* Copyright (C) 2004 Christopher Clark */ /* * There are duplicates of this code in: * - tools/blktap2/drivers/hashtable.c */ #include "hashtable.h" #include "hashtable_private.h" #include #include #include #include #include /* Credit for primes table: Aaron Krowne http://br.endernet.org/~akrowne/ http://planetmath.org/encyclopedia/GoodHashTablePrimes.html */ static const unsigned int primes[] = { 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 }; const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]); const unsigned int max_load_factor = 65; /* percentage */ /*****************************************************************************/ struct hashtable * create_hashtable(unsigned int minsize, unsigned int (*hashf) (void*), int (*eqf) (void*,void*)) { struct hashtable *h; unsigned int pindex, size = primes[0]; /* Check requested hashtable isn't too large */ if (minsize > (1u << 30)) return NULL; /* Enforce size as prime */ for (pindex=0; pindex < prime_table_length; pindex++) { if (primes[pindex] > minsize) { size = primes[pindex]; break; } } h = (struct hashtable *)calloc(1, sizeof(struct hashtable)); if (NULL == h) goto err0; h->table = (struct entry **)calloc(size, sizeof(struct entry *)); if (NULL == h->table) goto err1; h->tablelength = size; h->primeindex = pindex; h->entrycount = 0; h->hashfn = hashf; h->eqfn = eqf; h->loadlimit = (unsigned int)(((uint64_t)size * max_load_factor) / 100); return h; err1: free(h); err0: return NULL; } /*****************************************************************************/ unsigned int hash(struct hashtable *h, void *k) { /* Aim to protect against poor hash functions by adding logic here * - logic taken from java 1.4 hashtable source */ unsigned int i = h->hashfn(k); i += ~(i << 9); i ^= ((i >> 14) | (i << 18)); /* >>> */ i += (i << 4); i ^= ((i >> 10) | (i << 22)); /* >>> */ return i; } /*****************************************************************************/ static int hashtable_expand(struct hashtable *h) { /* Double the size of the table to accomodate more entries */ struct entry **newtable; struct entry *e; struct entry **pE; unsigned int newsize, i, index; /* Check we're not hitting max capacity */ if (h->primeindex == (prime_table_length - 1)) return 0; newsize = primes[++(h->primeindex)]; newtable = (struct entry **)calloc(newsize, sizeof(struct entry*)); if (NULL != newtable) { /* This algorithm is not 'stable'. ie. it reverses the list * when it transfers entries between the tables */ for (i = 0; i < h->tablelength; i++) { while (NULL != (e = h->table[i])) { h->table[i] = e->next; index = indexFor(newsize,e->h); e->next = newtable[index]; newtable[index] = e; } } free(h->table); h->table = newtable; } /* Plan B: realloc instead */ else { newtable = (struct entry **) realloc(h->table, newsize * sizeof(struct entry *)); if (NULL == newtable) { (h->primeindex)--; return 0; } h->table = newtable; memset(newtable[h->tablelength], 0, newsize - h->tablelength); for (i = 0; i < h->tablelength; i++) { for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { index = indexFor(newsize,e->h); if (index == i) { pE = &(e->next); } else { *pE = e->next; e->next = newtable[index]; newtable[index] = e; } } } } h->tablelength = newsize; h->loadlimit = (unsigned int) (((uint64_t)newsize * max_load_factor) / 100); return -1; } /*****************************************************************************/ unsigned int hashtable_count(struct hashtable *h) { return h->entrycount; } /*****************************************************************************/ int hashtable_insert(struct hashtable *h, void *k, void *v) { /* This method allows duplicate keys - but they shouldn't be used */ unsigned int index; struct entry *e; if (++(h->entrycount) > h->loadlimit) { /* Ignore the return value. If expand fails, we should * still try cramming just this value into the existing table * -- we may not have memory for a larger table, but one more * element may be ok. Next time we insert, we'll try expanding again.*/ hashtable_expand(h); } e = (struct entry *)calloc(1, sizeof(struct entry)); if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ e->h = hash(h,k); index = indexFor(h->tablelength,e->h); e->k = k; e->v = v; e->next = h->table[index]; h->table[index] = e; return -1; } /*****************************************************************************/ void * /* returns value associated with key */ hashtable_search(struct hashtable *h, void *k) { struct entry *e; unsigned int hashvalue, index; hashvalue = hash(h,k); index = indexFor(h->tablelength,hashvalue); e = h->table[index]; while (NULL != e) { /* Check hash value to short circuit heavier comparison */ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; e = e->next; } return NULL; } /*****************************************************************************/ void * /* returns value associated with key */ hashtable_remove(struct hashtable *h, void *k) { /* TODO: consider compacting the table when the load factor drops enough, * or provide a 'compact' method. */ struct entry *e; struct entry **pE; void *v; unsigned int hashvalue, index; hashvalue = hash(h,k); index = indexFor(h->tablelength,hash(h,k)); pE = &(h->table[index]); e = *pE; while (NULL != e) { /* Check hash value to short circuit heavier comparison */ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { *pE = e->next; h->entrycount--; v = e->v; freekey(e->k); free(e); return v; } pE = &(e->next); e = e->next; } return NULL; } /*****************************************************************************/ /* destroy */ void hashtable_destroy(struct hashtable *h, int free_values) { unsigned int i; struct entry *e, *f; struct entry **table = h->table; if (free_values) { for (i = 0; i < h->tablelength; i++) { e = table[i]; while (NULL != e) { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } } } else { for (i = 0; i < h->tablelength; i++) { e = table[i]; while (NULL != e) { f = e; e = e->next; freekey(f->k); free(f); } } } free(h->table); free(h); } /* * Copyright (c) 2002, Christopher Clark * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ xen-4.9.2/tools/xenstore/xenstored_watch.c0000664000175000017500000001413413256712137017034 0ustar smbsmb/* Watch code for Xen Store Daemon. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include #include #include "talloc.h" #include "list.h" #include "xenstored_watch.h" #include "xenstore_lib.h" #include "utils.h" #include "xenstored_domain.h" extern int quota_nb_watch_per_domain; struct watch { /* Watches on this connection */ struct list_head list; /* Current outstanding events applying to this watch. */ struct list_head events; /* Is this relative to connnection's implicit path? */ const char *relative_path; char *token; char *node; }; static bool check_event_node(const char *node) { if (!node || !strstarts(node, "@")) { errno = EINVAL; return false; } return true; } /* Is child a subnode of parent, or equal? */ static bool is_child(const char *child, const char *parent) { unsigned int len = strlen(parent); /* * / should really be "" for this algorithm to work, but that's a * usability nightmare. */ if (streq(parent, "/")) return true; if (strncmp(child, parent, len) != 0) return false; return child[len] == '/' || child[len] == '\0'; } /* * Send a watch event. * Temporary memory allocations are done with ctx. */ static void add_event(struct connection *conn, void *ctx, struct watch *watch, const char *name) { /* Data to send (node\0token\0). */ unsigned int len; char *data; if (!check_event_node(name)) { /* Can this conn load node, or see that it doesn't exist? */ struct node *node = get_node(conn, ctx, name, XS_PERM_READ); /* * XXX We allow EACCES here because otherwise a non-dom0 * backend driver cannot watch for disappearance of a frontend * xenstore directory. When the directory disappears, we * revert to permissions of the parent directory for that path, * which will typically disallow access for the backend. * But this breaks device-channel teardown! * Really we should fix this better... */ if (!node && errno != ENOENT && errno != EACCES) return; } if (watch->relative_path) { name += strlen(watch->relative_path); if (*name == '/') /* Could be "" */ name++; } len = strlen(name) + 1 + strlen(watch->token) + 1; data = talloc_array(ctx, char, len); if (!data) return; strcpy(data, name); strcpy(data + strlen(name) + 1, watch->token); send_reply(conn, XS_WATCH_EVENT, data, len); talloc_free(data); } /* * Check whether any watch events are to be sent. * Temporary memory allocations are done with ctx. */ void fire_watches(struct connection *conn, void *ctx, const char *name, bool recurse) { struct connection *i; struct watch *watch; /* During transactions, don't fire watches. */ if (conn && conn->transaction) return; /* Create an event for each watch. */ list_for_each_entry(i, &connections, list) { list_for_each_entry(watch, &i->watches, list) { if (is_child(name, watch->node)) add_event(i, ctx, watch, name); else if (recurse && is_child(watch->node, name)) add_event(i, ctx, watch, watch->node); } } } static int destroy_watch(void *_watch) { trace_destroy(_watch, "watch"); return 0; } int do_watch(struct connection *conn, struct buffered_data *in) { struct watch *watch; char *vec[2]; bool relative; if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) return EINVAL; if (strstarts(vec[0], "@")) { relative = false; if (strlen(vec[0]) > XENSTORE_REL_PATH_MAX) return EINVAL; /* check if valid event */ } else { relative = !strstarts(vec[0], "/"); vec[0] = canonicalize(conn, in, vec[0]); if (!vec[0]) return ENOMEM; if (!is_valid_nodename(vec[0])) return EINVAL; } /* Check for duplicates. */ list_for_each_entry(watch, &conn->watches, list) { if (streq(watch->node, vec[0]) && streq(watch->token, vec[1])) return EEXIST; } if (domain_watch(conn) > quota_nb_watch_per_domain) return E2BIG; watch = talloc(conn, struct watch); if (!watch) return ENOMEM; watch->node = talloc_strdup(watch, vec[0]); watch->token = talloc_strdup(watch, vec[1]); if (!watch->node || !watch->token) { talloc_free(watch); return ENOMEM; } if (relative) watch->relative_path = get_implicit_path(conn); else watch->relative_path = NULL; INIT_LIST_HEAD(&watch->events); domain_watch_inc(conn); list_add_tail(&watch->list, &conn->watches); trace_create(watch, "watch"); talloc_set_destructor(watch, destroy_watch); send_ack(conn, XS_WATCH); /* We fire once up front: simplifies clients and restart. */ add_event(conn, in, watch, watch->node); return 0; } int do_unwatch(struct connection *conn, struct buffered_data *in) { struct watch *watch; char *node, *vec[2]; if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) return EINVAL; node = canonicalize(conn, in, vec[0]); if (!node) return ENOMEM; list_for_each_entry(watch, &conn->watches, list) { if (streq(watch->node, node) && streq(watch->token, vec[1])) { list_del(&watch->list); talloc_free(watch); domain_watch_dec(conn); send_ack(conn, XS_UNWATCH); return 0; } } return ENOENT; } void conn_delete_all_watches(struct connection *conn) { struct watch *watch; while ((watch = list_top(&conn->watches, struct watch, list))) { list_del(&watch->list); talloc_free(watch); domain_watch_dec(conn); } } /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/xenstore/xenstored_core.h0000664000175000017500000001325213256712137016663 0ustar smbsmb/* Internal interfaces for Xen Store Daemon. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef _XENSTORED_CORE_H #define _XENSTORED_CORE_H #define XC_WANT_COMPAT_MAP_FOREIGN_API #include #include #include #include #include #include #include #include "xenstore_lib.h" #include "list.h" #include "tdb.h" #include "hashtable.h" /* DEFAULT_BUFFER_SIZE should be large enough for each errno string. */ #define DEFAULT_BUFFER_SIZE 16 #define MIN(a, b) (((a) < (b))? (a) : (b)) typedef int32_t wrl_creditt; #define WRL_CREDIT_MAX (1000*1000*1000) /* ^ satisfies non-overflow condition for wrl_xfer_credit */ struct buffered_data { struct list_head list; /* Are we still doing the header? */ bool inhdr; /* How far are we? */ unsigned int used; union { struct xsd_sockmsg msg; char raw[sizeof(struct xsd_sockmsg)]; } hdr; /* The actual data. */ char *buffer; char default_buffer[DEFAULT_BUFFER_SIZE]; }; struct connection; typedef int connwritefn_t(struct connection *, const void *, unsigned int); typedef int connreadfn_t(struct connection *, void *, unsigned int); struct connection { struct list_head list; /* The file descriptor we came in on. */ int fd; /* The index of pollfd in global pollfd array */ int pollfd_idx; /* Who am I? 0 for socket connections. */ unsigned int id; /* Is this a read-only connection? */ bool can_write; /* Buffered incoming data. */ struct buffered_data *in; /* Buffered output data */ struct list_head out_list; /* Transaction context for current request (NULL if none). */ struct transaction *transaction; /* List of in-progress transactions. */ struct list_head transaction_list; uint32_t next_transaction_id; unsigned int transaction_started; /* The domain I'm associated with, if any. */ struct domain *domain; /* The target of the domain I'm associated with. */ struct connection *target; /* My watches. */ struct list_head watches; /* Methods for communicating over this connection: write can be NULL */ connwritefn_t *write; connreadfn_t *read; }; extern struct list_head connections; struct node { const char *name; /* Parent (optional) */ struct node *parent; /* Generation count. */ uint64_t generation; #define NO_GENERATION ~((uint64_t)0) /* Permissions. */ unsigned int num_perms; struct xs_permissions *perms; /* Contents. */ unsigned int datalen; void *data; /* Children, each nul-terminated. */ unsigned int childlen; char *children; }; /* Return the only argument in the input. */ const char *onearg(struct buffered_data *in); /* Break input into vectors, return the number, fill in up to num of them. */ unsigned int get_strings(struct buffered_data *data, char *vec[], unsigned int num); void send_reply(struct connection *conn, enum xsd_sockmsg_type type, const void *data, unsigned int len); /* Some routines (write, mkdir, etc) just need a non-error return */ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); /* Canonicalize this path if possible. */ char *canonicalize(struct connection *conn, const void *ctx, const char *node); /* Write a node to the tdb data base. */ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node); /* Get this node, checking we have permissions. */ struct node *get_node(struct connection *conn, const void *ctx, const char *name, enum xs_perm_type perm); struct connection *new_connection(connwritefn_t *write, connreadfn_t *read); void check_store(void); void corrupt(struct connection *conn, const char *fmt, ...); /* Is this a valid node name? */ bool is_valid_nodename(const char *node); /* Tracing infrastructure. */ void trace_create(const void *data, const char *type); void trace_destroy(const void *data, const char *type); void trace(const char *fmt, ...); void dtrace_io(const struct connection *conn, const struct buffered_data *data, int out); void reopen_log(void); void close_log(void); extern char *tracefile; extern int tracefd; extern TDB_CONTEXT *tdb_ctx; extern int dom0_domid; extern int dom0_event; extern int priv_domid; extern int quota_nb_entry_per_domain; /* Map the kernel's xenstore page. */ void *xenbus_map(void); void unmap_xenbus(void *interface); static inline int xenbus_master_domid(void) { return dom0_domid; } /* Return the event channel used by xenbus. */ evtchn_port_t xenbus_evtchn(void); /* Tell the kernel xenstored is running. */ void xenbus_notify_running(void); /* Write out the pidfile */ void write_pidfile(const char *pidfile); /* Fork but do not close terminal FDs */ void daemonize(void); /* Close stdin/stdout/stderr to complete daemonize */ void finish_daemonize(void); /* Open a pipe for signal handling */ void init_pipe(int reopen_log_pipe[2]); xengnttab_handle **xgt_handle; int remember_string(struct hashtable *hash, const char *str); #endif /* _XENSTORED_CORE_H */ /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/xenstore/xenstore_client.c0000664000175000017500000004101613256712137017037 0ustar smbsmb/* * This file is subject to the terms and conditions of the GNU General * Public License. See the file "COPYING" in the main directory of * this archive for more details. * * Copyright (C) 2005 by Christian Limpach * Copyright (C) 2005 XenSource Ltd. * */ #include #include #include #include #include #include #include #include #include #include #include #include #define PATH_SEP '/' #define MAX_PATH_LEN 256 enum mode { MODE_unknown, MODE_chmod, MODE_exists, MODE_list, MODE_ls, MODE_read, MODE_rm, MODE_write, MODE_watch, }; static char *output_buf = NULL; static int output_pos = 0; static struct expanding_buffer ebuf; static int output_size = 0; static void output(const char *fmt, ...) { va_list ap; int len; char buf[1]; va_start(ap, fmt); len = vsnprintf(buf, 1, fmt, ap); if (len < 0) err(1, "output"); va_end(ap); if (len + 1 + output_pos > output_size) { output_size += len + 1024; output_buf = realloc(output_buf, output_size); if (output_buf == NULL) err(1, "malloc"); } va_start(ap, fmt); if (vsnprintf(&output_buf[output_pos], len + 1, fmt, ap) != len) err(1, "output"); va_end(ap); output_pos += len; } static void usage(enum mode mode, int incl_mode, const char *progname) { const char *mstr = NULL; switch (mode) { case MODE_unknown: errx(1, "Usage: %s [-h] [...]", progname); case MODE_read: mstr = incl_mode ? "read " : ""; errx(1, "Usage: %s %s[-h] [-p] [-s] key [...]", progname, mstr); case MODE_write: mstr = incl_mode ? "write " : ""; errx(1, "Usage: %s %s[-h] [-s] key value [...]", progname, mstr); case MODE_rm: mstr = incl_mode ? "rm " : ""; errx(1, "Usage: %s %s[-h] [-s] [-t] key [...]", progname, mstr); case MODE_exists: mstr = incl_mode ? "exists " : ""; /* fallthrough */ case MODE_list: mstr = mstr ? : incl_mode ? "list " : ""; errx(1, "Usage: %s %s[-h] [-p] [-s] key [...]", progname, mstr); case MODE_ls: mstr = mstr ? : incl_mode ? "ls " : ""; errx(1, "Usage: %s %s[-h] [-f] [-p] [-s] [path]", progname, mstr); case MODE_chmod: mstr = incl_mode ? "chmod " : ""; errx(1, "Usage: %s %s[-h] [-u] [-r] [-s] key ", progname, mstr); case MODE_watch: mstr = incl_mode ? "watch " : ""; errx(1, "Usage: %s %s[-h] [-n NR] key", progname, mstr); } } static int do_rm(char *path, struct xs_handle *xsh, xs_transaction_t xth) { if (xs_rm(xsh, xth, path)) { return 0; } else { warnx("could not remove path %s", path); return 1; } } #define STRING_MAX XENSTORE_ABS_PATH_MAX+1024 static int max_width = 80; static int desired_width = 60; static int show_whole_path = 0; #define TAG " = \"...\"" #define TAG_LEN strlen(TAG) #define MIN(a, b) (((a) < (b))? (a) : (b)) static void do_ls(struct xs_handle *h, char *path, int cur_depth, int show_perms) { char **e; char *newpath, *val; int newpath_len; int i; unsigned int num, len; newpath = malloc(STRING_MAX); if (!newpath) err(1, "malloc in do_ls"); e = xs_directory(h, XBT_NULL, path, &num); if (e == NULL) err(1, "xs_directory (%s)", path); for (i = 0; i 1; break; case MODE_write: transaction = (argc - switch_argv - optind) > 2; break; case MODE_ls: case MODE_watch: transaction = 0; break; default: transaction = 1; break; } if ( mode == MODE_ls ) { memset(&ws, 0, sizeof(ws)); ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); if (!ret) max_width = ws.ws_col - 2; } xsh = xs_open(socket ? XS_OPEN_SOCKETONLY : 0); if (xsh == NULL) err(1, "xs_open"); again: if (transaction) { xth = xs_transaction_start(xsh); if (xth == XBT_NULL) errx(1, "couldn't start transaction"); } ret = perform(mode, optind, argc - switch_argv, argv + switch_argv, xsh, xth, prefix, tidy, upto, recurse, nr_watches); if (transaction && !xs_transaction_end(xsh, xth, ret)) { if (ret == 0 && errno == EAGAIN) { output_pos = 0; goto again; } errx(1, "couldn't end transaction"); } if (output_pos) printf("%s", output_buf); free(output_buf); free(ebuf.buf); if (xsh) xs_close(xsh); return ret; } xen-4.9.2/tools/xenstore/xs_tdb_dump.c0000664000175000017500000000402013256712137016134 0ustar smbsmb/* Simple program to dump out all records of TDB */ #include #include #include #include #include #include #include #include "xenstore_lib.h" #include "tdb.h" #include "talloc.h" #include "utils.h" static uint32_t total_size(struct xs_tdb_record_hdr *hdr) { return sizeof(*hdr) + hdr->num_perms * sizeof(struct xs_permissions) + hdr->datalen + hdr->childlen; } static char perm_to_char(enum xs_perm_type perm) { return perm == XS_PERM_READ ? 'r' : perm == XS_PERM_WRITE ? 'w' : perm == XS_PERM_NONE ? '-' : perm == (XS_PERM_READ|XS_PERM_WRITE) ? 'b' : '?'; } static void tdb_logger(TDB_CONTEXT *tdb, int level, const char * fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } int main(int argc, char *argv[]) { TDB_DATA key; TDB_CONTEXT *tdb; if (argc != 2) barf("Usage: xs_tdb_dump "); tdb = tdb_open_ex(talloc_strdup(NULL, argv[1]), 0, 0, O_RDONLY, 0, &tdb_logger, NULL); if (!tdb) barf_perror("Could not open %s", argv[1]); key = tdb_firstkey(tdb); while (key.dptr) { TDB_DATA data; struct xs_tdb_record_hdr *hdr; data = tdb_fetch(tdb, key); hdr = (void *)data.dptr; if (data.dsize < sizeof(*hdr)) fprintf(stderr, "%.*s: BAD truncated\n", (int)key.dsize, key.dptr); else if (data.dsize != total_size(hdr)) fprintf(stderr, "%.*s: BAD length %i for %i/%i/%i (%i)\n", (int)key.dsize, key.dptr, (int)data.dsize, hdr->num_perms, hdr->datalen, hdr->childlen, total_size(hdr)); else { unsigned int i; char *p; printf("%.*s: ", (int)key.dsize, key.dptr); for (i = 0; i < hdr->num_perms; i++) printf("%s%c%i", i == 0 ? "" : ",", perm_to_char(hdr->perms[i].perms), hdr->perms[i].id); p = (void *)&hdr->perms[hdr->num_perms]; printf(" %.*s\n", hdr->datalen, p); p += hdr->datalen; for (i = 0; i < hdr->childlen; i += strlen(p+i)+1) printf("\t-> %s\n", p+i); } key = tdb_nextkey(tdb, key); } return 0; } xen-4.9.2/tools/xenstore/COPYING0000664000175000017500000006416113256712137014527 0ustar smbsmbThis license (LGPL) applies to the xenstore library which interfaces with the xenstore daemon (as stated in xs.c, xenstore.h, xs_lib.c and xenstore_lib.h). The remaining files in the directory are licensed as stated in the comments (as of this writing, GPL, see ../../COPYING). GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! xen-4.9.2/tools/xenstore/utils.h0000664000175000017500000000227213256712137015000 0ustar smbsmb#ifndef _UTILS_H #define _UTILS_H #include #include #include /* Is A == B ? */ #define streq(a,b) (strcmp((a),(b)) == 0) /* Does A start with B ? */ #define strstarts(a,b) (strncmp((a),(b),strlen(b)) == 0) /* Does A end in B ? */ static inline bool strends(const char *a, const char *b) { if (strlen(a) < strlen(b)) return false; return streq(a + strlen(a) - strlen(b), b); } #ifndef ARRAY_SIZE #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif void barf(const char *fmt, ...) __attribute__((noreturn)); void barf_perror(const char *fmt, ...) __attribute__((noreturn)); void (*xprintf)(const char *fmt, ...); #define eprintf(_fmt, _args...) xprintf("[ERR] %s" _fmt, __FUNCTION__, ##_args) /* * Mux errno values onto returned pointers. */ static inline void *ERR_PTR(long error) { return (void *)error; } static inline long PTR_ERR(const void *ptr) { return (long)ptr; } static inline long IS_ERR(const void *ptr) { return ((unsigned long)ptr > (unsigned long)-1000L); } #endif /* _UTILS_H */ /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/xenstore/utils.c0000664000175000017500000000172713256712137014777 0ustar smbsmb#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "utils.h" static void default_xprintf(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fflush(stderr); } void (*xprintf)(const char *fmt, ...) = default_xprintf; void barf(const char *fmt, ...) { char *str; int bytes; va_list arglist; xprintf("FATAL: "); va_start(arglist, fmt); bytes = vasprintf(&str, fmt, arglist); va_end(arglist); if (bytes >= 0) { xprintf("%s\n", str); free(str); } exit(1); } void barf_perror(const char *fmt, ...) { char *str; int bytes, err = errno; va_list arglist; xprintf("FATAL: "); va_start(arglist, fmt); bytes = vasprintf(&str, fmt, arglist); va_end(arglist); if (bytes >= 0) { xprintf("%s: %s\n", str, strerror(err)); free(str); } exit(1); } xen-4.9.2/tools/xenstore/xenstored_probes.d0000664000175000017500000000211213256712137017212 0ustar smbsmb/* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2 of the License. */ #include provider xenstore { /* tx id, dom id, pid, type, msg */ probe msg(uint32_t, unsigned int, pid_t, int, const char *); /* tx id, dom id, pid, type, reply */ probe reply(uint32_t, unsigned int, pid_t, int, const char *); /* tx id, dom id, pid, reply */ probe error(uint32_t, unsigned int, pid_t, const char *); /* dom id, pid, watch details */ probe watch_event(unsigned int, pid_t, const char *); }; #pragma D attributes Evolving/Evolving/Common provider xenstore provider #pragma D attributes Private/Private/Unknown provider xenstore module #pragma D attributes Private/Private/Unknown provider xenstore function #pragma D attributes Evolving/Evolving/Common provider xenstore name #pragma D attributes Evolving/Evolving/Common provider xenstore args xen-4.9.2/tools/xenstore/list.h0000664000175000017500000003425313256712137014617 0ustar smbsmb#ifndef _LINUX_LIST_H #define _LINUX_LIST_H /* Taken from Linux kernel code, but de-kernelized for userspace. */ #include #undef LIST_HEAD_INIT #undef LIST_HEAD #undef INIT_LIST_HEAD /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200200) #define container_of(ptr, type, member) ({ \ typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) #define list_top(head, type, member) \ ({ \ struct list_head *_head = (head); \ list_empty(_head) ? NULL : list_entry(_head->next, type, member); \ }) /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static __inline__ void __list_add_rcu(struct list_head * new, struct list_head * prev, struct list_head * next) { new->next = next; new->prev = prev; next->prev = new; prev->next = new; } /** * list_add_rcu - add a new entry to rcu-protected list * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static __inline__ void list_add_rcu(struct list_head *new, struct list_head *head) { __list_add_rcu(new, head, head->next); } /** * list_add_tail_rcu - add a new entry to rcu-protected list * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static __inline__ void list_add_tail_rcu(struct list_head *new, struct list_head *head) { __list_add_rcu(new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty on entry does not return true after this, the entry is * in an undefined state. */ static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } /** * list_del_rcu - deletes entry from list without re-initialization * @entry: the element to delete from the list. * * Note: list_empty on entry does not return true after this, * the entry is in an undefined state. It is useful for RCU based * lockfree traversal. * * In particular, it means that we can not poison the forward * pointers that may still be used for walking the list. */ static inline void list_del_rcu(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->prev = LIST_POISON2; } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } /** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add(list, head); } /** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add_tail(list, head); } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(struct list_head *head) { return head->next == head; } static inline void __list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } /** * list_splice - join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head); } /** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head); INIT_LIST_HEAD(list); } } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.prev, typeof(*pos), member)) /** * list_for_each_entry_continue - iterate over list of given type * continuing after existing point * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_continue(pos, head, member) \ for (pos = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is * too wasteful. * You lose the ability to access the tail in O(1). */ struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; #define HLIST_HEAD_INIT { .first = NULL } #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) #define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL) static __inline__ int hlist_unhashed(struct hlist_node *h) { return !h->pprev; } static __inline__ int hlist_empty(struct hlist_head *h) { return !h->first; } static __inline__ void __hlist_del(struct hlist_node *n) { struct hlist_node *next = n->next; struct hlist_node **pprev = n->pprev; *pprev = next; if (next) next->pprev = pprev; } static __inline__ void hlist_del(struct hlist_node *n) { __hlist_del(n); n->next = LIST_POISON1; n->pprev = LIST_POISON2; } /** * hlist_del_rcu - deletes entry from hash list without re-initialization * @entry: the element to delete from the hash list. * * Note: list_unhashed() on entry does not return true after this, * the entry is in an undefined state. It is useful for RCU based * lockfree traversal. * * In particular, it means that we can not poison the forward * pointers that may still be used for walking the hash list. */ static inline void hlist_del_rcu(struct hlist_node *n) { __hlist_del(n); n->pprev = LIST_POISON2; } static __inline__ void hlist_del_init(struct hlist_node *n) { if (n->pprev) { __hlist_del(n); INIT_HLIST_NODE(n); } } #define hlist_del_rcu_init hlist_del_init static __inline__ void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if (first) first->pprev = &n->next; h->first = n; n->pprev = &h->first; } static __inline__ void hlist_add_head_rcu(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; n->pprev = &h->first; if (first) first->pprev = &n->next; h->first = n; } /* next must be != NULL */ static __inline__ void hlist_add_before(struct hlist_node *n, struct hlist_node *next) { n->pprev = next->pprev; n->next = next; next->pprev = &n->next; *(n->pprev) = n; } static __inline__ void hlist_add_after(struct hlist_node *n, struct hlist_node *next) { next->next = n->next; *(next->pprev) = n; n->next = next; } #define hlist_entry(ptr, type, member) container_of(ptr,type,member) /* Cannot easily do prefetch unfortunately */ #define hlist_for_each(pos, head) \ for (pos = (head)->first; pos; pos = pos->next) #define hlist_for_each_safe(pos, n, head) \ for (pos = (head)->first; n = pos ? pos->next : 0, pos; \ pos = n) /** * hlist_for_each_entry - iterate over list of given type * @tpos: the type * to use as a loop counter. * @pos: the &struct hlist_node to use as a loop counter. * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry(tpos, pos, head, member) \ for (pos = (head)->first; \ pos && ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) /** * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point * @tpos: the type * to use as a loop counter. * @pos: the &struct hlist_node to use as a loop counter. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_continue(tpos, pos, member) \ for (pos = (pos)->next; \ pos && ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) /** * hlist_for_each_entry_from - iterate over a hlist continuing from existing point * @tpos: the type * to use as a loop counter. * @pos: the &struct hlist_node to use as a loop counter. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_from(tpos, pos, member) \ for (; pos && ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = pos->next) /** * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @tpos: the type * to use as a loop counter. * @pos: the &struct hlist_node to use as a loop counter. * @n: another &struct hlist_node to use as temporary storage * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ for (pos = (head)->first; \ pos && ({ n = pos->next; 1; }) && \ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ pos = n) #endif xen-4.9.2/tools/xenstore/xenstored_minios.c0000664000175000017500000000261413256712137017224 0ustar smbsmb/* Simple prototype Xen Store Daemon providing simple tree-like database. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include "xenstored_core.h" #include void write_pidfile(const char *pidfile) { } void daemonize(void) { } void finish_daemonize(void) { } void init_pipe(int reopen_log_pipe[2]) { reopen_log_pipe[0] = -1; reopen_log_pipe[1] = -1; } void xenbus_notify_running(void) { } evtchn_port_t xenbus_evtchn(void) { return dom0_event; } void *xenbus_map(void) { return xengnttab_map_grant_ref(*xgt_handle, xenbus_master_domid(), GNTTAB_RESERVED_XENSTORE, PROT_READ|PROT_WRITE); } void unmap_xenbus(void *interface) { xengnttab_unmap(*xgt_handle, interface, 1); } xen-4.9.2/tools/xenstore/xenstored_transaction.h0000664000175000017500000000352413256712137020261 0ustar smbsmb/* Transaction code for Xen Store Daemon. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef _XENSTORED_TRANSACTION_H #define _XENSTORED_TRANSACTION_H #include "xenstored_core.h" enum node_access_type { NODE_ACCESS_READ, NODE_ACCESS_WRITE, NODE_ACCESS_DELETE }; struct transaction; int do_transaction_start(struct connection *conn, struct buffered_data *node); int do_transaction_end(struct connection *conn, struct buffered_data *in); struct transaction *transaction_lookup(struct connection *conn, uint32_t id); /* inc/dec entry number local to trans while changing a node */ void transaction_entry_inc(struct transaction *trans, unsigned int domid); void transaction_entry_dec(struct transaction *trans, unsigned int domid); /* This node was accessed. */ int access_node(struct connection *conn, struct node *node, enum node_access_type type, TDB_DATA *key); /* Prepend the transaction to name if appropriate. */ int transaction_prepend(struct connection *conn, const char *name, TDB_DATA *key); void conn_delete_all_transactions(struct connection *conn); int check_transactions(struct hashtable *hash); #endif /* _XENSTORED_TRANSACTION_H */ xen-4.9.2/tools/xenstore/.gdbinit0000664000175000017500000000023313256712137015103 0ustar smbsmbset environment XENSTORED_RUNDIR=testsuite/tmp set environment XENSTORED_ROOTDIR=testsuite/tmp handle SIGUSR1 noprint nostop handle SIGPIPE noprint nostop xen-4.9.2/tools/xenstore/xenstored_posix.c0000664000175000017500000000614413256712137017072 0ustar smbsmb/* Simple prototype Xen Store Daemon providing simple tree-like database. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include #include "utils.h" #include "xenstored_core.h" #include "xenstored_osdep.h" void write_pidfile(const char *pidfile) { char buf[100]; int len; int fd; fd = open(pidfile, O_RDWR | O_CREAT, 0600); if (fd == -1) barf_perror("Opening pid file %s", pidfile); /* We exit silently if daemon already running. */ if (lockf(fd, F_TLOCK, 0) == -1) exit(0); len = snprintf(buf, sizeof(buf), "%ld\n", (long)getpid()); if (write(fd, buf, len) != len) barf_perror("Writing pid file %s", pidfile); close(fd); } /* Stevens. */ void daemonize(void) { pid_t pid; /* Separate from our parent via fork, so init inherits us. */ if ((pid = fork()) < 0) barf_perror("Failed to fork daemon"); if (pid != 0) exit(0); /* Session leader so ^C doesn't whack us. */ setsid(); /* Let session leader exit so child cannot regain CTTY */ if ((pid = fork()) < 0) barf_perror("Failed to fork daemon"); if (pid != 0) exit(0); /* Move off any mount points we might be in. */ if (chdir("/") == -1) barf_perror("Failed to chdir"); /* Discard our parent's old-fashioned umask prejudices. */ umask(0); } void finish_daemonize(void) { int devnull = open("/dev/null", O_RDWR); if (devnull == -1) barf_perror("Could not open /dev/null\n"); dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); dup2(devnull, STDERR_FILENO); close(devnull); xprintf = trace; } void init_pipe(int reopen_log_pipe[2]) { if (pipe(reopen_log_pipe)) { barf_perror("pipe"); } } void unmap_xenbus(void *interface) { munmap(interface, getpagesize()); } #ifndef __sun__ evtchn_port_t xenbus_evtchn(void) { int fd; int rc; evtchn_port_t port; char str[20]; fd = open(XENSTORED_PORT_DEV, O_RDONLY); if (fd == -1) return -1; rc = read(fd, str, sizeof(str) - 1); if (rc == -1) { int err = errno; close(fd); errno = err; return -1; } str[rc] = '\0'; port = strtoul(str, NULL, 0); close(fd); return port; } void *xenbus_map(void) { int fd; void *addr; fd = open(XENSTORED_KVA_DEV, O_RDWR); if (fd == -1) return NULL; addr = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) addr = NULL; close(fd); return addr; } void xenbus_notify_running(void) { } #endif /* !__sun__ */ xen-4.9.2/tools/xenstore/xenstored_osdep.h0000664000175000017500000000122113256712137017036 0ustar smbsmb/* * OS specific bits for xenstored * Copyright (C) 2014 Citrix Systems R&D. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2 of the * License. */ #if defined(__linux__) #define XENSTORED_KVA_DEV "/proc/xen/xsd_kva" #define XENSTORED_PORT_DEV "/proc/xen/xsd_port" #elif defined(__NetBSD__) #define XENSTORED_KVA_DEV "/dev/xsd_kva" #define XENSTORED_PORT_DEV "/kern/xen/xsd_port" #elif defined(__FreeBSD__) #define XENSTORED_KVA_DEV "/dev/xen/xenstored" #define XENSTORED_PORT_DEV "/dev/xen/xenstored" #endif xen-4.9.2/tools/xenstore/TODO0000664000175000017500000000066113256712137014157 0ustar smbsmbTODO in no particular order. Some of these will never be done. There are omissions of important but necessary things. It is up to the reader to fill in the blanks. - Timeout failed watch responses - Dynamic/supply nodes - Persistant storage of introductions, watches and transactions, so daemon can restart - Remove assumption that rename doesn't fail - Multi-root transactions, for setting up front and back ends at same time. xen-4.9.2/tools/xenstore/Makefile0000664000175000017500000001322613256712137015130 0ustar smbsmbXEN_ROOT=$(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk MAJOR = 3.0 MINOR = 3 CFLAGS += -Werror CFLAGS += -I. # Include configure output (config.h) CFLAGS += -include $(XEN_ROOT)/tools/config.h CFLAGS += -I./include CFLAGS += $(CFLAGS_libxenevtchn) CFLAGS += $(CFLAGS_libxenctrl) CFLAGS += -DXEN_LIB_STORED="\"$(XEN_LIB_STORED)\"" CFLAGS += -DXEN_RUN_STORED="\"$(XEN_RUN_STORED)\"" CFLAGS-$(CONFIG_SYSTEMD) += $(SYSTEMD_CFLAGS) LDFLAGS-$(CONFIG_SYSTEMD) += $(SYSTEMD_LIBS) CFLAGS += $(CFLAGS-y) LDFLAGS += $(LDFLAGS-y) CLIENTS := xenstore-exists xenstore-list xenstore-read xenstore-rm xenstore-chmod CLIENTS += xenstore-write xenstore-ls xenstore-watch XENSTORED_OBJS = xenstored_core.o xenstored_watch.o xenstored_domain.o XENSTORED_OBJS += xenstored_transaction.o xenstored_control.o XENSTORED_OBJS += xs_lib.o talloc.o utils.o tdb.o hashtable.o XENSTORED_OBJS_$(CONFIG_Linux) = xenstored_posix.o XENSTORED_OBJS_$(CONFIG_SunOS) = xenstored_solaris.o xenstored_posix.o xenstored_probes.o XENSTORED_OBJS_$(CONFIG_NetBSD) = xenstored_posix.o XENSTORED_OBJS_$(CONFIG_FreeBSD) = xenstored_posix.o XENSTORED_OBJS_$(CONFIG_MiniOS) = xenstored_minios.o XENSTORED_OBJS += $(XENSTORED_OBJS_y) LDLIBS_xenstored += -lrt ifneq ($(XENSTORE_STATIC_CLIENTS),y) LIBXENSTORE := libxenstore.so else LIBXENSTORE := libxenstore.a xenstore xenstore-control: CFLAGS += -static endif ALL_TARGETS = libxenstore.a clients ifneq ($(nosharedlibs),y) ALL_TARGETS += libxenstore.so endif ifeq ($(XENSTORE_XENSTORED),y) ALL_TARGETS += xs_tdb_dump xenstored endif ifdef CONFIG_STUBDOM CFLAGS += -DNO_SOCKETS=1 endif .PHONY: all all: $(ALL_TARGETS) .PHONY: clients clients: xenstore $(CLIENTS) xenstore-control ifeq ($(CONFIG_SunOS),y) xenstored_probes.h: xenstored_probes.d dtrace -C -h -s xenstored_probes.d xenstored_solaris.o: xenstored_probes.h xenstored_probes.o: xenstored_solaris.o dtrace -C -G -s xenstored_probes.d xenstored_solaris.o CFLAGS += -DHAVE_DTRACE=1 endif $(XENSTORED_OBJS): CFLAGS += $(CFLAGS_libxengnttab) xenstored: $(XENSTORED_OBJS) $(CC) $^ $(LDFLAGS) $(LDLIBS_libxenevtchn) $(LDLIBS_libxengnttab) $(LDLIBS_libxenctrl) $(LDLIBS_xenstored) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS) xenstored.a: $(XENSTORED_OBJS) $(AR) cr $@ $^ $(CLIENTS): xenstore ln -f xenstore $@ xenstore: xenstore_client.o $(LIBXENSTORE) $(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS) xenstore-control: xenstore_control.o $(LIBXENSTORE) $(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS) xs_tdb_dump: xs_tdb_dump.o utils.o tdb.o talloc.o $(CC) $^ $(LDFLAGS) -o $@ $(APPEND_LDFLAGS) libxenstore.so: libxenstore.so.$(MAJOR) ln -sf $< $@ libxenstore.so.$(MAJOR): libxenstore.so.$(MAJOR).$(MINOR) ln -sf $< $@ xs.opic: CFLAGS += -DUSE_PTHREAD libxenstore.so.$(MAJOR).$(MINOR): xs.opic xs_lib.opic $(CC) $(LDFLAGS) $(PTHREAD_LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenstore.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(SOCKET_LIBS) $(PTHREAD_LIBS) $(APPEND_LDFLAGS) libxenstore.a: xs.o xs_lib.o $(AR) rcs $@ $^ PKG_CONFIG := xenstore.pc PKG_CONFIG_VERSION := $(MAJOR).$(MINOR) ifneq ($(CONFIG_LIBXC_MINIOS),y) PKG_CONFIG_INST := $(PKG_CONFIG) $(PKG_CONFIG_INST): PKG_CONFIG_PREFIX = $(prefix) $(PKG_CONFIG_INST): PKG_CONFIG_INCDIR = $(includedir) $(PKG_CONFIG_INST): PKG_CONFIG_LIBDIR = $(libdir) endif PKG_CONFIG_LOCAL := $(foreach pc,$(PKG_CONFIG),$(PKG_CONFIG_DIR)/$(pc)) $(PKG_CONFIG_LOCAL): PKG_CONFIG_PREFIX = $(XEN_ROOT) $(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(XEN_XENSTORE)/include $(PKG_CONFIG_LOCAL): PKG_CONFIG_LIBDIR = $(CURDIR) $(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude) $(LIBXENSTORE): $(PKG_CONFIG_INST) $(PKG_CONFIG_LOCAL) .PHONY: clean clean: rm -f *.a *.o *.opic *.so* xenstored_probes.h rm -f xenstored xs_random xs_stress xs_crashme rm -f xs_tdb_dump xenstore-control init-xenstore-domain rm -f xenstore $(CLIENTS) rm -f xenstore.pc $(RM) $(DEPS) .PHONY: distclean distclean: clean .PHONY: TAGS TAGS: etags `find . -name '*.[ch]'` .PHONY: tarball tarball: clean cd .. && tar -c -j -v -h -f xenstore.tar.bz2 xenstore/ .PHONY: install install: all $(INSTALL_DIR) $(DESTDIR)$(bindir) $(INSTALL_DIR) $(DESTDIR)$(includedir) $(INSTALL_DIR) $(DESTDIR)$(includedir)/xenstore-compat ifeq ($(XENSTORE_XENSTORED),y) $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_DIR) $(DESTDIR)$(XEN_LIB_STORED) $(INSTALL_PROG) xenstored $(DESTDIR)$(sbindir) endif $(INSTALL_PROG) xenstore-control $(DESTDIR)$(bindir) $(INSTALL_PROG) xenstore $(DESTDIR)$(bindir) set -e ; for c in $(CLIENTS) ; do \ ln -f $(DESTDIR)$(bindir)/xenstore $(DESTDIR)$(bindir)/$${c} ; \ done $(INSTALL_DIR) $(DESTDIR)$(libdir) $(INSTALL_SHLIB) libxenstore.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir) ln -sf libxenstore.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libxenstore.so.$(MAJOR) ln -sf libxenstore.so.$(MAJOR) $(DESTDIR)$(libdir)/libxenstore.so $(INSTALL_DATA) libxenstore.a $(DESTDIR)$(libdir) $(INSTALL_DATA) include/xenstore.h $(DESTDIR)$(includedir) $(INSTALL_DATA) include/xenstore_lib.h $(DESTDIR)$(includedir) $(INSTALL_DATA) include/compat/xs.h $(DESTDIR)$(includedir)/xenstore-compat/xs.h $(INSTALL_DATA) include/compat/xs_lib.h $(DESTDIR)$(includedir)/xenstore-compat/xs_lib.h ln -sf xenstore-compat/xs.h $(DESTDIR)$(includedir)/xs.h ln -sf xenstore-compat/xs_lib.h $(DESTDIR)$(includedir)/xs_lib.h $(INSTALL_DATA) xenstore.pc $(DESTDIR)$(PKG_INSTALLDIR) .PHONY: clients-install clients-install: clients $(INSTALL_DIR) $(DESTDIR)$(bindir) $(INSTALL_PROG) xenstore $(DESTDIR)$(bindir) set -e ; for c in $(CLIENTS) ; do \ ln -f $(DESTDIR)$(bindir)/xenstore $(DESTDIR)$(bindir)/$${c} ; \ done -include $(DEPS) # never delete any intermediate files. .SECONDARY: xen-4.9.2/tools/xenstore/talloc.c0000664000175000017500000007052613256712137015120 0ustar smbsmb/* Samba Unix SMB/CIFS implementation. Samba trivial allocation library - new interface NOTE: Please read talloc_guide.txt for full documentation Copyright (C) Andrew Tridgell 2004 ** NOTE! The following LGPL license applies to the talloc ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . */ /* inspired by http://swapped.cc/halloc/ */ #ifdef _SAMBA_BUILD_ #include "includes.h" #if ((SAMBA_VERSION_MAJOR==3)&&(SAMBA_VERSION_MINOR<9)) /* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file * we trust ourselves... */ #ifdef malloc #undef malloc #endif #ifdef realloc #undef realloc #endif #endif #else #include #include #include #include #include #include "talloc.h" /* assume a modern system */ #define HAVE_VA_COPY #endif /* use this to force every realloc to change the pointer, to stress test code that might not cope */ #define ALWAYS_REALLOC 0 #define MAX_TALLOC_SIZE 0x10000000 #define TALLOC_MAGIC 0xe814ec70 #define TALLOC_FLAG_FREE 0x01 #define TALLOC_FLAG_LOOP 0x02 #define TALLOC_MAGIC_REFERENCE ((const char *)1) /* by default we abort when given a bad pointer (such as when talloc_free() is called on a pointer that came from malloc() */ #ifndef TALLOC_ABORT #define TALLOC_ABORT(reason) abort() #endif #ifndef discard_const_p #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) # define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) #else # define discard_const_p(type, ptr) ((type *)(ptr)) #endif #endif /* this null_context is only used if talloc_enable_leak_report() or talloc_enable_leak_report_full() is called, otherwise it remains NULL */ static const void *null_context; static void *cleanup_context; struct talloc_reference_handle { struct talloc_reference_handle *next, *prev; void *ptr; }; typedef int (*talloc_destructor_t)(void *); struct talloc_chunk { struct talloc_chunk *next, *prev; struct talloc_chunk *parent, *child; struct talloc_reference_handle *refs; unsigned int null_refs; /* references from null_context */ talloc_destructor_t destructor; const char *name; size_t size; unsigned flags; }; /* 16 byte alignment seems to keep everyone happy */ #define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) #define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) /* panic if we get a bad magic value */ static struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) { const char *pp = ptr; struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); if ((tc->flags & ~0xF) != TALLOC_MAGIC) { TALLOC_ABORT("Bad talloc magic value - unknown value"); } if (tc->flags & TALLOC_FLAG_FREE) { TALLOC_ABORT("Bad talloc magic value - double free"); } return tc; } /* hook into the front of the list */ #define _TLIST_ADD(list, p) \ do { \ if (!(list)) { \ (list) = (p); \ (p)->next = (p)->prev = NULL; \ } else { \ (list)->prev = (p); \ (p)->next = (list); \ (p)->prev = NULL; \ (list) = (p); \ }\ } while (0) /* remove an element from a list - element doesn't have to be in list. */ #define _TLIST_REMOVE(list, p) \ do { \ if ((p) == (list)) { \ (list) = (p)->next; \ if (list) (list)->prev = NULL; \ } else { \ if ((p)->prev) (p)->prev->next = (p)->next; \ if ((p)->next) (p)->next->prev = (p)->prev; \ } \ if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ } while (0) /* return the parent chunk of a pointer */ static struct talloc_chunk *talloc_parent_chunk(const void *ptr) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); while (tc->prev) tc=tc->prev; return tc->parent; } void *talloc_parent(const void *ptr) { struct talloc_chunk *tc = talloc_parent_chunk(ptr); return tc? TC_PTR_FROM_CHUNK(tc) : NULL; } /* Allocate a bit of memory as a child of an existing pointer */ void *_talloc(const void *context, size_t size) { struct talloc_chunk *tc; if (context == NULL) { context = null_context; } if (size >= MAX_TALLOC_SIZE) { return NULL; } tc = malloc(TC_HDR_SIZE+size); if (tc == NULL) return NULL; tc->size = size; tc->flags = TALLOC_MAGIC; tc->destructor = NULL; tc->child = NULL; tc->name = NULL; tc->refs = NULL; tc->null_refs = 0; if (context) { struct talloc_chunk *parent = talloc_chunk_from_ptr(context); tc->parent = parent; if (parent->child) { parent->child->parent = NULL; } _TLIST_ADD(parent->child, tc); } else { tc->next = tc->prev = tc->parent = NULL; } return TC_PTR_FROM_CHUNK(tc); } /* setup a destructor to be called on free of a pointer the destructor should return 0 on success, or -1 on failure. if the destructor fails then the free is failed, and the memory can be continued to be used */ void talloc_set_destructor(const void *ptr, int (*destructor)(void *)) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); tc->destructor = destructor; } /* increase the reference count on a piece of memory. */ void talloc_increase_ref_count(const void *ptr) { struct talloc_chunk *tc; if (ptr == NULL) return; tc = talloc_chunk_from_ptr(ptr); tc->null_refs++; } /* helper for talloc_reference() */ static int talloc_reference_destructor(void *ptr) { struct talloc_reference_handle *handle = ptr; struct talloc_chunk *tc1 = talloc_chunk_from_ptr(ptr); struct talloc_chunk *tc2 = talloc_chunk_from_ptr(handle->ptr); if (tc1->destructor != (talloc_destructor_t)-1) { tc1->destructor = NULL; } _TLIST_REMOVE(tc2->refs, handle); talloc_free(handle); return 0; } /* make a secondary reference to a pointer, hanging off the given context. the pointer remains valid until both the original caller and this given context are freed. the major use for this is when two different structures need to reference the same underlying data, and you want to be able to free the two instances separately, and in either order */ void *talloc_reference(const void *context, const void *ptr) { struct talloc_chunk *tc; struct talloc_reference_handle *handle; if (ptr == NULL) return NULL; tc = talloc_chunk_from_ptr(ptr); handle = talloc_named_const(context, sizeof(*handle), TALLOC_MAGIC_REFERENCE); if (handle == NULL) return NULL; /* note that we hang the destructor off the handle, not the main context as that allows the caller to still setup their own destructor on the context if they want to */ talloc_set_destructor(handle, talloc_reference_destructor); handle->ptr = discard_const_p(void, ptr); _TLIST_ADD(tc->refs, handle); return handle->ptr; } /* remove a secondary reference to a pointer. This undo's what talloc_reference() has done. The context and pointer arguments must match those given to a talloc_reference() */ static int talloc_unreference(const void *context, const void *ptr) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); struct talloc_reference_handle *h; if (context == NULL) { context = null_context; } if ((context == null_context) && tc->null_refs) { tc->null_refs--; return 0; } for (h=tc->refs;h;h=h->next) { struct talloc_chunk *p = talloc_parent_chunk(h); if (p == NULL) { if (context == NULL) break; } else if (TC_PTR_FROM_CHUNK(p) == context) { break; } } if (h == NULL) { return -1; } talloc_set_destructor(h, NULL); _TLIST_REMOVE(tc->refs, h); talloc_free(h); return 0; } /* remove a specific parent context from a pointer. This is a more controlled varient of talloc_free() */ int talloc_unlink(const void *context, void *ptr) { struct talloc_chunk *tc_p, *new_p; void *new_parent; if (ptr == NULL) { return -1; } if (context == NULL) { context = null_context; } if (talloc_unreference(context, ptr) == 0) { return 0; } if (context == NULL) { if (talloc_parent_chunk(ptr) != NULL) { return -1; } } else { if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { return -1; } } tc_p = talloc_chunk_from_ptr(ptr); if (tc_p->refs == NULL) { return talloc_free(ptr); } new_p = talloc_parent_chunk(tc_p->refs); if (new_p) { new_parent = TC_PTR_FROM_CHUNK(new_p); } else { new_parent = NULL; } if (talloc_unreference(new_parent, ptr) != 0) { return -1; } talloc_steal(new_parent, ptr); return 0; } /* add a name to an existing pointer - va_list version */ static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); tc->name = talloc_vasprintf(ptr, fmt, ap); if (tc->name) { talloc_set_name_const(tc->name, ".name"); } } /* add a name to an existing pointer */ void talloc_set_name(const void *ptr, const char *fmt, ...) { va_list ap; va_start(ap, fmt); talloc_set_name_v(ptr, fmt, ap); va_end(ap); } /* more efficient way to add a name to a pointer - the name must point to a true string constant */ void talloc_set_name_const(const void *ptr, const char *name) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); tc->name = name; } /* create a named talloc pointer. Any talloc pointer can be named, and talloc_named() operates just like talloc() except that it allows you to name the pointer. */ void *talloc_named(const void *context, size_t size, const char *fmt, ...) { va_list ap; void *ptr; ptr = _talloc(context, size); if (ptr == NULL) return NULL; va_start(ap, fmt); talloc_set_name_v(ptr, fmt, ap); va_end(ap); return ptr; } /* create a named talloc pointer. Any talloc pointer can be named, and talloc_named() operates just like talloc() except that it allows you to name the pointer. */ void *talloc_named_const(const void *context, size_t size, const char *name) { void *ptr; ptr = _talloc(context, size); if (ptr == NULL) { return NULL; } talloc_set_name_const(ptr, name); return ptr; } /* return the name of a talloc ptr, or "UNNAMED" */ const char *talloc_get_name(const void *ptr) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); if (tc->name == TALLOC_MAGIC_REFERENCE) { return ".reference"; } if (tc->name) { return tc->name; } return "UNNAMED"; } /* check if a pointer has the given name. If it does, return the pointer, otherwise return NULL */ void *talloc_check_name(const void *ptr, const char *name) { const char *pname; if (ptr == NULL) return NULL; pname = talloc_get_name(ptr); if (pname == name || strcmp(pname, name) == 0) { return discard_const_p(void, ptr); } return NULL; } /* this is for compatibility with older versions of talloc */ void *talloc_init(const char *fmt, ...) { va_list ap; void *ptr; talloc_enable_null_tracking(); ptr = _talloc(NULL, 0); if (ptr == NULL) return NULL; va_start(ap, fmt); talloc_set_name_v(ptr, fmt, ap); va_end(ap); return ptr; } /* this is a replacement for the Samba3 talloc_destroy_pool functionality. It should probably not be used in new code. It's in here to keep the talloc code consistent across Samba 3 and 4. */ static void talloc_free_children(void *ptr) { struct talloc_chunk *tc; if (ptr == NULL) { return; } tc = talloc_chunk_from_ptr(ptr); while (tc->child) { /* we need to work out who will own an abandoned child if it cannot be freed. In priority order, the first choice is owner of any remaining reference to this pointer, the second choice is our parent, and the final choice is the null context. */ void *child = TC_PTR_FROM_CHUNK(tc->child); const void *new_parent = null_context; if (tc->child->refs) { struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } if (talloc_free(child) == -1) { if (new_parent == null_context) { struct talloc_chunk *p = talloc_parent_chunk(ptr); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } talloc_steal(new_parent, child); } } } /* free a talloc pointer. This also frees all child pointers of this pointer recursively return 0 if the memory is actually freed, otherwise -1. The memory will not be freed if the ref_count is > 1 or the destructor (if any) returns non-zero */ int talloc_free(void *ptr) { struct talloc_chunk *tc; if (ptr == NULL) { return -1; } tc = talloc_chunk_from_ptr(ptr); if (tc->null_refs) { tc->null_refs--; return -1; } if (tc->refs) { talloc_reference_destructor(tc->refs); return -1; } if (tc->flags & TALLOC_FLAG_LOOP) { /* we have a free loop - stop looping */ return 0; } if (tc->destructor) { talloc_destructor_t d = tc->destructor; if (d == (talloc_destructor_t)-1) { return -1; } tc->destructor = (talloc_destructor_t)-1; if (d(ptr) == -1) { tc->destructor = d; return -1; } tc->destructor = NULL; } tc->flags |= TALLOC_FLAG_LOOP; talloc_free_children(ptr); if (tc->parent) { _TLIST_REMOVE(tc->parent->child, tc); if (tc->parent->child) { tc->parent->child->parent = tc->parent; } } else { if (tc->prev) tc->prev->next = tc->next; if (tc->next) tc->next->prev = tc->prev; } tc->flags |= TALLOC_FLAG_FREE; free(tc); return 0; } /* A talloc version of realloc. The context argument is only used if ptr is NULL */ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) { struct talloc_chunk *tc; void *new_ptr; /* size zero is equivalent to free() */ if (size == 0) { talloc_free(ptr); return NULL; } if (size >= MAX_TALLOC_SIZE) { return NULL; } /* realloc(NULL) is equavalent to malloc() */ if (ptr == NULL) { return talloc_named_const(context, size, name); } tc = talloc_chunk_from_ptr(ptr); /* don't allow realloc on referenced pointers */ if (tc->refs) { return NULL; } /* by resetting magic we catch users of the old memory */ tc->flags |= TALLOC_FLAG_FREE; #if ALWAYS_REALLOC new_ptr = malloc(size + TC_HDR_SIZE); if (new_ptr) { memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); free(tc); } #else new_ptr = realloc(tc, size + TC_HDR_SIZE); #endif if (!new_ptr) { tc->flags &= ~TALLOC_FLAG_FREE; return NULL; } tc = new_ptr; tc->flags &= ~TALLOC_FLAG_FREE; if (tc->parent) { tc->parent->child = new_ptr; } if (tc->child) { tc->child->parent = new_ptr; } if (tc->prev) { tc->prev->next = tc; } if (tc->next) { tc->next->prev = tc; } tc->size = size; talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); return TC_PTR_FROM_CHUNK(tc); } /* move a lump of memory from one talloc context to another return the ptr on success, or NULL if it could not be transferred. passing NULL as ptr will always return NULL with no side effects. */ void *talloc_steal(const void *new_ctx, const void *ptr) { struct talloc_chunk *tc, *new_tc; if (!ptr) { return NULL; } if (new_ctx == NULL) { new_ctx = null_context; } tc = talloc_chunk_from_ptr(ptr); if (new_ctx == NULL) { if (tc->parent) { _TLIST_REMOVE(tc->parent->child, tc); if (tc->parent->child) { tc->parent->child->parent = tc->parent; } } else { if (tc->prev) tc->prev->next = tc->next; if (tc->next) tc->next->prev = tc->prev; } tc->parent = tc->next = tc->prev = NULL; return discard_const_p(void, ptr); } new_tc = talloc_chunk_from_ptr(new_ctx); if (tc == new_tc) { return discard_const_p(void, ptr); } if (tc->parent) { _TLIST_REMOVE(tc->parent->child, tc); if (tc->parent->child) { tc->parent->child->parent = tc->parent; } } else { if (tc->prev) tc->prev->next = tc->next; if (tc->next) tc->next->prev = tc->prev; } tc->parent = new_tc; if (new_tc->child) new_tc->child->parent = NULL; _TLIST_ADD(new_tc->child, tc); return discard_const_p(void, ptr); } /* return the total size of a talloc pool (subtree) */ off_t talloc_total_size(const void *ptr) { off_t total = 0; struct talloc_chunk *c, *tc; if (ptr == NULL) { ptr = null_context; } if (ptr == NULL) { return 0; } tc = talloc_chunk_from_ptr(ptr); if (tc->flags & TALLOC_FLAG_LOOP) { return 0; } tc->flags |= TALLOC_FLAG_LOOP; total = tc->size; for (c=tc->child;c;c=c->next) { total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); } tc->flags &= ~TALLOC_FLAG_LOOP; return total; } /* return the total number of blocks in a talloc pool (subtree) */ off_t talloc_total_blocks(const void *ptr) { off_t total = 0; struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); if (tc->flags & TALLOC_FLAG_LOOP) { return 0; } tc->flags |= TALLOC_FLAG_LOOP; total++; for (c=tc->child;c;c=c->next) { total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); } tc->flags &= ~TALLOC_FLAG_LOOP; return total; } /* return the number of external references to a pointer */ static int talloc_reference_count(const void *ptr) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); struct talloc_reference_handle *h; int ret = 0; for (h=tc->refs;h;h=h->next) { ret++; } return ret; } /* report on memory usage by all children of a pointer, giving a full tree view */ void talloc_report_depth(const void *ptr, FILE *f, int depth) { struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); if (tc->flags & TALLOC_FLAG_LOOP) { return; } tc->flags |= TALLOC_FLAG_LOOP; for (c=tc->child;c;c=c->next) { if (c->name == TALLOC_MAGIC_REFERENCE) { struct talloc_reference_handle *handle = TC_PTR_FROM_CHUNK(c); const char *name2 = talloc_get_name(handle->ptr); fprintf(f, "%*sreference to: %s\n", depth*4, "", name2); } else { const char *name = talloc_get_name(TC_PTR_FROM_CHUNK(c)); fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d)\n", depth*4, "", name, (unsigned long)talloc_total_size(TC_PTR_FROM_CHUNK(c)), (unsigned long)talloc_total_blocks(TC_PTR_FROM_CHUNK(c)), talloc_reference_count(TC_PTR_FROM_CHUNK(c))); talloc_report_depth(TC_PTR_FROM_CHUNK(c), f, depth+1); } } tc->flags &= ~TALLOC_FLAG_LOOP; } /* report on memory usage by all children of a pointer, giving a full tree view */ void talloc_report_full(const void *ptr, FILE *f) { if (ptr == NULL) { ptr = null_context; } if (ptr == NULL) return; fprintf(f,"full talloc report on '%s' (total %lu bytes in %lu blocks)\n", talloc_get_name(ptr), (unsigned long)talloc_total_size(ptr), (unsigned long)talloc_total_blocks(ptr)); talloc_report_depth(ptr, f, 1); fflush(f); } /* report on memory usage by all children of a pointer */ void talloc_report(const void *ptr, FILE *f) { struct talloc_chunk *c, *tc; if (ptr == NULL) { ptr = null_context; } if (ptr == NULL) return; fprintf(f,"talloc report on '%s' (total %lu bytes in %lu blocks)\n", talloc_get_name(ptr), (unsigned long)talloc_total_size(ptr), (unsigned long)talloc_total_blocks(ptr)); tc = talloc_chunk_from_ptr(ptr); for (c=tc->child;c;c=c->next) { fprintf(f, "\t%-30s contains %6lu bytes in %3lu blocks\n", talloc_get_name(TC_PTR_FROM_CHUNK(c)), (unsigned long)talloc_total_size(TC_PTR_FROM_CHUNK(c)), (unsigned long)talloc_total_blocks(TC_PTR_FROM_CHUNK(c))); } fflush(f); } /* report on any memory hanging off the null context */ static void talloc_report_null(void) { if (talloc_total_size(null_context) != 0) { talloc_report(null_context, stderr); } } /* report on any memory hanging off the null context */ static void talloc_report_null_full(void) { if (talloc_total_size(null_context) != 0) { talloc_report_full(null_context, stderr); } } /* enable tracking of the NULL context */ void talloc_enable_null_tracking(void) { if (null_context == NULL) { null_context = talloc_named_const(NULL, 0, "null_context"); } } #ifdef _SAMBA_BUILD_ /* Ugly calls to Samba-specific sprintf_append... JRA. */ /* report on memory usage by all children of a pointer, giving a full tree view */ static void talloc_report_depth_str(const void *ptr, char **pps, ssize_t *plen, size_t *pbuflen, int depth) { struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); if (tc->flags & TALLOC_FLAG_LOOP) { return; } tc->flags |= TALLOC_FLAG_LOOP; for (c=tc->child;c;c=c->next) { if (c->name == TALLOC_MAGIC_REFERENCE) { struct talloc_reference_handle *handle = TC_PTR_FROM_CHUNK(c); const char *name2 = talloc_get_name(handle->ptr); sprintf_append(NULL, pps, plen, pbuflen, "%*sreference to: %s\n", depth*4, "", name2); } else { const char *name = talloc_get_name(TC_PTR_FROM_CHUNK(c)); sprintf_append(NULL, pps, plen, pbuflen, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d)\n", depth*4, "", name, (unsigned long)talloc_total_size(TC_PTR_FROM_CHUNK(c)), (unsigned long)talloc_total_blocks(TC_PTR_FROM_CHUNK(c)), talloc_reference_count(TC_PTR_FROM_CHUNK(c))); talloc_report_depth_str(TC_PTR_FROM_CHUNK(c), pps, plen, pbuflen, depth+1); } } tc->flags &= ~TALLOC_FLAG_LOOP; } /* report on memory usage by all children of a pointer */ char *talloc_describe_all(void) { ssize_t len = 0; size_t buflen = 512; char *s = NULL; if (null_context == NULL) { return NULL; } sprintf_append(NULL, &s, &len, &buflen, "full talloc report on '%s' (total %lu bytes in %lu blocks)\n", talloc_get_name(null_context), (unsigned long)talloc_total_size(null_context), (unsigned long)talloc_total_blocks(null_context)); if (!s) { return NULL; } talloc_report_depth_str(null_context, &s, &len, &buflen, 1); return s; } #endif /* enable leak reporting on exit */ void talloc_enable_leak_report(void) { talloc_enable_null_tracking(); atexit(talloc_report_null); } /* enable full leak reporting on exit */ void talloc_enable_leak_report_full(void) { talloc_enable_null_tracking(); atexit(talloc_report_null_full); } /* talloc and zero memory. */ void *_talloc_zero(const void *ctx, size_t size, const char *name) { void *p = talloc_named_const(ctx, size, name); if (p) { memset(p, '\0', size); } return p; } /* memdup with a talloc. */ void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) { void *newp = talloc_named_const(t, size, name); if (newp) { memcpy(newp, p, size); } return newp; } /* strdup with a talloc */ char *talloc_strdup(const void *t, const char *p) { char *ret; if (!p) { return NULL; } ret = talloc_memdup(t, p, strlen(p) + 1); if (ret) { talloc_set_name_const(ret, ret); } return ret; } /* append to a talloced string */ char *talloc_append_string(const void *t, char *orig, const char *append) { char *ret; size_t olen = strlen(orig); size_t alenz; if (!append) return orig; alenz = strlen(append) + 1; ret = talloc_realloc(t, orig, char, olen + alenz); if (!ret) return NULL; /* append the string with the trailing \0 */ memcpy(&ret[olen], append, alenz); return ret; } /* strndup with a talloc */ char *talloc_strndup(const void *t, const char *p, size_t n) { size_t len; char *ret; for (len=0; lensize - 1; if ((len = vsnprintf(NULL, 0, fmt, ap2)) <= 0) { /* Either the vsnprintf failed or the format resulted in * no characters being formatted. In the former case, we * ought to return NULL, in the latter we ought to return * the original string. Most current callers of this * function expect it to never return NULL. */ va_end(ap2); return s; } va_end(ap2); s = talloc_realloc(NULL, s, char, s_len + len+1); if (!s) return NULL; VA_COPY(ap2, ap); vsnprintf(s+s_len, len+1, fmt, ap2); va_end(ap2); talloc_set_name_const(s, s); return s; } /* Realloc @p s to append the formatted result of @p fmt and return @p s, which may have moved. Good for gradually accumulating output into a string buffer. */ char *talloc_asprintf_append(char *s, const char *fmt, ...) { va_list ap; va_start(ap, fmt); s = talloc_vasprintf_append(s, fmt, ap); va_end(ap); return s; } /* alloc an array, checking for integer overflow in the array size */ void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) { if (count >= MAX_TALLOC_SIZE/el_size) { return NULL; } return talloc_named_const(ctx, el_size * count, name); } /* alloc an zero array, checking for integer overflow in the array size */ void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) { if (count >= MAX_TALLOC_SIZE/el_size) { return NULL; } return _talloc_zero(ctx, el_size * count, name); } /* realloc an array, checking for integer overflow in the array size */ void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) { if (count >= MAX_TALLOC_SIZE/el_size) { return NULL; } return _talloc_realloc(ctx, ptr, el_size * count, name); } /* a function version of talloc_realloc(), so it can be passed as a function pointer to libraries that want a realloc function (a realloc function encapsulates all the basic capabilities of an allocation library, which is why this is useful) */ void *talloc_realloc_fn(const void *context, void *ptr, size_t size) { return _talloc_realloc(context, ptr, size, NULL); } static void talloc_autofree(void) { talloc_free(cleanup_context); cleanup_context = NULL; } /* return a context which will be auto-freed on exit this is useful for reducing the noise in leak reports */ void *talloc_autofree_context(void) { if (cleanup_context == NULL) { cleanup_context = talloc_named_const(NULL, 0, "autofree_context"); atexit(talloc_autofree); } return cleanup_context; } size_t talloc_get_size(const void *context) { struct talloc_chunk *tc; if (context == NULL) return 0; tc = talloc_chunk_from_ptr(context); return tc->size; } /* find a parent of this context that has the given name, if any */ void *talloc_find_parent_byname(const void *context, const char *name) { struct talloc_chunk *tc; if (context == NULL) { return NULL; } tc = talloc_chunk_from_ptr(context); while (tc) { if (tc->name && strcmp(tc->name, name) == 0) { return TC_PTR_FROM_CHUNK(tc); } while (tc && tc->prev) tc = tc->prev; tc = tc->parent; } return NULL; } /* show the parentage of a context */ void talloc_show_parents(const void *context, FILE *file) { struct talloc_chunk *tc; if (context == NULL) { fprintf(file, "talloc no parents for NULL\n"); return; } tc = talloc_chunk_from_ptr(context); fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); while (tc) { fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))); while (tc && tc->prev) tc = tc->prev; tc = tc->parent; } } xen-4.9.2/tools/xenstore/hashtable_private.h0000664000175000017500000000576213256712137017334 0ustar smbsmb/* Copyright (C) 2002, 2004 Christopher Clark */ /* * There are duplicates of this code in: * - tools/blktap2/drivers/hashtable_private.h */ #ifndef __HASHTABLE_PRIVATE_CWC22_H__ #define __HASHTABLE_PRIVATE_CWC22_H__ #include "hashtable.h" /*****************************************************************************/ struct entry { void *k, *v; unsigned int h; struct entry *next; }; struct hashtable { unsigned int tablelength; struct entry **table; unsigned int entrycount; unsigned int loadlimit; unsigned int primeindex; unsigned int (*hashfn) (void *k); int (*eqfn) (void *k1, void *k2); }; /*****************************************************************************/ unsigned int hash(struct hashtable *h, void *k); /*****************************************************************************/ /* indexFor */ static inline unsigned int indexFor(unsigned int tablelength, unsigned int hashvalue) { return (hashvalue % tablelength); }; /* Only works if tablelength == 2^N */ /*static inline unsigned int indexFor(unsigned int tablelength, unsigned int hashvalue) { return (hashvalue & (tablelength - 1u)); } */ /*****************************************************************************/ #define freekey(X) free(X) /*define freekey(X) ; */ /*****************************************************************************/ #endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ /* * Copyright (c) 2002, Christopher Clark * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ xen-4.9.2/tools/xenstore/xs_lib.c0000664000175000017500000001012713256712137015111 0ustar smbsmb/* Common routines between Xen store user library and daemon. Copyright (C) 2005 Rusty Russell IBM Corporation This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . */ #include #include #include #include #include #include "xenstore_lib.h" /* Common routines for the Xen store daemon and client library. */ const char *xs_daemon_rootdir(void) { char *s = getenv("XENSTORED_ROOTDIR"); return (s ? s : XEN_LIB_STORED); } const char *xs_daemon_rundir(void) { char *s = getenv("XENSTORED_RUNDIR"); return (s ? s : XEN_RUN_STORED); } static const char *xs_daemon_path(void) { static char buf[PATH_MAX]; char *s = getenv("XENSTORED_PATH"); if (s) return s; if (snprintf(buf, sizeof(buf), "%s/socket", xs_daemon_rundir()) >= PATH_MAX) return NULL; return buf; } const char *xs_daemon_tdb(void) { static char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/tdb", xs_daemon_rootdir()); return buf; } const char *xs_daemon_socket(void) { return xs_daemon_path(); } const char *xs_daemon_socket_ro(void) { static char buf[PATH_MAX]; const char *s = xs_daemon_path(); if (s == NULL) return NULL; if (snprintf(buf, sizeof(buf), "%s_ro", s) >= PATH_MAX) return NULL; return buf; } const char *xs_domain_dev(void) { char *s = getenv("XENSTORED_PATH"); if (s) return s; #if defined(__RUMPUSER_XEN__) || defined(__RUMPRUN__) return "/dev/xen/xenbus"; #elif defined(__linux__) if (access("/dev/xen/xenbus", F_OK) == 0) return "/dev/xen/xenbus"; return "/proc/xen/xenbus"; #elif defined(__NetBSD__) return "/kern/xen/xenbus"; #elif defined(__FreeBSD__) return "/dev/xen/xenstore"; #else return "/dev/xen/xenbus"; #endif } /* Simple routines for writing to sockets, etc. */ bool xs_write_all(int fd, const void *data, unsigned int len) { while (len) { int done; done = write(fd, data, len); if (done < 0 && errno == EINTR) continue; if (done <= 0) return false; data += done; len -= done; } return true; } /* Convert strings to permissions. False if a problem. */ bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num, const char *strings) { const char *p; char *end; unsigned int i; for (p = strings, i = 0; i < num; i++) { /* "r", "w", or "b" for both. */ switch (*p) { case 'r': perms[i].perms = XS_PERM_READ; break; case 'w': perms[i].perms = XS_PERM_WRITE; break; case 'b': perms[i].perms = XS_PERM_READ|XS_PERM_WRITE; break; case 'n': perms[i].perms = XS_PERM_NONE; break; default: errno = EINVAL; return false; } p++; perms[i].id = strtol(p, &end, 0); if (*end || !*p) { errno = EINVAL; return false; } p = end + 1; } return true; } /* Convert permissions to a string (up to len MAX_STRLEN(unsigned int)+1). */ bool xs_perm_to_string(const struct xs_permissions *perm, char *buffer, size_t buf_len) { switch ((int)perm->perms) { case XS_PERM_WRITE: *buffer = 'w'; break; case XS_PERM_READ: *buffer = 'r'; break; case XS_PERM_READ|XS_PERM_WRITE: *buffer = 'b'; break; case XS_PERM_NONE: *buffer = 'n'; break; default: errno = EINVAL; return false; } snprintf(buffer+1, buf_len-1, "%i", (int)perm->id); return true; } /* Given a string and a length, count how many strings (nul terms). */ unsigned int xs_count_strings(const char *strings, unsigned int len) { unsigned int num; const char *p; for (p = strings, num = 0; p < strings + len; p++) if (*p == '\0') num++; return num; } xen-4.9.2/tools/xenstore/xenstored_solaris.c0000664000175000017500000000631013256712137017377 0ustar smbsmb/****************************************************************************** * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright (C) 2005 Rusty Russell IBM Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2 of the * License. */ #include #include #include #include #include #include #include #include #include #include "talloc.h" #include "xenstored_core.h" #include "xenstored_probes.h" evtchn_port_t xenbus_evtchn(void) { int fd; evtchn_port_t port; fd = open("/dev/xen/xenbus", O_RDONLY); if (fd == -1) return -1; port = ioctl(fd, IOCTL_XENBUS_XENSTORE_EVTCHN); close(fd); return port; } void *xenbus_map(void) { int fd; void *addr; fd = open("/dev/xen/xenbus", O_RDWR); if (fd == -1) return NULL; addr = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) addr = NULL; close(fd); return addr; } void xenbus_notify_running(void) { int fd; fd = open("/dev/xen/xenbus", O_RDONLY); (void) ioctl(fd, IOCTL_XENBUS_NOTIFY_UP); close(fd); } static pid_t cred(const struct connection *conn) { ucred_t *ucred = NULL; pid_t pid; if (conn->domain) return (0); if (getpeerucred(conn->fd, &ucred) == -1) return (0); pid = ucred_getpid(ucred); ucred_free(ucred); return (pid); } /* * The strings are often a number of nil-separated strings. We'll just * replace the separators with spaces - not quite right, but good * enough. */ static char * mangle(const struct connection *conn, const struct buffered_data *in) { char *str; int i; if (in->hdr.msg.len == 0) return (talloc_strdup(conn, "")); if ((str = talloc_zero_size(conn, in->hdr.msg.len + 1)) == NULL) return (NULL); memcpy(str, in->buffer, in->hdr.msg.len); /* * The protocol is absurdly inconsistent in whether the length * includes the terminating nil or not; replace all nils that * aren't the last one. */ for (i = 0; i < (in->hdr.msg.len - 1); i++) { if (str[i] == '\0') str[i] = ' '; } return (str); } void dtrace_io(const struct connection *conn, const struct buffered_data *in, int io_out) { if (!io_out) { if (XENSTORE_MSG_ENABLED()) { char *mangled = mangle(conn, in); XENSTORE_MSG(in->hdr.msg.tx_id, conn->id, cred(conn), in->hdr.msg.type, mangled); } goto out; } switch (in->hdr.msg.type) { case XS_ERROR: if (XENSTORE_ERROR_ENABLED()) { char *mangled = mangle(conn, in); XENSTORE_ERROR(in->hdr.msg.tx_id, conn->id, cred(conn), mangled); } break; case XS_WATCH_EVENT: if (XENSTORE_WATCH_EVENT_ENABLED()) { char *mangled = mangle(conn, in); XENSTORE_WATCH_EVENT(conn->id, cred(conn), mangled); } break; default: if (XENSTORE_REPLY_ENABLED()) { char *mangled = mangle(conn, in); XENSTORE_REPLY(in->hdr.msg.tx_id, conn->id, cred(conn), in->hdr.msg.type, mangled); } break; } out: /* * 6589130 dtrace -G fails for certain tail-calls on x86 */ asm("nop"); } xen-4.9.2/tools/xenstore/xenstored_domain.c0000664000175000017500000005522113256712137017177 0ustar smbsmb/* Domain communications for Xen Store Daemon. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include #include #include "utils.h" #include "talloc.h" #include "xenstored_core.h" #include "xenstored_domain.h" #include "xenstored_transaction.h" #include "xenstored_watch.h" #include #include #include static xc_interface **xc_handle; xengnttab_handle **xgt_handle; static evtchn_port_t virq_port; xenevtchn_handle *xce_handle = NULL; struct domain { struct list_head list; /* The id of this domain */ unsigned int domid; /* Event channel port */ evtchn_port_t port; /* The remote end of the event channel, used only to validate repeated domain introductions. */ evtchn_port_t remote_port; /* The mfn associated with the event channel, used only to validate repeated domain introductions. */ unsigned long mfn; /* Domain path in store. */ char *path; /* Shared page. */ struct xenstore_domain_interface *interface; /* The connection associated with this. */ struct connection *conn; /* Have we noticed that this domain is shutdown? */ int shutdown; /* number of entry from this domain in the store */ int nbentry; /* number of watch for this domain */ int nbwatch; /* write rate limit */ wrl_creditt wrl_credit; /* [ -wrl_config_writecost, +_dburst ] */ struct wrl_timestampt wrl_timestamp; bool wrl_delay_logged; }; static LIST_HEAD(domains); static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod) { return ((prod - cons) <= XENSTORE_RING_SIZE); } static void *get_output_chunk(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod, char *buf, uint32_t *len) { *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod); if ((XENSTORE_RING_SIZE - (prod - cons)) < *len) *len = XENSTORE_RING_SIZE - (prod - cons); return buf + MASK_XENSTORE_IDX(prod); } static const void *get_input_chunk(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod, const char *buf, uint32_t *len) { *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons); if ((prod - cons) < *len) *len = prod - cons; return buf + MASK_XENSTORE_IDX(cons); } static int writechn(struct connection *conn, const void *data, unsigned int len) { uint32_t avail; void *dest; struct xenstore_domain_interface *intf = conn->domain->interface; XENSTORE_RING_IDX cons, prod; /* Must read indexes once, and before anything else, and verified. */ cons = intf->rsp_cons; prod = intf->rsp_prod; xen_mb(); if (!check_indexes(cons, prod)) { errno = EIO; return -1; } dest = get_output_chunk(cons, prod, intf->rsp, &avail); if (avail < len) len = avail; memcpy(dest, data, len); xen_mb(); intf->rsp_prod += len; xenevtchn_notify(xce_handle, conn->domain->port); return len; } static int readchn(struct connection *conn, void *data, unsigned int len) { uint32_t avail; const void *src; struct xenstore_domain_interface *intf = conn->domain->interface; XENSTORE_RING_IDX cons, prod; /* Must read indexes once, and before anything else, and verified. */ cons = intf->req_cons; prod = intf->req_prod; xen_mb(); if (!check_indexes(cons, prod)) { errno = EIO; return -1; } src = get_input_chunk(cons, prod, intf->req, &avail); if (avail < len) len = avail; memcpy(data, src, len); xen_mb(); intf->req_cons += len; xenevtchn_notify(xce_handle, conn->domain->port); return len; } static void *map_interface(domid_t domid, unsigned long mfn) { if (*xgt_handle != NULL) { /* this is the preferred method */ return xengnttab_map_grant_ref(*xgt_handle, domid, GNTTAB_RESERVED_XENSTORE, PROT_READ|PROT_WRITE); } else { return xc_map_foreign_range(*xc_handle, domid, XC_PAGE_SIZE, PROT_READ|PROT_WRITE, mfn); } } static void unmap_interface(void *interface) { if (*xgt_handle != NULL) xengnttab_unmap(*xgt_handle, interface, 1); else munmap(interface, XC_PAGE_SIZE); } static int destroy_domain(void *_domain) { struct domain *domain = _domain; list_del(&domain->list); if (domain->port) { if (xenevtchn_unbind(xce_handle, domain->port) == -1) eprintf("> Unbinding port %i failed!\n", domain->port); } if (domain->interface) { /* Domain 0 was mapped by dom0_init, so it must be unmapped using munmap() and not the grant unmap call. */ if (domain->domid == 0) unmap_xenbus(domain->interface); else unmap_interface(domain->interface); } fire_watches(NULL, domain, "@releaseDomain", false); wrl_domain_destroy(domain); return 0; } static void domain_cleanup(void) { xc_dominfo_t dominfo; struct domain *domain; int notify = 0; again: list_for_each_entry(domain, &domains, list) { if (xc_domain_getinfo(*xc_handle, domain->domid, 1, &dominfo) == 1 && dominfo.domid == domain->domid) { if ((dominfo.crashed || dominfo.shutdown) && !domain->shutdown) { domain->shutdown = 1; notify = 1; } if (!dominfo.dying) continue; } if (domain->conn) { talloc_unlink(talloc_autofree_context(), domain->conn); domain->conn = NULL; notify = 0; /* destroy_domain() fires the watch */ goto again; } } if (notify) fire_watches(NULL, NULL, "@releaseDomain", false); } /* We scan all domains rather than use the information given here. */ void handle_event(void) { evtchn_port_t port; if ((port = xenevtchn_pending(xce_handle)) == -1) barf_perror("Failed to read from event fd"); if (port == virq_port) domain_cleanup(); if (xenevtchn_unmask(xce_handle, port) == -1) barf_perror("Failed to write to event fd"); } bool domain_can_read(struct connection *conn) { struct xenstore_domain_interface *intf = conn->domain->interface; if (domain_is_unprivileged(conn) && conn->domain->wrl_credit < 0) return false; return (intf->req_cons != intf->req_prod); } static bool domid_is_unprivileged(unsigned int domid) { return domid != 0 && domid != priv_domid; } bool domain_is_unprivileged(struct connection *conn) { return conn && conn->domain && domid_is_unprivileged(conn->domain->domid); } bool domain_can_write(struct connection *conn) { struct xenstore_domain_interface *intf = conn->domain->interface; return ((intf->rsp_prod - intf->rsp_cons) != XENSTORE_RING_SIZE); } static char *talloc_domain_path(void *context, unsigned int domid) { return talloc_asprintf(context, "/local/domain/%u", domid); } static struct domain *new_domain(void *context, unsigned int domid, int port) { struct domain *domain; int rc; domain = talloc(context, struct domain); if (!domain) return NULL; domain->port = 0; domain->shutdown = 0; domain->domid = domid; domain->path = talloc_domain_path(domain, domid); if (!domain->path) return NULL; wrl_domain_new(domain); list_add(&domain->list, &domains); talloc_set_destructor(domain, destroy_domain); /* Tell kernel we're interested in this event. */ rc = xenevtchn_bind_interdomain(xce_handle, domid, port); if (rc == -1) return NULL; domain->port = rc; domain->conn = new_connection(writechn, readchn); if (!domain->conn) return NULL; domain->conn->domain = domain; domain->conn->id = domid; domain->remote_port = port; domain->nbentry = 0; domain->nbwatch = 0; return domain; } static struct domain *find_domain_by_domid(unsigned int domid) { struct domain *i; list_for_each_entry(i, &domains, list) { if (i->domid == domid) return i; } return NULL; } static void domain_conn_reset(struct domain *domain) { struct connection *conn = domain->conn; struct buffered_data *out; conn_delete_all_watches(conn); conn_delete_all_transactions(conn); while ((out = list_top(&conn->out_list, struct buffered_data, list))) { list_del(&out->list); talloc_free(out); } talloc_free(conn->in); domain->interface->req_cons = domain->interface->req_prod = 0; domain->interface->rsp_cons = domain->interface->rsp_prod = 0; } /* domid, mfn, evtchn, path */ int do_introduce(struct connection *conn, struct buffered_data *in) { struct domain *domain; char *vec[3]; unsigned int domid; unsigned long mfn; evtchn_port_t port; int rc; struct xenstore_domain_interface *interface; if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) return EINVAL; if (domain_is_unprivileged(conn) || !conn->can_write) return EACCES; domid = atoi(vec[0]); mfn = atol(vec[1]); port = atoi(vec[2]); /* Sanity check args. */ if (port <= 0) return EINVAL; domain = find_domain_by_domid(domid); if (domain == NULL) { interface = map_interface(domid, mfn); if (!interface) return errno; /* Hang domain off "in" until we're finished. */ domain = new_domain(in, domid, port); if (!domain) { rc = errno; unmap_interface(interface); return rc; } domain->interface = interface; domain->mfn = mfn; /* Now domain belongs to its connection. */ talloc_steal(domain->conn, domain); fire_watches(NULL, in, "@introduceDomain", false); } else if ((domain->mfn == mfn) && (domain->conn != conn)) { /* Use XS_INTRODUCE for recreating the xenbus event-channel. */ if (domain->port) xenevtchn_unbind(xce_handle, domain->port); rc = xenevtchn_bind_interdomain(xce_handle, domid, port); domain->port = (rc == -1) ? 0 : rc; domain->remote_port = port; } else return EINVAL; domain_conn_reset(domain); send_ack(conn, XS_INTRODUCE); return 0; } static struct domain *find_connected_domain(unsigned int domid) { struct domain *domain; domain = find_domain_by_domid(domid); if (!domain) return ERR_PTR(-ENOENT); if (!domain->conn) return ERR_PTR(-EINVAL); return domain; } int do_set_target(struct connection *conn, struct buffered_data *in) { char *vec[2]; unsigned int domid, tdomid; struct domain *domain, *tdomain; if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) return EINVAL; if (domain_is_unprivileged(conn) || !conn->can_write) return EACCES; domid = atoi(vec[0]); tdomid = atoi(vec[1]); domain = find_connected_domain(domid); if (IS_ERR(domain)) return -PTR_ERR(domain); tdomain = find_connected_domain(tdomid); if (IS_ERR(tdomain)) return -PTR_ERR(tdomain); talloc_reference(domain->conn, tdomain->conn); domain->conn->target = tdomain->conn; send_ack(conn, XS_SET_TARGET); return 0; } static struct domain *onearg_domain(struct connection *conn, struct buffered_data *in) { const char *domid_str = onearg(in); unsigned int domid; if (!domid_str) return ERR_PTR(-EINVAL); domid = atoi(domid_str); if (!domid) return ERR_PTR(-EINVAL); if (domain_is_unprivileged(conn)) return ERR_PTR(-EACCES); return find_connected_domain(domid); } /* domid */ int do_release(struct connection *conn, struct buffered_data *in) { struct domain *domain; domain = onearg_domain(conn, in); if (IS_ERR(domain)) return -PTR_ERR(domain); talloc_free(domain->conn); send_ack(conn, XS_RELEASE); return 0; } int do_resume(struct connection *conn, struct buffered_data *in) { struct domain *domain; domain = onearg_domain(conn, in); if (IS_ERR(domain)) return -PTR_ERR(domain); domain->shutdown = 0; send_ack(conn, XS_RESUME); return 0; } int do_get_domain_path(struct connection *conn, struct buffered_data *in) { char *path; const char *domid_str = onearg(in); if (!domid_str) return EINVAL; path = talloc_domain_path(conn, atoi(domid_str)); if (!path) return errno; send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1); talloc_free(path); return 0; } int do_is_domain_introduced(struct connection *conn, struct buffered_data *in) { int result; unsigned int domid; const char *domid_str = onearg(in); if (!domid_str) return EINVAL; domid = atoi(domid_str); if (domid == DOMID_SELF) result = 1; else result = (find_domain_by_domid(domid) != NULL); send_reply(conn, XS_IS_DOMAIN_INTRODUCED, result ? "T" : "F", 2); return 0; } /* Allow guest to reset all watches */ int do_reset_watches(struct connection *conn, struct buffered_data *in) { conn_delete_all_watches(conn); conn_delete_all_transactions(conn); send_ack(conn, XS_RESET_WATCHES); return 0; } static int close_xc_handle(void *_handle) { xc_interface_close(*(xc_interface**)_handle); return 0; } static int close_xgt_handle(void *_handle) { xengnttab_close(*(xengnttab_handle **)_handle); return 0; } /* Returns the implicit path of a connection (only domains have this) */ const char *get_implicit_path(const struct connection *conn) { if (!conn->domain) return "/local/domain/0"; return conn->domain->path; } /* Restore existing connections. */ void restore_existing_connections(void) { } static int dom0_init(void) { evtchn_port_t port; struct domain *dom0; port = xenbus_evtchn(); if (port == -1) return -1; dom0 = new_domain(NULL, xenbus_master_domid(), port); if (dom0 == NULL) return -1; dom0->interface = xenbus_map(); if (dom0->interface == NULL) return -1; talloc_steal(dom0->conn, dom0); xenevtchn_notify(xce_handle, dom0->port); return 0; } void domain_init(void) { int rc; xc_handle = talloc(talloc_autofree_context(), xc_interface*); if (!xc_handle) barf_perror("Failed to allocate domain handle"); *xc_handle = xc_interface_open(0,0,0); if (!*xc_handle) barf_perror("Failed to open connection to hypervisor"); talloc_set_destructor(xc_handle, close_xc_handle); xgt_handle = talloc(talloc_autofree_context(), xengnttab_handle*); if (!xgt_handle) barf_perror("Failed to allocate domain gnttab handle"); *xgt_handle = xengnttab_open(NULL, 0); if (*xgt_handle == NULL) xprintf("WARNING: Failed to open connection to gnttab\n"); else talloc_set_destructor(xgt_handle, close_xgt_handle); xce_handle = xenevtchn_open(NULL, 0); if (xce_handle == NULL) barf_perror("Failed to open evtchn device"); if (dom0_init() != 0) barf_perror("Failed to initialize dom0 state"); if ((rc = xenevtchn_bind_virq(xce_handle, VIRQ_DOM_EXC)) == -1) barf_perror("Failed to bind to domain exception virq port"); virq_port = rc; } void domain_entry_inc(struct connection *conn, struct node *node) { struct domain *d; if (!conn) return; if (node->perms && node->perms[0].id != conn->id) { if (conn->transaction) { transaction_entry_inc(conn->transaction, node->perms[0].id); } else { d = find_domain_by_domid(node->perms[0].id); if (d) d->nbentry++; } } else if (conn->domain) { if (conn->transaction) { transaction_entry_inc(conn->transaction, conn->domain->domid); } else { conn->domain->nbentry++; } } } void domain_entry_dec(struct connection *conn, struct node *node) { struct domain *d; if (!conn) return; if (node->perms && node->perms[0].id != conn->id) { if (conn->transaction) { transaction_entry_dec(conn->transaction, node->perms[0].id); } else { d = find_domain_by_domid(node->perms[0].id); if (d && d->nbentry) d->nbentry--; } } else if (conn->domain && conn->domain->nbentry) { if (conn->transaction) { transaction_entry_dec(conn->transaction, conn->domain->domid); } else { conn->domain->nbentry--; } } } int domain_entry_fix(unsigned int domid, int num, bool update) { struct domain *d; int cnt; d = find_domain_by_domid(domid); if (!d) return 0; cnt = d->nbentry + num; if (cnt < 0) cnt = 0; if (update) d->nbentry = cnt; return domid_is_unprivileged(domid) ? cnt : 0; } int domain_entry(struct connection *conn) { return (domain_is_unprivileged(conn)) ? conn->domain->nbentry : 0; } void domain_watch_inc(struct connection *conn) { if (!conn || !conn->domain) return; conn->domain->nbwatch++; } void domain_watch_dec(struct connection *conn) { if (!conn || !conn->domain) return; if (conn->domain->nbwatch) conn->domain->nbwatch--; } int domain_watch(struct connection *conn) { return (domain_is_unprivileged(conn)) ? conn->domain->nbwatch : 0; } static wrl_creditt wrl_config_writecost = WRL_FACTOR; static wrl_creditt wrl_config_rate = WRL_RATE * WRL_FACTOR; static wrl_creditt wrl_config_dburst = WRL_DBURST * WRL_FACTOR; static wrl_creditt wrl_config_gburst = WRL_GBURST * WRL_FACTOR; static wrl_creditt wrl_config_newdoms_dburst = WRL_DBURST * WRL_NEWDOMS * WRL_FACTOR; long wrl_ntransactions; static long wrl_ndomains; static wrl_creditt wrl_reserve; /* [-wrl_config_newdoms_dburst, +_gburst ] */ static time_t wrl_log_last_warning; /* 0: no previous warning */ void wrl_gettime_now(struct wrl_timestampt *now_wt) { struct timespec now_ts; int r; r = clock_gettime(CLOCK_MONOTONIC, &now_ts); if (r) barf_perror("Could not find time (clock_gettime failed)"); now_wt->sec = now_ts.tv_sec; now_wt->msec = now_ts.tv_nsec / 1000000; } static void wrl_xfer_credit(wrl_creditt *debit, wrl_creditt debit_floor, wrl_creditt *credit, wrl_creditt credit_ceil) /* * Transfers zero or more credit from "debit" to "credit". * Transfers as much as possible while maintaining * debit >= debit_floor and credit <= credit_ceil. * (If that's violated already, does nothing.) * * Sufficient conditions to avoid overflow, either of: * |every argument| <= 0x3fffffff * |every argument| <= 1E9 * |every argument| <= WRL_CREDIT_MAX * (And this condition is preserved.) */ { wrl_creditt xfer = MIN( *debit - debit_floor, credit_ceil - *credit ); if (xfer > 0) { *debit -= xfer; *credit += xfer; } } void wrl_domain_new(struct domain *domain) { domain->wrl_credit = 0; wrl_gettime_now(&domain->wrl_timestamp); wrl_ndomains++; /* Steal up to DBURST from the reserve */ wrl_xfer_credit(&wrl_reserve, -wrl_config_newdoms_dburst, &domain->wrl_credit, wrl_config_dburst); } void wrl_domain_destroy(struct domain *domain) { wrl_ndomains--; /* * Don't bother recalculating domain's credit - this just * means we don't give the reserve the ending domain's credit * for time elapsed since last update. */ wrl_xfer_credit(&domain->wrl_credit, 0, &wrl_reserve, wrl_config_dburst); } void wrl_credit_update(struct domain *domain, struct wrl_timestampt now) { /* * We want to calculate * credit += (now - timestamp) * RATE / ndoms; * But we want it to saturate, and to avoid floating point. * To avoid rounding errors from constantly adding small * amounts of credit, we only add credit for whole milliseconds. */ long seconds = now.sec - domain->wrl_timestamp.sec; long milliseconds = now.msec - domain->wrl_timestamp.msec; long msec; int64_t denom, num; wrl_creditt surplus; seconds = MIN(seconds, 1000*1000); /* arbitrary, prevents overflow */ msec = seconds * 1000 + milliseconds; if (msec < 0) /* shouldn't happen with CLOCK_MONOTONIC */ msec = 0; /* 32x32 -> 64 cannot overflow */ denom = (int64_t)msec * wrl_config_rate; num = (int64_t)wrl_ndomains * 1000; /* denom / num <= 1E6 * wrl_config_rate, so with reasonable wrl_config_rate, denom / num << 2^64 */ /* at last! */ domain->wrl_credit = MIN( (int64_t)domain->wrl_credit + denom / num, WRL_CREDIT_MAX ); /* (maybe briefly violating the DBURST cap on wrl_credit) */ /* maybe take from the reserve to make us nonnegative */ wrl_xfer_credit(&wrl_reserve, 0, &domain->wrl_credit, 0); /* return any surplus (over DBURST) to the reserve */ surplus = 0; wrl_xfer_credit(&domain->wrl_credit, wrl_config_dburst, &surplus, WRL_CREDIT_MAX); wrl_xfer_credit(&surplus, 0, &wrl_reserve, wrl_config_gburst); /* surplus is now implicitly discarded */ domain->wrl_timestamp = now; trace("wrl: dom %4d %6ld msec %9ld credit %9ld reserve" " %9ld discard\n", domain->domid, msec, (long)domain->wrl_credit, (long)wrl_reserve, (long)surplus); } void wrl_check_timeout(struct domain *domain, struct wrl_timestampt now, int *ptimeout) { uint64_t num, denom; int wakeup; wrl_credit_update(domain, now); if (domain->wrl_credit >= 0) /* not blocked */ return; if (!*ptimeout) /* already decided on immediate wakeup, so no need to calculate our timeout */ return; /* calculate wakeup = now + -credit / (RATE / ndoms); */ /* credit cannot go more -ve than one transaction, * so the first multiplication cannot overflow even 32-bit */ num = (uint64_t)(-domain->wrl_credit * 1000) * wrl_ndomains; denom = wrl_config_rate; wakeup = MIN( num / denom /* uint64_t */, INT_MAX ); if (*ptimeout==-1 || wakeup < *ptimeout) *ptimeout = wakeup; trace("wrl: domain %u credit=%ld (reserve=%ld) SLEEPING for %d\n", domain->domid, (long)domain->wrl_credit, (long)wrl_reserve, wakeup); } #define WRL_LOG(now, ...) \ (syslog(LOG_WARNING, "write rate limit: " __VA_ARGS__)) void wrl_apply_debit_actual(struct domain *domain) { struct wrl_timestampt now; if (!domain) /* sockets escape the write rate limit */ return; wrl_gettime_now(&now); wrl_credit_update(domain, now); domain->wrl_credit -= wrl_config_writecost; trace("wrl: domain %u credit=%ld (reserve=%ld)\n", domain->domid, (long)domain->wrl_credit, (long)wrl_reserve); if (domain->wrl_credit < 0) { if (!domain->wrl_delay_logged) { domain->wrl_delay_logged = true; WRL_LOG(now, "domain %ld is affected", (long)domain->domid); } else if (!wrl_log_last_warning) { WRL_LOG(now, "rate limiting restarts"); } wrl_log_last_warning = now.sec; } } void wrl_log_periodic(struct wrl_timestampt now) { if (wrl_log_last_warning && (now.sec - wrl_log_last_warning) > WRL_LOGEVERY) { WRL_LOG(now, "not in force recently"); wrl_log_last_warning = 0; } } void wrl_apply_debit_direct(struct connection *conn) { if (!conn) /* some writes are generated internally */ return; if (conn->transaction) /* these are accounted for when the transaction ends */ return; if (!wrl_ntransactions) /* we don't conflict with anyone */ return; wrl_apply_debit_actual(conn->domain); } void wrl_apply_debit_trans_commit(struct connection *conn) { if (wrl_ntransactions <= 1) /* our own transaction appears in the counter */ return; wrl_apply_debit_actual(conn->domain); } /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/xenstore/xenstored_core.c0000664000175000017500000014376213256712137016670 0ustar smbsmb/* Simple prototype Xen Store Daemon providing simple tree-like database. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #ifndef NO_SOCKETS #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "list.h" #include "talloc.h" #include "xenstore_lib.h" #include "xenstored_core.h" #include "xenstored_watch.h" #include "xenstored_transaction.h" #include "xenstored_domain.h" #include "xenstored_control.h" #include "tdb.h" #ifndef NO_SOCKETS #if defined(HAVE_SYSTEMD) #define XEN_SYSTEMD_ENABLED 1 #endif #endif #if defined(XEN_SYSTEMD_ENABLED) #include #endif extern xenevtchn_handle *xce_handle; /* in xenstored_domain.c */ static int xce_pollfd_idx = -1; static struct pollfd *fds; static unsigned int current_array_size; static unsigned int nr_fds; #define ROUNDUP(_x, _w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1)) static bool verbose = false; LIST_HEAD(connections); int tracefd = -1; static bool recovery = true; static int reopen_log_pipe[2]; static int reopen_log_pipe0_pollfd_idx = -1; char *tracefile = NULL; TDB_CONTEXT *tdb_ctx = NULL; static const char *sockmsg_string(enum xsd_sockmsg_type type); #define log(...) \ do { \ char *s = talloc_asprintf(NULL, __VA_ARGS__); \ if (s) { \ trace("%s\n", s); \ syslog(LOG_ERR, "%s", s); \ talloc_free(s); \ } else { \ trace("talloc failure during logging\n"); \ syslog(LOG_ERR, "talloc failure during logging\n"); \ } \ } while (0) int quota_nb_entry_per_domain = 1000; int quota_nb_watch_per_domain = 128; int quota_max_entry_size = 2048; /* 2K */ int quota_max_transaction = 10; void trace(const char *fmt, ...) { va_list arglist; char *str; char sbuf[1024]; int ret, dummy; if (tracefd < 0) return; /* try to use a static buffer */ va_start(arglist, fmt); ret = vsnprintf(sbuf, 1024, fmt, arglist); va_end(arglist); if (ret <= 1024) { dummy = write(tracefd, sbuf, ret); return; } /* fail back to dynamic allocation */ va_start(arglist, fmt); str = talloc_vasprintf(NULL, fmt, arglist); va_end(arglist); if (str) { dummy = write(tracefd, str, strlen(str)); talloc_free(str); } } static void trace_io(const struct connection *conn, const struct buffered_data *data, int out) { unsigned int i; time_t now; struct tm *tm; #ifdef HAVE_DTRACE dtrace_io(conn, data, out); #endif if (tracefd < 0) return; now = time(NULL); tm = localtime(&now); trace("%s %p %04d%02d%02d %02d:%02d:%02d %s (", out ? "OUT" : "IN", conn, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, sockmsg_string(data->hdr.msg.type)); for (i = 0; i < data->hdr.msg.len; i++) trace("%c", (data->buffer[i] != '\0') ? data->buffer[i] : ' '); trace(")\n"); } void trace_create(const void *data, const char *type) { trace("CREATE %s %p\n", type, data); } void trace_destroy(const void *data, const char *type) { trace("DESTROY %s %p\n", type, data); } /** * Signal handler for SIGHUP, which requests that the trace log is reopened * (in the main loop). A single byte is written to reopen_log_pipe, to awaken * the poll() in the main loop. */ static void trigger_reopen_log(int signal __attribute__((unused))) { char c = 'A'; int dummy; dummy = write(reopen_log_pipe[1], &c, 1); } void close_log(void) { if (tracefd >= 0) close(tracefd); tracefd = -1; } void reopen_log(void) { if (tracefile) { close_log(); tracefd = open(tracefile, O_WRONLY|O_CREAT|O_APPEND, 0600); if (tracefd < 0) perror("Could not open tracefile"); else trace("\n***\n"); } } static bool write_messages(struct connection *conn) { int ret; struct buffered_data *out; out = list_top(&conn->out_list, struct buffered_data, list); if (out == NULL) return true; if (out->inhdr) { if (verbose) xprintf("Writing msg %s (%.*s) out to %p\n", sockmsg_string(out->hdr.msg.type), out->hdr.msg.len, out->buffer, conn); ret = conn->write(conn, out->hdr.raw + out->used, sizeof(out->hdr) - out->used); if (ret < 0) return false; out->used += ret; if (out->used < sizeof(out->hdr)) return true; out->inhdr = false; out->used = 0; /* Second write might block if non-zero. */ if (out->hdr.msg.len && !conn->domain) return true; } ret = conn->write(conn, out->buffer + out->used, out->hdr.msg.len - out->used); if (ret < 0) return false; out->used += ret; if (out->used != out->hdr.msg.len) return true; trace_io(conn, out, 1); list_del(&out->list); talloc_free(out); return true; } static int destroy_conn(void *_conn) { struct connection *conn = _conn; /* Flush outgoing if possible, but don't block. */ if (!conn->domain) { struct pollfd pfd; pfd.fd = conn->fd; pfd.events = POLLOUT; while (!list_empty(&conn->out_list) && poll(&pfd, 1, 0) == 1) if (!write_messages(conn)) break; close(conn->fd); } if (conn->target) talloc_unlink(conn, conn->target); list_del(&conn->list); trace_destroy(conn, "connection"); return 0; } /* This function returns index inside the array if succeed, -1 if fail */ static int set_fd(int fd, short events) { int ret; if (current_array_size < nr_fds + 1) { struct pollfd *new_fds = NULL; unsigned long newsize; /* Round up to 2^8 boundary, in practice this just * make newsize larger than current_array_size. */ newsize = ROUNDUP(nr_fds + 1, 8); new_fds = realloc(fds, sizeof(struct pollfd)*newsize); if (!new_fds) goto fail; fds = new_fds; memset(&fds[0] + current_array_size, 0, sizeof(struct pollfd ) * (newsize-current_array_size)); current_array_size = newsize; } fds[nr_fds].fd = fd; fds[nr_fds].events = events; ret = nr_fds; nr_fds++; return ret; fail: syslog(LOG_ERR, "realloc failed, ignoring fd %d\n", fd); return -1; } static void initialize_fds(int sock, int *p_sock_pollfd_idx, int ro_sock, int *p_ro_sock_pollfd_idx, int *ptimeout) { struct connection *conn; struct wrl_timestampt now; if (fds) memset(fds, 0, sizeof(struct pollfd) * current_array_size); nr_fds = 0; *ptimeout = -1; if (sock != -1) *p_sock_pollfd_idx = set_fd(sock, POLLIN|POLLPRI); if (ro_sock != -1) *p_ro_sock_pollfd_idx = set_fd(ro_sock, POLLIN|POLLPRI); if (reopen_log_pipe[0] != -1) reopen_log_pipe0_pollfd_idx = set_fd(reopen_log_pipe[0], POLLIN|POLLPRI); if (xce_handle != NULL) xce_pollfd_idx = set_fd(xenevtchn_fd(xce_handle), POLLIN|POLLPRI); wrl_gettime_now(&now); wrl_log_periodic(now); list_for_each_entry(conn, &connections, list) { if (conn->domain) { wrl_check_timeout(conn->domain, now, ptimeout); if (domain_can_read(conn) || (domain_can_write(conn) && !list_empty(&conn->out_list))) *ptimeout = 0; } else { short events = POLLIN|POLLPRI; if (!list_empty(&conn->out_list)) events |= POLLOUT; conn->pollfd_idx = set_fd(conn->fd, events); } } } /* * If it fails, returns NULL and sets errno. * Temporary memory allocations will be done with ctx. */ static struct node *read_node(struct connection *conn, const void *ctx, const char *name) { TDB_DATA key, data; struct xs_tdb_record_hdr *hdr; struct node *node; node = talloc(ctx, struct node); if (!node) { errno = ENOMEM; return NULL; } node->name = talloc_strdup(node, name); if (!node->name) { talloc_free(node); errno = ENOMEM; return NULL; } if (transaction_prepend(conn, name, &key)) return NULL; data = tdb_fetch(tdb_ctx, key); if (data.dptr == NULL) { if (tdb_error(tdb_ctx) == TDB_ERR_NOEXIST) { node->generation = NO_GENERATION; access_node(conn, node, NODE_ACCESS_READ, NULL); errno = ENOENT; } else { log("TDB error on read: %s", tdb_errorstr(tdb_ctx)); errno = EIO; } talloc_free(node); return NULL; } node->parent = NULL; talloc_steal(node, data.dptr); /* Datalen, childlen, number of permissions */ hdr = (void *)data.dptr; node->generation = hdr->generation; node->num_perms = hdr->num_perms; node->datalen = hdr->datalen; node->childlen = hdr->childlen; /* Permissions are struct xs_permissions. */ node->perms = hdr->perms; /* Data is binary blob (usually ascii, no nul). */ node->data = node->perms + node->num_perms; /* Children is strings, nul separated. */ node->children = node->data + node->datalen; access_node(conn, node, NODE_ACCESS_READ, NULL); return node; } int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) { TDB_DATA data; void *p; struct xs_tdb_record_hdr *hdr; data.dsize = sizeof(*hdr) + node->num_perms*sizeof(node->perms[0]) + node->datalen + node->childlen; if (domain_is_unprivileged(conn) && data.dsize >= quota_max_entry_size) { errno = ENOSPC; return errno; } data.dptr = talloc_size(node, data.dsize); hdr = (void *)data.dptr; hdr->generation = node->generation; hdr->num_perms = node->num_perms; hdr->datalen = node->datalen; hdr->childlen = node->childlen; memcpy(hdr->perms, node->perms, node->num_perms*sizeof(node->perms[0])); p = hdr->perms + node->num_perms; memcpy(p, node->data, node->datalen); p += node->datalen; memcpy(p, node->children, node->childlen); /* TDB should set errno, but doesn't even set ecode AFAICT. */ if (tdb_store(tdb_ctx, *key, data, TDB_REPLACE) != 0) { corrupt(conn, "Write of %s failed", key->dptr); errno = EIO; return errno; } return 0; } static int write_node(struct connection *conn, struct node *node) { TDB_DATA key; if (access_node(conn, node, NODE_ACCESS_WRITE, &key)) return errno; return write_node_raw(conn, &key, node); } static enum xs_perm_type perm_for_conn(struct connection *conn, struct xs_permissions *perms, unsigned int num) { unsigned int i; enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER; if (!conn->can_write) mask &= ~XS_PERM_WRITE; /* Owners and tools get it all... */ if (!domain_is_unprivileged(conn) || perms[0].id == conn->id || (conn->target && perms[0].id == conn->target->id)) return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask; for (i = 1; i < num; i++) if (perms[i].id == conn->id || (conn->target && perms[i].id == conn->target->id)) return perms[i].perms & mask; return perms[0].perms & mask; } /* * Get name of node parent. * Temporary memory allocations are done with ctx. */ static char *get_parent(const void *ctx, const char *node) { char *parent; char *slash = strrchr(node + 1, '/'); parent = slash ? talloc_asprintf(ctx, "%.*s", (int)(slash - node), node) : talloc_strdup(ctx, "/"); if (!parent) errno = ENOMEM; return parent; } /* * What do parents say? * Temporary memory allocations are done with ctx. */ static int ask_parents(struct connection *conn, const void *ctx, const char *name, enum xs_perm_type *perm) { struct node *node; do { name = get_parent(ctx, name); if (!name) return errno; node = read_node(conn, ctx, name); if (node) break; if (errno == ENOMEM) return errno; } while (!streq(name, "/")); /* No permission at root? We're in trouble. */ if (!node) { corrupt(conn, "No permissions file at root"); *perm = XS_PERM_NONE; return 0; } *perm = perm_for_conn(conn, node->perms, node->num_perms); return 0; } /* * We have a weird permissions system. You can allow someone into a * specific node without allowing it in the parents. If it's going to * fail, however, we don't want the errno to indicate any information * about the node. * Temporary memory allocations are done with ctx. */ static int errno_from_parents(struct connection *conn, const void *ctx, const char *node, int errnum, enum xs_perm_type perm) { enum xs_perm_type parent_perm = XS_PERM_NONE; /* We always tell them about memory failures. */ if (errnum == ENOMEM) return errnum; if (ask_parents(conn, ctx, node, &parent_perm)) return errno; if (parent_perm & perm) return errnum; return EACCES; } /* * If it fails, returns NULL and sets errno. * Temporary memory allocations are done with ctx. */ struct node *get_node(struct connection *conn, const void *ctx, const char *name, enum xs_perm_type perm) { struct node *node; if (!name || !is_valid_nodename(name)) { errno = EINVAL; return NULL; } node = read_node(conn, ctx, name); /* If we don't have permission, we don't have node. */ if (node) { if ((perm_for_conn(conn, node->perms, node->num_perms) & perm) != perm) { errno = EACCES; node = NULL; } } /* Clean up errno if they weren't supposed to know. */ if (!node && errno != ENOMEM) errno = errno_from_parents(conn, ctx, name, errno, perm); return node; } static struct buffered_data *new_buffer(void *ctx) { struct buffered_data *data; data = talloc_zero(ctx, struct buffered_data); if (data == NULL) return NULL; data->inhdr = true; return data; } /* Return length of string (including nul) at this offset. * If there is no nul, returns 0 for failure. */ static unsigned int get_string(const struct buffered_data *data, unsigned int offset) { const char *nul; if (offset >= data->used) return 0; nul = memchr(data->buffer + offset, 0, data->used - offset); if (!nul) return 0; return nul - (data->buffer + offset) + 1; } /* Break input into vectors, return the number, fill in up to num of them. * Always returns the actual number of nuls in the input. Stores the * positions of the starts of the nul-terminated strings in vec. * Callers who use this and then rely only on vec[] will * ignore any data after the final nul. */ unsigned int get_strings(struct buffered_data *data, char *vec[], unsigned int num) { unsigned int off, i, len; off = i = 0; while ((len = get_string(data, off)) != 0) { if (i < num) vec[i] = data->buffer + off; i++; off += len; } return i; } static void send_error(struct connection *conn, int error) { unsigned int i; for (i = 0; error != xsd_errors[i].errnum; i++) { if (i == ARRAY_SIZE(xsd_errors) - 1) { eprintf("xenstored: error %i untranslatable", error); i = 0; /* EINVAL */ break; } } send_reply(conn, XS_ERROR, xsd_errors[i].errstring, strlen(xsd_errors[i].errstring) + 1); } void send_reply(struct connection *conn, enum xsd_sockmsg_type type, const void *data, unsigned int len) { struct buffered_data *bdata; if ( len > XENSTORE_PAYLOAD_MAX ) { send_error(conn, E2BIG); return; } /* Replies reuse the request buffer, events need a new one. */ if (type != XS_WATCH_EVENT) { bdata = conn->in; bdata->inhdr = true; bdata->used = 0; conn->in = NULL; } else { /* Message is a child of the connection for auto-cleanup. */ bdata = new_buffer(conn); /* * Allocation failure here is unfortunate: we have no way to * tell anybody about it. */ if (!bdata) return; } if (len <= DEFAULT_BUFFER_SIZE) bdata->buffer = bdata->default_buffer; else bdata->buffer = talloc_array(bdata, char, len); if (!bdata->buffer) { if (type == XS_WATCH_EVENT) { /* Same as above: no way to tell someone. */ talloc_free(bdata); return; } /* re-establish request buffer for sending ENOMEM. */ conn->in = bdata; send_error(conn, ENOMEM); return; } /* Update relevant header fields and fill in the message body. */ bdata->hdr.msg.type = type; bdata->hdr.msg.len = len; memcpy(bdata->buffer, data, len); /* Queue for later transmission. */ list_add_tail(&bdata->list, &conn->out_list); return; } /* Some routines (write, mkdir, etc) just need a non-error return */ void send_ack(struct connection *conn, enum xsd_sockmsg_type type) { send_reply(conn, type, "OK", sizeof("OK")); } static bool valid_chars(const char *node) { /* Nodes can have lots of crap. */ return (strspn(node, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789-/_@") == strlen(node)); } bool is_valid_nodename(const char *node) { /* Must start in /. */ if (!strstarts(node, "/")) return false; /* Cannot end in / (unless it's just "/"). */ if (strends(node, "/") && !streq(node, "/")) return false; /* No double //. */ if (strstr(node, "//")) return false; if (strlen(node) > XENSTORE_ABS_PATH_MAX) return false; return valid_chars(node); } /* We expect one arg in the input: return NULL otherwise. * The payload must contain exactly one nul, at the end. */ const char *onearg(struct buffered_data *in) { if (!in->used || get_string(in, 0) != in->used) return NULL; return in->buffer; } static char *perms_to_strings(const void *ctx, struct xs_permissions *perms, unsigned int num, unsigned int *len) { unsigned int i; char *strings = NULL; char buffer[MAX_STRLEN(unsigned int) + 1]; for (*len = 0, i = 0; i < num; i++) { if (!xs_perm_to_string(&perms[i], buffer, sizeof(buffer))) return NULL; strings = talloc_realloc(ctx, strings, char, *len + strlen(buffer) + 1); if (!strings) return NULL; strcpy(strings + *len, buffer); *len += strlen(buffer) + 1; } return strings; } char *canonicalize(struct connection *conn, const void *ctx, const char *node) { const char *prefix; if (!node || (node[0] == '/') || (node[0] == '@')) return (char *)node; prefix = get_implicit_path(conn); if (prefix) return talloc_asprintf(ctx, "%s/%s", prefix, node); return (char *)node; } static struct node *get_node_canonicalized(struct connection *conn, const void *ctx, const char *name, char **canonical_name, enum xs_perm_type perm) { char *tmp_name; if (!canonical_name) canonical_name = &tmp_name; *canonical_name = canonicalize(conn, ctx, name); return get_node(conn, ctx, *canonical_name, perm); } static int send_directory(struct connection *conn, struct buffered_data *in) { struct node *node; node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); if (!node) return errno; send_reply(conn, XS_DIRECTORY, node->children, node->childlen); return 0; } static int send_directory_part(struct connection *conn, struct buffered_data *in) { unsigned int off, len, maxlen, genlen; char *child, *data; struct node *node; char gen[24]; if (xs_count_strings(in->buffer, in->used) != 2) return EINVAL; /* First arg is node name. */ node = get_node_canonicalized(conn, in, in->buffer, NULL, XS_PERM_READ); if (!node) return errno; /* Second arg is childlist offset. */ off = atoi(in->buffer + strlen(in->buffer) + 1); genlen = snprintf(gen, sizeof(gen), "%"PRIu64, node->generation) + 1; /* Offset behind list: just return a list with an empty string. */ if (off >= node->childlen) { gen[genlen] = 0; send_reply(conn, XS_DIRECTORY_PART, gen, genlen + 1); return 0; } len = 0; maxlen = XENSTORE_PAYLOAD_MAX - genlen - 1; child = node->children + off; while (len + strlen(child) < maxlen) { len += strlen(child) + 1; child += strlen(child) + 1; if (off + len == node->childlen) break; } data = talloc_array(in, char, genlen + len + 1); if (!data) return ENOMEM; memcpy(data, gen, genlen); memcpy(data + genlen, node->children + off, len); if (off + len == node->childlen) { data[genlen + len] = 0; len++; } send_reply(conn, XS_DIRECTORY_PART, data, genlen + len); return 0; } static int do_read(struct connection *conn, struct buffered_data *in) { struct node *node; node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); if (!node) return errno; send_reply(conn, XS_READ, node->data, node->datalen); return 0; } static void delete_node_single(struct connection *conn, struct node *node) { TDB_DATA key; if (access_node(conn, node, NODE_ACCESS_DELETE, &key)) return; if (tdb_delete(tdb_ctx, key) != 0) { corrupt(conn, "Could not delete '%s'", node->name); return; } domain_entry_dec(conn, node); } /* Must not be / */ static char *basename(const char *name) { return strrchr(name, '/') + 1; } static struct node *construct_node(struct connection *conn, const void *ctx, const char *name) { const char *base; unsigned int baselen; struct node *parent, *node; char *children, *parentname = get_parent(ctx, name); if (!parentname) return NULL; /* If parent doesn't exist, create it. */ parent = read_node(conn, parentname, parentname); if (!parent) parent = construct_node(conn, ctx, parentname); if (!parent) return NULL; if (domain_entry(conn) >= quota_nb_entry_per_domain) { errno = ENOSPC; return NULL; } /* Add child to parent. */ base = basename(name); baselen = strlen(base) + 1; children = talloc_array(ctx, char, parent->childlen + baselen); if (!children) goto nomem; memcpy(children, parent->children, parent->childlen); memcpy(children + parent->childlen, base, baselen); parent->children = children; parent->childlen += baselen; /* Allocate node */ node = talloc(ctx, struct node); if (!node) goto nomem; node->name = talloc_strdup(node, name); if (!node->name) goto nomem; /* Inherit permissions, except unprivileged domains own what they create */ node->num_perms = parent->num_perms; node->perms = talloc_memdup(node, parent->perms, node->num_perms * sizeof(node->perms[0])); if (!node->perms) goto nomem; if (domain_is_unprivileged(conn)) node->perms[0].id = conn->id; /* No children, no data */ node->children = node->data = NULL; node->childlen = node->datalen = 0; node->parent = parent; domain_entry_inc(conn, node); return node; nomem: errno = ENOMEM; return NULL; } static int destroy_node(void *_node) { struct node *node = _node; TDB_DATA key; if (streq(node->name, "/")) corrupt(NULL, "Destroying root node!"); key.dptr = (void *)node->name; key.dsize = strlen(node->name); tdb_delete(tdb_ctx, key); return 0; } static struct node *create_node(struct connection *conn, const void *ctx, const char *name, void *data, unsigned int datalen) { struct node *node, *i; node = construct_node(conn, ctx, name); if (!node) return NULL; node->data = data; node->datalen = datalen; /* We write out the nodes down, setting destructor in case * something goes wrong. */ for (i = node; i; i = i->parent) { if (write_node(conn, i)) { domain_entry_dec(conn, i); return NULL; } talloc_set_destructor(i, destroy_node); } /* OK, now remove destructors so they stay around */ for (i = node; i; i = i->parent) talloc_set_destructor(i, NULL); return node; } /* path, data... */ static int do_write(struct connection *conn, struct buffered_data *in) { unsigned int offset, datalen; struct node *node; char *vec[1] = { NULL }; /* gcc4 + -W + -Werror fucks code. */ char *name; /* Extra "strings" can be created by binary data. */ if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) return EINVAL; offset = strlen(vec[0]) + 1; datalen = in->used - offset; node = get_node_canonicalized(conn, in, vec[0], &name, XS_PERM_WRITE); if (!node) { /* No permissions, invalid input? */ if (errno != ENOENT) return errno; node = create_node(conn, in, name, in->buffer + offset, datalen); if (!node) return errno; } else { node->data = in->buffer + offset; node->datalen = datalen; if (write_node(conn, node)) return errno; } fire_watches(conn, in, name, false); send_ack(conn, XS_WRITE); return 0; } static int do_mkdir(struct connection *conn, struct buffered_data *in) { struct node *node; char *name; node = get_node_canonicalized(conn, in, onearg(in), &name, XS_PERM_WRITE); /* If it already exists, fine. */ if (!node) { /* No permissions? */ if (errno != ENOENT) return errno; node = create_node(conn, in, name, NULL, 0); if (!node) return errno; fire_watches(conn, in, name, false); } send_ack(conn, XS_MKDIR); return 0; } static void delete_node(struct connection *conn, struct node *node) { unsigned int i; char *name; /* Delete self, then delete children. If we crash, then the worst that can happen is the children will continue to take up space, but will otherwise be unreachable. */ delete_node_single(conn, node); /* Delete children, too. */ for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { struct node *child; name = talloc_asprintf(node, "%s/%s", node->name, node->children + i); child = name ? read_node(conn, node, name) : NULL; if (child) { delete_node(conn, child); } else { trace("delete_node: Error deleting child '%s/%s'!\n", node->name, node->children + i); /* Skip it, we've already deleted the parent. */ } talloc_free(name); } } /* Delete memory using memmove. */ static void memdel(void *mem, unsigned off, unsigned len, unsigned total) { memmove(mem + off, mem + off + len, total - off - len); } static int remove_child_entry(struct connection *conn, struct node *node, size_t offset) { size_t childlen = strlen(node->children + offset); memdel(node->children, offset, childlen + 1, node->childlen); node->childlen -= childlen + 1; return write_node(conn, node); } static int delete_child(struct connection *conn, struct node *node, const char *childname) { unsigned int i; for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { if (streq(node->children+i, childname)) { return remove_child_entry(conn, node, i); } } corrupt(conn, "Can't find child '%s' in %s", childname, node->name); return ENOENT; } static int _rm(struct connection *conn, const void *ctx, struct node *node, const char *name) { /* Delete from parent first, then if we crash, the worst that can happen is the child will continue to take up space, but will otherwise be unreachable. */ struct node *parent; char *parentname = get_parent(ctx, name); if (!parentname) return errno; parent = read_node(conn, ctx, parentname); if (!parent) return (errno == ENOMEM) ? ENOMEM : EINVAL; if (delete_child(conn, parent, basename(name))) return EINVAL; delete_node(conn, node); return 0; } static int do_rm(struct connection *conn, struct buffered_data *in) { struct node *node; int ret; char *name; char *parentname; node = get_node_canonicalized(conn, in, onearg(in), &name, XS_PERM_WRITE); if (!node) { /* Didn't exist already? Fine, if parent exists. */ if (errno == ENOENT) { parentname = get_parent(in, name); if (!parentname) return errno; node = read_node(conn, in, parentname); if (node) { send_ack(conn, XS_RM); return 0; } /* Restore errno, just in case. */ if (errno != ENOMEM) errno = ENOENT; } return errno; } if (streq(name, "/")) return EINVAL; ret = _rm(conn, in, node, name); if (ret) return ret; fire_watches(conn, in, name, true); send_ack(conn, XS_RM); return 0; } static int do_get_perms(struct connection *conn, struct buffered_data *in) { struct node *node; char *strings; unsigned int len; node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); if (!node) return errno; strings = perms_to_strings(node, node->perms, node->num_perms, &len); if (!strings) return errno; send_reply(conn, XS_GET_PERMS, strings, len); return 0; } static int do_set_perms(struct connection *conn, struct buffered_data *in) { unsigned int num; struct xs_permissions *perms; char *name, *permstr; struct node *node; num = xs_count_strings(in->buffer, in->used); if (num < 2) return EINVAL; /* First arg is node name. */ /* We must own node to do this (tools can do this too). */ node = get_node_canonicalized(conn, in, in->buffer, &name, XS_PERM_WRITE | XS_PERM_OWNER); if (!node) return errno; permstr = in->buffer + strlen(in->buffer) + 1; num--; perms = talloc_array(node, struct xs_permissions, num); if (!perms) return ENOMEM; if (!xs_strings_to_perms(perms, num, permstr)) return errno; /* Unprivileged domains may not change the owner. */ if (domain_is_unprivileged(conn) && perms[0].id != node->perms[0].id) return EPERM; domain_entry_dec(conn, node); node->perms = perms; node->num_perms = num; domain_entry_inc(conn, node); if (write_node(conn, node)) return errno; fire_watches(conn, in, name, false); send_ack(conn, XS_SET_PERMS); return 0; } static struct { const char *str; int (*func)(struct connection *conn, struct buffered_data *in); } const wire_funcs[XS_TYPE_COUNT] = { [XS_CONTROL] = { "CONTROL", do_control }, [XS_DIRECTORY] = { "DIRECTORY", send_directory }, [XS_READ] = { "READ", do_read }, [XS_GET_PERMS] = { "GET_PERMS", do_get_perms }, [XS_WATCH] = { "WATCH", do_watch }, [XS_UNWATCH] = { "UNWATCH", do_unwatch }, [XS_TRANSACTION_START] = { "TRANSACTION_START", do_transaction_start }, [XS_TRANSACTION_END] = { "TRANSACTION_END", do_transaction_end }, [XS_INTRODUCE] = { "INTRODUCE", do_introduce }, [XS_RELEASE] = { "RELEASE", do_release }, [XS_GET_DOMAIN_PATH] = { "GET_DOMAIN_PATH", do_get_domain_path }, [XS_WRITE] = { "WRITE", do_write }, [XS_MKDIR] = { "MKDIR", do_mkdir }, [XS_RM] = { "RM", do_rm }, [XS_SET_PERMS] = { "SET_PERMS", do_set_perms }, [XS_WATCH_EVENT] = { "WATCH_EVENT", NULL }, [XS_ERROR] = { "ERROR", NULL }, [XS_IS_DOMAIN_INTRODUCED] = { "IS_DOMAIN_INTRODUCED", do_is_domain_introduced }, [XS_RESUME] = { "RESUME", do_resume }, [XS_SET_TARGET] = { "SET_TARGET", do_set_target }, [XS_RESET_WATCHES] = { "RESET_WATCHES", do_reset_watches }, [XS_DIRECTORY_PART] = { "DIRECTORY_PART", send_directory_part }, }; static const char *sockmsg_string(enum xsd_sockmsg_type type) { if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].str) return wire_funcs[type].str; return "**UNKNOWN**"; } /* Process "in" for conn: "in" will vanish after this conversation, so * we can talloc off it for temporary variables. May free "conn". */ static void process_message(struct connection *conn, struct buffered_data *in) { struct transaction *trans; enum xsd_sockmsg_type type = in->hdr.msg.type; int ret; trans = transaction_lookup(conn, in->hdr.msg.tx_id); if (IS_ERR(trans)) { send_error(conn, -PTR_ERR(trans)); return; } assert(conn->transaction == NULL); conn->transaction = trans; if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].func) ret = wire_funcs[type].func(conn, in); else { eprintf("Client unknown operation %i", type); ret = ENOSYS; } if (ret) send_error(conn, ret); conn->transaction = NULL; } static void consider_message(struct connection *conn) { if (verbose) xprintf("Got message %s len %i from %p\n", sockmsg_string(conn->in->hdr.msg.type), conn->in->hdr.msg.len, conn); process_message(conn, conn->in); assert(conn->in == NULL); } /* Errors in reading or allocating here mean we get out of sync, so we * drop the whole client connection. */ static void handle_input(struct connection *conn) { int bytes; struct buffered_data *in; if (!conn->in) { conn->in = new_buffer(conn); /* In case of no memory just try it again next time. */ if (!conn->in) return; } in = conn->in; /* Not finished header yet? */ if (in->inhdr) { if (in->used != sizeof(in->hdr)) { bytes = conn->read(conn, in->hdr.raw + in->used, sizeof(in->hdr) - in->used); if (bytes < 0) goto bad_client; in->used += bytes; if (in->used != sizeof(in->hdr)) return; if (in->hdr.msg.len > XENSTORE_PAYLOAD_MAX) { syslog(LOG_ERR, "Client tried to feed us %i", in->hdr.msg.len); goto bad_client; } } if (in->hdr.msg.len <= DEFAULT_BUFFER_SIZE) in->buffer = in->default_buffer; else in->buffer = talloc_array(in, char, in->hdr.msg.len); /* In case of no memory just try it again next time. */ if (!in->buffer) return; in->used = 0; in->inhdr = false; } bytes = conn->read(conn, in->buffer + in->used, in->hdr.msg.len - in->used); if (bytes < 0) goto bad_client; in->used += bytes; if (in->used != in->hdr.msg.len) return; trace_io(conn, in, 0); consider_message(conn); return; bad_client: /* Kill it. */ talloc_free(conn); } static void handle_output(struct connection *conn) { if (!write_messages(conn)) talloc_free(conn); } struct connection *new_connection(connwritefn_t *write, connreadfn_t *read) { struct connection *new; new = talloc_zero(talloc_autofree_context(), struct connection); if (!new) return NULL; new->fd = -1; new->pollfd_idx = -1; new->write = write; new->read = read; new->can_write = true; new->transaction_started = 0; INIT_LIST_HEAD(&new->out_list); INIT_LIST_HEAD(&new->watches); INIT_LIST_HEAD(&new->transaction_list); list_add_tail(&new->list, &connections); talloc_set_destructor(new, destroy_conn); trace_create(new, "connection"); return new; } #ifdef NO_SOCKETS static void accept_connection(int sock, bool canwrite) { } #else static int writefd(struct connection *conn, const void *data, unsigned int len) { int rc; while ((rc = write(conn->fd, data, len)) < 0) { if (errno == EAGAIN) { rc = 0; break; } if (errno != EINTR) break; } return rc; } static int readfd(struct connection *conn, void *data, unsigned int len) { int rc; while ((rc = read(conn->fd, data, len)) < 0) { if (errno == EAGAIN) { rc = 0; break; } if (errno != EINTR) break; } /* Reading zero length means we're done with this connection. */ if ((rc == 0) && (len != 0)) { errno = EBADF; rc = -1; } return rc; } static void accept_connection(int sock, bool canwrite) { int fd; struct connection *conn; fd = accept(sock, NULL, NULL); if (fd < 0) return; conn = new_connection(writefd, readfd); if (conn) { conn->fd = fd; conn->can_write = canwrite; } else close(fd); } #endif static int tdb_flags; /* We create initial nodes manually. */ static void manual_node(const char *name, const char *child) { struct node *node; struct xs_permissions perms = { .id = 0, .perms = XS_PERM_NONE }; node = talloc_zero(NULL, struct node); if (!node) barf_perror("Could not allocate initial node %s", name); node->name = name; node->perms = &perms; node->num_perms = 1; node->children = (char *)child; if (child) node->childlen = strlen(child) + 1; if (write_node(NULL, node)) barf_perror("Could not create initial node %s", name); talloc_free(node); } static void tdb_logger(TDB_CONTEXT *tdb, int level, const char * fmt, ...) { va_list ap; char *s; va_start(ap, fmt); s = talloc_vasprintf(NULL, fmt, ap); va_end(ap); if (s) { trace("TDB: %s\n", s); syslog(LOG_ERR, "TDB: %s", s); if (verbose) xprintf("TDB: %s", s); talloc_free(s); } else { trace("talloc failure during logging\n"); syslog(LOG_ERR, "talloc failure during logging\n"); } } static void setup_structure(void) { char *tdbname; tdbname = talloc_strdup(talloc_autofree_context(), xs_daemon_tdb()); if (!tdbname) barf_perror("Could not create tdbname"); if (!(tdb_flags & TDB_INTERNAL)) unlink(tdbname); tdb_ctx = tdb_open_ex(tdbname, 7919, tdb_flags, O_RDWR|O_CREAT|O_EXCL, 0640, &tdb_logger, NULL); if (!tdb_ctx) barf_perror("Could not create tdb file %s", tdbname); manual_node("/", "tool"); manual_node("/tool", "xenstored"); manual_node("/tool/xenstored", NULL); check_store(); } static unsigned int hash_from_key_fn(void *k) { char *str = k; unsigned int hash = 5381; char c; while ((c = *str++)) hash = ((hash << 5) + hash) + (unsigned int)c; return hash; } static int keys_equal_fn(void *key1, void *key2) { return 0 == strcmp((char *)key1, (char *)key2); } static char *child_name(const char *s1, const char *s2) { if (strcmp(s1, "/")) { return talloc_asprintf(NULL, "%s/%s", s1, s2); } else { return talloc_asprintf(NULL, "/%s", s2); } } int remember_string(struct hashtable *hash, const char *str) { char *k = malloc(strlen(str) + 1); if (!k) return 0; strcpy(k, str); return hashtable_insert(hash, k, (void *)1); } /** * A node has a children field that names the children of the node, separated * by NULs. We check whether there are entries in there that are duplicated * (and if so, delete the second one), and whether there are any that do not * have a corresponding child node (and if so, delete them). Each valid child * is then recursively checked. * * No deleting is performed if the recovery flag is cleared (i.e. -R was * passed on the command line). * * As we go, we record each node in the given reachable hashtable. These * entries will be used later in clean_store. */ static int check_store_(const char *name, struct hashtable *reachable) { struct node *node = read_node(NULL, name, name); int ret = 0; if (node) { size_t i = 0; struct hashtable * children = create_hashtable(16, hash_from_key_fn, keys_equal_fn); if (!remember_string(reachable, name)) { hashtable_destroy(children, 0); log("check_store: ENOMEM"); return ENOMEM; } while (i < node->childlen && !ret) { struct node *childnode; size_t childlen = strlen(node->children + i); char * childname = child_name(node->name, node->children + i); if (!childname) { log("check_store: ENOMEM"); ret = ENOMEM; break; } childnode = read_node(NULL, childname, childname); if (childnode) { if (hashtable_search(children, childname)) { log("check_store: '%s' is duplicated!", childname); if (recovery) { remove_child_entry(NULL, node, i); i -= childlen + 1; } } else { if (!remember_string(children, childname)) { log("check_store: ENOMEM"); talloc_free(childnode); talloc_free(childname); ret = ENOMEM; break; } ret = check_store_(childname, reachable); } } else if (errno != ENOMEM) { log("check_store: No child '%s' found!\n", childname); if (recovery) { remove_child_entry(NULL, node, i); i -= childlen + 1; } } else { log("check_store: ENOMEM"); ret = ENOMEM; } talloc_free(childnode); talloc_free(childname); i += childlen + 1; } hashtable_destroy(children, 0 /* Don't free values (they are all (void *)1) */); talloc_free(node); } else if (errno != ENOMEM) { /* Impossible, because no database should ever be without the root, and otherwise, we've just checked in our caller (which made a recursive call to get here). */ log("check_store: No child '%s' found: impossible!", name); } else { log("check_store: ENOMEM"); ret = ENOMEM; } return ret; } /** * Helper to clean_store below. */ static int clean_store_(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA val, void *private) { struct hashtable *reachable = private; char *slash; char * name = talloc_strndup(NULL, key.dptr, key.dsize); if (!name) { log("clean_store: ENOMEM"); return 1; } if (name[0] != '/') { slash = strchr(name, '/'); if (slash) *slash = 0; } if (!hashtable_search(reachable, name)) { log("clean_store: '%s' is orphaned!", name); if (recovery) { tdb_delete(tdb, key); } } talloc_free(name); return 0; } /** * Given the list of reachable nodes, iterate over the whole store, and * remove any that were not reached. */ static void clean_store(struct hashtable *reachable) { tdb_traverse(tdb_ctx, &clean_store_, reachable); } void check_store(void) { char * root = talloc_strdup(NULL, "/"); struct hashtable * reachable = create_hashtable(16, hash_from_key_fn, keys_equal_fn); if (!reachable) { log("check_store: ENOMEM"); return; } log("Checking store ..."); if (!check_store_(root, reachable) && !check_transactions(reachable)) clean_store(reachable); log("Checking store complete."); hashtable_destroy(reachable, 0 /* Don't free values (they are all (void *)1) */); talloc_free(root); } /* Something is horribly wrong: check the store. */ void corrupt(struct connection *conn, const char *fmt, ...) { va_list arglist; char *str; int saved_errno = errno; va_start(arglist, fmt); str = talloc_vasprintf(NULL, fmt, arglist); va_end(arglist); log("corruption detected by connection %i: err %s: %s", conn ? (int)conn->id : -1, strerror(saved_errno), str); check_store(); } #ifdef NO_SOCKETS static void init_sockets(int **psock, int **pro_sock) { static int minus_one = -1; *psock = *pro_sock = &minus_one; } #else static int destroy_fd(void *_fd) { int *fd = _fd; close(*fd); return 0; } static void init_sockets(int **psock, int **pro_sock) { struct sockaddr_un addr; int *sock, *ro_sock; const char *soc_str = xs_daemon_socket(); const char *soc_str_ro = xs_daemon_socket_ro(); /* Create sockets for them to listen to. */ *psock = sock = talloc(talloc_autofree_context(), int); if (!sock) barf_perror("No memory when creating sockets"); *sock = socket(PF_UNIX, SOCK_STREAM, 0); if (*sock < 0) barf_perror("Could not create socket"); *pro_sock = ro_sock = talloc(talloc_autofree_context(), int); if (!ro_sock) barf_perror("No memory when creating sockets"); *ro_sock = socket(PF_UNIX, SOCK_STREAM, 0); if (*ro_sock < 0) barf_perror("Could not create socket"); talloc_set_destructor(sock, destroy_fd); talloc_set_destructor(ro_sock, destroy_fd); /* FIXME: Be more sophisticated, don't mug running daemon. */ unlink(soc_str); unlink(soc_str_ro); addr.sun_family = AF_UNIX; if(strlen(soc_str) >= sizeof(addr.sun_path)) barf_perror("socket string '%s' too long", soc_str); strcpy(addr.sun_path, soc_str); if (bind(*sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) barf_perror("Could not bind socket to %s", soc_str); if(strlen(soc_str_ro) >= sizeof(addr.sun_path)) barf_perror("socket string '%s' too long", soc_str_ro); strcpy(addr.sun_path, soc_str_ro); if (bind(*ro_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) barf_perror("Could not bind socket to %s", soc_str_ro); if (chmod(soc_str, 0600) != 0 || chmod(soc_str_ro, 0660) != 0) barf_perror("Could not chmod sockets"); if (listen(*sock, 1) != 0 || listen(*ro_sock, 1) != 0) barf_perror("Could not listen on sockets"); } #endif static void usage(void) { fprintf(stderr, "Usage:\n" "\n" " xenstored \n" "\n" "where options may include:\n" "\n" " -D, --no-domain-init to state that xenstored should not initialise dom0,\n" " -F, --pid-file giving a file for the daemon's pid to be written,\n" " -H, --help to output this message,\n" " -N, --no-fork to request that the daemon does not fork,\n" " -P, --output-pid to request that the pid of the daemon is output,\n" " -T, --trace-file giving the file for logging, and\n" " -E, --entry-nb limit the number of entries per domain,\n" " -S, --entry-size limit the size of entry per domain, and\n" " -W, --watch-nb limit the number of watches per domain,\n" " -t, --transaction limit the number of transaction allowed per domain,\n" " -R, --no-recovery to request that no recovery should be attempted when\n" " the store is corrupted (debug only),\n" " -I, --internal-db store database in memory, not on disk\n" " -V, --verbose to request verbose execution.\n"); } static struct option options[] = { { "no-domain-init", 0, NULL, 'D' }, { "entry-nb", 1, NULL, 'E' }, { "pid-file", 1, NULL, 'F' }, { "event", 1, NULL, 'e' }, { "master-domid", 1, NULL, 'm' }, { "help", 0, NULL, 'H' }, { "no-fork", 0, NULL, 'N' }, { "priv-domid", 1, NULL, 'p' }, { "output-pid", 0, NULL, 'P' }, { "entry-size", 1, NULL, 'S' }, { "trace-file", 1, NULL, 'T' }, { "transaction", 1, NULL, 't' }, { "no-recovery", 0, NULL, 'R' }, { "internal-db", 0, NULL, 'I' }, { "verbose", 0, NULL, 'V' }, { "watch-nb", 1, NULL, 'W' }, { NULL, 0, NULL, 0 } }; extern void dump_conn(struct connection *conn); int dom0_domid = 0; int dom0_event = 0; int priv_domid = 0; int main(int argc, char *argv[]) { int opt, *sock = NULL, *ro_sock = NULL; int sock_pollfd_idx = -1, ro_sock_pollfd_idx = -1; bool dofork = true; bool outputpid = false; bool no_domain_init = false; const char *pidfile = NULL; int timeout; while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:T:RVW:", options, NULL)) != -1) { switch (opt) { case 'D': no_domain_init = true; break; case 'E': quota_nb_entry_per_domain = strtol(optarg, NULL, 10); break; case 'F': pidfile = optarg; break; case 'H': usage(); return 0; case 'N': dofork = false; break; case 'P': outputpid = true; break; case 'R': recovery = false; break; case 'S': quota_max_entry_size = strtol(optarg, NULL, 10); break; case 't': quota_max_transaction = strtol(optarg, NULL, 10); break; case 'T': tracefile = optarg; break; case 'I': tdb_flags = TDB_INTERNAL|TDB_NOLOCK; break; case 'V': verbose = true; break; case 'W': quota_nb_watch_per_domain = strtol(optarg, NULL, 10); break; case 'e': dom0_event = strtol(optarg, NULL, 10); break; case 'm': dom0_domid = strtol(optarg, NULL, 10); break; case 'p': priv_domid = strtol(optarg, NULL, 10); break; } } if (optind != argc) barf("%s: No arguments desired", argv[0]); reopen_log(); /* make sure xenstored directories exist */ /* Errors ignored here, will be reported when we open files */ mkdir(xs_daemon_rundir(), 0755); mkdir(xs_daemon_rootdir(), 0755); if (dofork) { openlog("xenstored", 0, LOG_DAEMON); daemonize(); } if (pidfile) write_pidfile(pidfile); /* Talloc leak reports go to stderr, which is closed if we fork. */ if (!dofork) talloc_enable_leak_report_full(); /* Don't kill us with SIGPIPE. */ signal(SIGPIPE, SIG_IGN); talloc_enable_null_tracking(); init_sockets(&sock, &ro_sock); init_pipe(reopen_log_pipe); /* Setup the database */ setup_structure(); /* Listen to hypervisor. */ if (!no_domain_init) domain_init(); /* Restore existing connections. */ restore_existing_connections(); if (outputpid) { printf("%ld\n", (long)getpid()); fflush(stdout); } /* redirect to /dev/null now we're ready to accept connections */ if (dofork) finish_daemonize(); signal(SIGHUP, trigger_reopen_log); if (tracefile) tracefile = talloc_strdup(NULL, tracefile); /* Get ready to listen to the tools. */ initialize_fds(*sock, &sock_pollfd_idx, *ro_sock, &ro_sock_pollfd_idx, &timeout); /* Tell the kernel we're up and running. */ xenbus_notify_running(); #if defined(XEN_SYSTEMD_ENABLED) sd_notify(1, "READY=1"); fprintf(stderr, SD_NOTICE "xenstored is ready\n"); #endif /* Main loop. */ for (;;) { struct connection *conn, *next; if (poll(fds, nr_fds, timeout) < 0) { if (errno == EINTR) continue; barf_perror("Poll failed"); } if (reopen_log_pipe0_pollfd_idx != -1) { if (fds[reopen_log_pipe0_pollfd_idx].revents & ~POLLIN) { close(reopen_log_pipe[0]); close(reopen_log_pipe[1]); init_pipe(reopen_log_pipe); } else if (fds[reopen_log_pipe0_pollfd_idx].revents & POLLIN) { char c; if (read(reopen_log_pipe[0], &c, 1) != 1) barf_perror("read failed"); reopen_log(); } reopen_log_pipe0_pollfd_idx = -1; } if (sock_pollfd_idx != -1) { if (fds[sock_pollfd_idx].revents & ~POLLIN) { barf_perror("sock poll failed"); break; } else if (fds[sock_pollfd_idx].revents & POLLIN) { accept_connection(*sock, true); sock_pollfd_idx = -1; } } if (ro_sock_pollfd_idx != -1) { if (fds[ro_sock_pollfd_idx].revents & ~POLLIN) { barf_perror("ro sock poll failed"); break; } else if (fds[ro_sock_pollfd_idx].revents & POLLIN) { accept_connection(*ro_sock, false); ro_sock_pollfd_idx = -1; } } if (xce_pollfd_idx != -1) { if (fds[xce_pollfd_idx].revents & ~POLLIN) { barf_perror("xce_handle poll failed"); break; } else if (fds[xce_pollfd_idx].revents & POLLIN) { handle_event(); xce_pollfd_idx = -1; } } next = list_entry(connections.next, typeof(*conn), list); if (&next->list != &connections) talloc_increase_ref_count(next); while (&next->list != &connections) { conn = next; next = list_entry(conn->list.next, typeof(*conn), list); if (&next->list != &connections) talloc_increase_ref_count(next); if (conn->domain) { if (domain_can_read(conn)) handle_input(conn); if (talloc_free(conn) == 0) continue; talloc_increase_ref_count(conn); if (domain_can_write(conn) && !list_empty(&conn->out_list)) handle_output(conn); if (talloc_free(conn) == 0) continue; } else { if (conn->pollfd_idx != -1) { if (fds[conn->pollfd_idx].revents & ~(POLLIN|POLLOUT)) talloc_free(conn); else if (fds[conn->pollfd_idx].revents & POLLIN) handle_input(conn); } if (talloc_free(conn) == 0) continue; talloc_increase_ref_count(conn); if (conn->pollfd_idx != -1) { if (fds[conn->pollfd_idx].revents & ~(POLLIN|POLLOUT)) talloc_free(conn); else if (fds[conn->pollfd_idx].revents & POLLOUT) handle_output(conn); } if (talloc_free(conn) == 0) continue; conn->pollfd_idx = -1; } } initialize_fds(*sock, &sock_pollfd_idx, *ro_sock, &ro_sock_pollfd_idx, &timeout); } } /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/xenstore/xenstore_control.c0000664000175000017500000000304613256712137017242 0ustar smbsmb#include #include #include #include "xenstore.h" int main(int argc, char **argv) { struct xs_handle *xsh; char *par = NULL; char *ret; unsigned int p, len = 0; int rc = 0; if (argc < 2) { fprintf(stderr, "Usage:\n" "%s [...]\n", argv[0]); rc = 2; goto out; } for (p = 2; p < argc; p++) len += strlen(argv[p]) + 1; if (len) { par = malloc(len); if (!par) { fprintf(stderr, "Allocation error.\n"); rc = 1; goto out; } len = 0; for (p = 2; p < argc; p++) { memcpy(par + len, argv[p], strlen(argv[p]) + 1); len += strlen(argv[p]) + 1; } } xsh = xs_open(0); if (xsh == NULL) { fprintf(stderr, "Failed to contact Xenstored.\n"); rc = 1; goto out; } ret = xs_control_command(xsh, argv[1], par, len); if (!ret) { rc = 3; if (errno == EINVAL) { ret = xs_control_command(xsh, "help", NULL, 0); if (ret) fprintf(stderr, "Command not supported. Valid commands are:\n" "%s\n", ret); else fprintf(stderr, "Error when executing command.\n"); } else fprintf(stderr, "Error %d when trying to execute command.\n", errno); } else if (strlen(ret) > 0) printf("%s\n", ret); xs_close(xsh); out: free(par); return rc; } xen-4.9.2/tools/xenstore/xenstored_watch.h0000664000175000017500000000231113256712137017033 0ustar smbsmb/* Watch code for Xen Store Daemon. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef _XENSTORED_WATCH_H #define _XENSTORED_WATCH_H #include "xenstored_core.h" int do_watch(struct connection *conn, struct buffered_data *in); int do_unwatch(struct connection *conn, struct buffered_data *in); /* Fire all watches: recurse means all the children are affected (ie. rm). */ void fire_watches(struct connection *conn, void *tmp, const char *name, bool recurse); void conn_delete_all_watches(struct connection *conn); #endif /* _XENSTORED_WATCH_H */ xen-4.9.2/tools/xenstore/README0000664000175000017500000000042613256712137014346 0ustar smbsmbThe following files are imported from the Samba project. We use the versions from Samba 3, the current stable branch. talloc.c: samba-trunk/source/lib/talloc.c r14291 2006-03-13 04:27:47 +0000 talloc.h: samba-trunk/source/include/talloc.h r11986 2005-12-01 00:43:36 +0000 xen-4.9.2/tools/xenstore/include/0000775000175000017500000000000013256712137015107 5ustar smbsmbxen-4.9.2/tools/xenstore/include/xenstore_lib.h0000664000175000017500000000570013256712137017757 0ustar smbsmb/* Common routines between Xen store user library and daemon. Copyright (C) 2005 Rusty Russell IBM Corporation This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . */ #ifndef XENSTORE_LIB_H #define XENSTORE_LIB_H #include #include #include #include #include #include /* Bitmask of permissions. */ enum xs_perm_type { XS_PERM_NONE = 0, XS_PERM_READ = 1, XS_PERM_WRITE = 2, /* Internal use. */ XS_PERM_ENOENT_OK = 4, XS_PERM_OWNER = 8, }; struct xs_permissions { unsigned int id; enum xs_perm_type perms; }; /* Header of the node record in tdb. */ struct xs_tdb_record_hdr { uint64_t generation; uint32_t num_perms; uint32_t datalen; uint32_t childlen; struct xs_permissions perms[0]; }; /* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */ #define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2) /* Path for various daemon things: env vars can override. */ const char *xs_daemon_rootdir(void); const char *xs_daemon_rundir(void); const char *xs_daemon_socket(void); const char *xs_daemon_socket_ro(void); const char *xs_domain_dev(void); const char *xs_daemon_tdb(void); /* Simple write function: loops for you. */ bool xs_write_all(int fd, const void *data, unsigned int len); /* Convert strings to permissions. False if a problem. */ bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num, const char *strings); /* Convert permissions to a string (up to len MAX_STRLEN(unsigned int)+1). */ bool xs_perm_to_string(const struct xs_permissions *perm, char *buffer, size_t buf_len); /* Given a string and a length, count how many strings (nul terms). */ unsigned int xs_count_strings(const char *strings, unsigned int len); /* Sanitising (quoting) possibly-binary strings. */ struct expanding_buffer { char *buf; int avail; }; /* Ensure that given expanding buffer has at least min_avail characters. */ char *expanding_buffer_ensure(struct expanding_buffer *, int min_avail); /* sanitise_value() may return NULL if malloc fails. */ char *sanitise_value(struct expanding_buffer *, const char *val, unsigned len); /* *out_len_r on entry is ignored; out must be at least strlen(in)+1 bytes. */ void unsanitise_value(char *out, unsigned *out_len_r, const char *in); #endif /* XENSTORE_LIB_H */ xen-4.9.2/tools/xenstore/include/compat/0000775000175000017500000000000013256712137016372 5ustar smbsmbxen-4.9.2/tools/xenstore/include/compat/xs.h0000664000175000017500000000011113256712137017166 0ustar smbsmb#warning xs.h is deprecated use xenstore.h instead #include xen-4.9.2/tools/xenstore/include/compat/xs_lib.h0000664000175000017500000000012513256712137020021 0ustar smbsmb#warning xs_lib.h is deprecated use xenstore_lib.h instead #include xen-4.9.2/tools/xenstore/include/xenstore.h0000664000175000017500000002350313256712137017132 0ustar smbsmb/* Xen Store Daemon providing simple tree-like database. Copyright (C) 2005 Rusty Russell IBM Corporation This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . */ #ifndef XENSTORE_H #define XENSTORE_H #include #define XBT_NULL 0 #define XS_OPEN_READONLY 1UL<<0 #define XS_OPEN_SOCKETONLY 1UL<<1 /* * Setting XS_UNWATCH_FILTER arranges that after xs_unwatch, no * related watch events will be delivered via xs_read_watch. But * this relies on the couple token, subpath is unique. * * XS_UNWATCH_FILTER clear XS_UNWATCH_FILTER set * * Even after xs_unwatch, "stale" After xs_unwatch returns, no * instances of the watch event watch events with the same * may be delivered. token and with the same subpath * will be delivered. * * A path and a subpath can be The application must avoid * register with the same token. registering a path (/foo/) and * a subpath (/foo/bar) with the * same path until a successful * xs_unwatch for the first watch * has returned. */ #define XS_UNWATCH_FILTER 1UL<<2 struct xs_handle; typedef uint32_t xs_transaction_t; /* IMPORTANT: For details on xenstore protocol limits, see * docs/misc/xenstore.txt in the Xen public source repository, and use the * XENSTORE_*_MAX limit macros defined in xen/io/xs_wire.h. */ /* On failure, these routines set errno. */ /* Open a connection to the xs daemon. * Attempts to make a connection over the socket interface, * and if it fails, then over the xenbus interface. * Mode 0 specifies read-write access, XS_OPEN_READONLY for * read-only access. * * * Connections made with xs_open(0) (which might be shared page or * socket based) are only guaranteed to work in the parent after * fork. * * Connections made with xs_open(XS_OPEN_SOCKETONLY) will be usable * in either the parent or the child after fork, but not both. * * xs_daemon_open*() and xs_domain_open() are deprecated synonyms * for xs_open(0). * * XS_OPEN_READONLY has no bearing on any of this. * * Returns a handle or NULL. */ struct xs_handle *xs_open(unsigned long flags); /* Close the connection to the xs daemon. */ void xs_close(struct xs_handle *xsh); /* Connect to the xs daemon. * Returns a handle or NULL. * Deprecated, please use xs_open(0) instead */ struct xs_handle *xs_daemon_open(void); struct xs_handle *xs_domain_open(void); /* Connect to the xs daemon (readonly for non-root clients). * Returns a handle or NULL. * Deprecated, please use xs_open(XS_OPEN_READONLY) instead */ struct xs_handle *xs_daemon_open_readonly(void); /* Close the connection to the xs daemon. * Deprecated, please use xs_close() instead */ void xs_daemon_close(struct xs_handle *); /* Throw away the connection to the xs daemon, for use after fork(). */ void xs_daemon_destroy_postfork(struct xs_handle *); /* Get contents of a directory. * Returns a malloced array: call free() on it after use. * Num indicates size. */ char **xs_directory(struct xs_handle *h, xs_transaction_t t, const char *path, unsigned int *num); /* Get the value of a single file, nul terminated. * Returns a malloced value: call free() on it after use. * len indicates length in bytes, not including terminator. */ void *xs_read(struct xs_handle *h, xs_transaction_t t, const char *path, unsigned int *len); /* Write the value of a single file. * Returns false on failure. */ bool xs_write(struct xs_handle *h, xs_transaction_t t, const char *path, const void *data, unsigned int len); /* Create a new directory. * Returns false on failure, or success if it already exists. */ bool xs_mkdir(struct xs_handle *h, xs_transaction_t t, const char *path); /* Destroy a file or directory (and children). * Returns false on failure, or if it doesn't exist. */ bool xs_rm(struct xs_handle *h, xs_transaction_t t, const char *path); /* Get permissions of node (first element is owner, first perms is "other"). * Returns malloced array, or NULL: call free() after use. */ struct xs_permissions *xs_get_permissions(struct xs_handle *h, xs_transaction_t t, const char *path, unsigned int *num); /* Set permissions of node (must be owner). Returns false on failure. * * Domain 0 may read / write anywhere in the store, regardless of * permission settings. * * Note: * The perms array is a list of (domid, permissions) pairs. The first * element in the list specifies the owner of the list, plus the flags * for every domain not explicitly specified subsequently. The * subsequent entries are normal capabilities. * * Example C code: * * struct xs_permissions perms[2]; * * perms[0].id = dm_domid; * perms[0].perms = XS_PERM_NONE; * perms[1].id = guest_domid; * perms[1].perms = XS_PERM_READ; * * It means the owner of the path is domain $dm_domid (hence it always * has read and write permission), all other domains (unless specified * in subsequent pair) can neither read from nor write to that * path. It then specifies domain $guest_domid can read from that * path. */ bool xs_set_permissions(struct xs_handle *h, xs_transaction_t t, const char *path, struct xs_permissions *perms, unsigned int num_perms); /* Watch a node for changes (poll on fd to detect, or call read_watch()). * When the node (or any child) changes, fd will become readable. * Token is returned when watch is read, to allow matching. * Returns false on failure. */ bool xs_watch(struct xs_handle *h, const char *path, const char *token); /* Return the FD to poll on to see if a watch has fired. */ int xs_fileno(struct xs_handle *h); /* Check for node changes. On success, returns a non-NULL pointer ret * such that ret[0] and ret[1] are valid C strings, namely the * triggering path (see docs/misc/xenstore.txt) and the token (from * xs_watch). On error return value is NULL setting errno. * * Callers should, after xs_fileno has become readable, repeatedly * call xs_check_watch until it returns NULL and sets errno to EAGAIN. * (If the fd became readable, xs_check_watch is allowed to make it no * longer show up as readable even if future calls to xs_check_watch * will return more watch events.) * * After the caller is finished with the returned information it * should be freed all in one go with free(ret). */ char **xs_check_watch(struct xs_handle *h); /* Find out what node change was on (will block if nothing pending). * Returns array containing the path and token. Use XS_WATCH_* to access these * elements. Call free() after use. */ char **xs_read_watch(struct xs_handle *h, unsigned int *num); /* Remove a watch on a node: implicitly acks any outstanding watch. * Returns false on failure (no watch on that node). */ bool xs_unwatch(struct xs_handle *h, const char *path, const char *token); /* Start a transaction: changes by others will not be seen during this * transaction, and changes will not be visible to others until end. * Returns NULL on failure. */ xs_transaction_t xs_transaction_start(struct xs_handle *h); /* End a transaction. * If abandon is true, transaction is discarded instead of committed. * Returns false on failure: if errno == EAGAIN, you have to restart * transaction. */ bool xs_transaction_end(struct xs_handle *h, xs_transaction_t t, bool abort); /* Introduce a new domain. * This tells the store daemon about a shared memory page, event channel and * store path associated with a domain: the domain uses these to communicate. */ bool xs_introduce_domain(struct xs_handle *h, unsigned int domid, unsigned long mfn, unsigned int eventchn); /* Set the target of a domain * This tells the store daemon that a domain is targetting another one, so * it should let it tinker with it. */ bool xs_set_target(struct xs_handle *h, unsigned int domid, unsigned int target); /* Resume a domain. * Clear the shutdown flag for this domain in the store. */ bool xs_resume_domain(struct xs_handle *h, unsigned int domid); /* Release a domain. * Tells the store domain to release the memory page to the domain. */ bool xs_release_domain(struct xs_handle *h, unsigned int domid); /* Query the home path of a domain. Call free() after use. */ char *xs_get_domain_path(struct xs_handle *h, unsigned int domid); /* Returns true if child is either equal to parent, or a node underneath * parent; or false otherwise. Done by string comparison, so relative and * absolute pathnames never in a parent/child relationship by this * definition. Cannot fail. */ bool xs_path_is_subpath(const char *parent, const char *child); /* Return whether the domain specified has been introduced to xenstored. */ bool xs_is_domain_introduced(struct xs_handle *h, unsigned int domid); char *xs_control_command(struct xs_handle *h, const char *cmd, void *data, unsigned int len); /* Deprecated: use xs_control_command() instead. */ char *xs_debug_command(struct xs_handle *h, const char *cmd, void *data, unsigned int len); int xs_suspend_evtchn_port(int domid); #endif /* XENSTORE_H */ /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/xenstore/xenstored_control.c0000664000175000017500000001005213256712137017401 0ustar smbsmb/* Interactive commands for Xen Store Daemon. Copyright (C) 2017 Juergen Gross, SUSE Linux GmbH This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include "utils.h" #include "talloc.h" #include "xenstored_core.h" #include "xenstored_control.h" struct cmd_s { char *cmd; int (*func)(void *, struct connection *, char **, int); char *pars; }; static int do_control_check(void *ctx, struct connection *conn, char **vec, int num) { if (num) return EINVAL; check_store(); send_ack(conn, XS_CONTROL); return 0; } static int do_control_log(void *ctx, struct connection *conn, char **vec, int num) { if (num != 1) return EINVAL; if (!strcmp(vec[0], "on")) reopen_log(); else if (!strcmp(vec[0], "off")) close_log(); else return EINVAL; send_ack(conn, XS_CONTROL); return 0; } static int do_control_logfile(void *ctx, struct connection *conn, char **vec, int num) { if (num != 1) return EINVAL; close_log(); talloc_free(tracefile); tracefile = talloc_strdup(NULL, vec[0]); reopen_log(); send_ack(conn, XS_CONTROL); return 0; } static int do_control_memreport(void *ctx, struct connection *conn, char **vec, int num) { FILE *fp; int fd; if (num > 1) return EINVAL; if (num == 0) { if (tracefd < 0) { if (!tracefile) return EBADF; fp = fopen(tracefile, "a"); } else { /* * Use dup() in order to avoid closing the file later * with fclose() which will release stream resources. */ fd = dup(tracefd); if (fd < 0) return EBADF; fp = fdopen(fd, "a"); if (!fp) close(fd); } } else fp = fopen(vec[0], "a"); if (!fp) return EBADF; talloc_report_full(NULL, fp); fclose(fp); send_ack(conn, XS_CONTROL); return 0; } static int do_control_print(void *ctx, struct connection *conn, char **vec, int num) { if (num != 1) return EINVAL; xprintf("control: %s", vec[0]); send_ack(conn, XS_CONTROL); return 0; } static int do_control_help(void *, struct connection *, char **, int); static struct cmd_s cmds[] = { { "check", do_control_check, "" }, { "log", do_control_log, "on|off" }, { "logfile", do_control_logfile, "" }, { "memreport", do_control_memreport, "[]" }, { "print", do_control_print, "" }, { "help", do_control_help, "" }, }; static int do_control_help(void *ctx, struct connection *conn, char **vec, int num) { int cmd, len = 0; char *resp; if (num) return EINVAL; for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) { len += strlen(cmds[cmd].cmd) + 1; len += strlen(cmds[cmd].pars) + 1; } len++; resp = talloc_array(ctx, char, len); if (!resp) return ENOMEM; len = 0; for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) { strcpy(resp + len, cmds[cmd].cmd); len += strlen(cmds[cmd].cmd); resp[len] = '\t'; len++; strcpy(resp + len, cmds[cmd].pars); len += strlen(cmds[cmd].pars); resp[len] = '\n'; len++; } resp[len] = 0; send_reply(conn, XS_CONTROL, resp, len); return 0; } int do_control(struct connection *conn, struct buffered_data *in) { int num; int cmd; char **vec; if (conn->id != 0) return EACCES; num = xs_count_strings(in->buffer, in->used); vec = talloc_array(in, char *, num); if (!vec) return ENOMEM; if (get_strings(in, vec, num) != num) return EIO; for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) if (streq(vec[0], cmds[cmd].cmd)) return cmds[cmd].func(in, conn, vec + 1, num - 1); return EINVAL; } xen-4.9.2/tools/xenstore/tdb.c0000664000175000017500000013455713256712137014420 0ustar smbsmb /* Unix SMB/CIFS implementation. trivial database library Copyright (C) Andrew Tridgell 1999-2004 Copyright (C) Paul `Rusty' Russell 2000 Copyright (C) Jeremy Allison 2000-2003 ** NOTE! The following LGPL license applies to the tdb ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . */ #ifndef _SAMBA_BUILD_ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "tdb.h" #include #include "talloc.h" #undef HAVE_MMAP #else #include "includes.h" #include "lib/tdb/include/tdb.h" #include "system/time.h" #include "system/shmem.h" #include "system/filesys.h" #endif #define TDB_MAGIC_FOOD "TDB file\n" #define TDB_VERSION (0x26011967 + 7) #define TDB_MAGIC (0x26011999U) #define TDB_FREE_MAGIC (~TDB_MAGIC) #define TDB_DEAD_MAGIC (0xFEE1DEAD) #define TDB_ALIGNMENT 4 #define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT) #define DEFAULT_HASH_SIZE 131 #define TDB_PAGE_SIZE 0x2000 #define FREELIST_TOP (sizeof(struct tdb_header)) #define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1)) #define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24)) #define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC) #define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r)) #define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off)) #define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1)) /* NB assumes there is a local variable called "tdb" that is the * current context, also takes doubly-parenthesized print-style * argument. */ #define TDB_LOG(x) tdb->log_fn x /* lock offsets */ #define GLOBAL_LOCK 0 #define ACTIVE_LOCK 4 #ifndef MAP_FILE #define MAP_FILE 0 #endif #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif #ifndef discard_const_p # if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) # define discard_const(ptr) ((void *)((intptr_t)(ptr))) # else # define discard_const(ptr) ((void *)(ptr)) # endif # define discard_const_p(type, ptr) ((type *)discard_const(ptr)) #endif /* free memory if the pointer is valid and zero the pointer */ #ifndef SAFE_FREE #define SAFE_FREE(x) do { if ((x) != NULL) {talloc_free(discard_const_p(void *, (x))); (x)=NULL;} } while(0) #endif #define BUCKET(hash) ((hash) % tdb->header.hash_size) static TDB_DATA tdb_null; /* all contexts, to ensure no double-opens (fcntl locks don't nest!) */ static TDB_CONTEXT *tdbs = NULL; static int tdb_munmap(TDB_CONTEXT *tdb) { if (tdb->flags & TDB_INTERNAL) return 0; #ifdef HAVE_MMAP if (tdb->map_ptr) { int ret = munmap(tdb->map_ptr, tdb->map_size); if (ret != 0) return ret; } #endif tdb->map_ptr = NULL; return 0; } static void tdb_mmap(TDB_CONTEXT *tdb) { if (tdb->flags & TDB_INTERNAL) return; #ifdef HAVE_MMAP if (!(tdb->flags & TDB_NOMMAP)) { tdb->map_ptr = mmap(NULL, tdb->map_size, PROT_READ|(tdb->read_only? 0:PROT_WRITE), MAP_SHARED|MAP_FILE, tdb->fd, 0); /* * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!! */ if (tdb->map_ptr == MAP_FAILED) { tdb->map_ptr = NULL; TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n", tdb->map_size, strerror(errno))); } } else { tdb->map_ptr = NULL; } #else tdb->map_ptr = NULL; #endif } /* Endian conversion: we only ever deal with 4 byte quantities */ static void *convert(void *buf, uint32_t size) { uint32_t i, *p = buf; for (i = 0; i < size / 4; i++) p[i] = TDB_BYTEREV(p[i]); return buf; } #define DOCONV() (tdb->flags & TDB_CONVERT) #define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x) /* the body of the database is made of one list_struct for the free space plus a separate data list for each hash value */ struct list_struct { tdb_off next; /* offset of the next record in the list */ tdb_len rec_len; /* total byte length of record */ tdb_len key_len; /* byte length of key */ tdb_len data_len; /* byte length of data */ uint32_t full_hash; /* the full 32 bit hash of the key */ uint32_t magic; /* try to catch errors */ /* the following union is implied: union { char record[rec_len]; struct { char key[key_len]; char data[data_len]; } uint32_t totalsize; (tailer) } */ }; /* a byte range locking function - return 0 on success this functions locks/unlocks 1 byte at the specified offset. On error, errno is also set so that errors are passed back properly through tdb_open(). */ static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, int rw_type, int lck_type, int probe) { struct flock fl; int ret; if (tdb->flags & TDB_NOLOCK) return 0; if ((rw_type == F_WRLCK) && (tdb->read_only)) { errno = EACCES; return -1; } fl.l_type = rw_type; fl.l_whence = SEEK_SET; fl.l_start = offset; fl.l_len = 1; fl.l_pid = 0; do { ret = fcntl(tdb->fd,lck_type,&fl); } while (ret == -1 && errno == EINTR); if (ret == -1) { if (!probe && lck_type != F_SETLK) { /* Ensure error code is set for log fun to examine. */ tdb->ecode = TDB_ERR_LOCK; TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n", tdb->fd, offset, rw_type, lck_type)); } /* Generic lock error. errno set by fcntl. * EAGAIN is an expected return from non-blocking * locks. */ if (errno != EAGAIN) { TDB_LOG((tdb, 5, "tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d: %s\n", tdb->fd, offset, rw_type, lck_type, strerror(errno))); } return TDB_ERRCODE(TDB_ERR_LOCK, -1); } return 0; } /* lock a list in the database. list -1 is the alloc list */ static int tdb_lock(TDB_CONTEXT *tdb, int list, int ltype) { if (list < -1 || list >= (int)tdb->header.hash_size) { TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n", list, ltype)); return -1; } if (tdb->flags & TDB_NOLOCK) return 0; /* Since fcntl locks don't nest, we do a lock for the first one, and simply bump the count for future ones */ if (tdb->locked[list+1].count == 0) { if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) { TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n", list, ltype, strerror(errno))); return -1; } tdb->locked[list+1].ltype = ltype; } tdb->locked[list+1].count++; return 0; } /* unlock the database: returns void because it's too late for errors. */ /* changed to return int it may be interesting to know there has been an error --simo */ static int tdb_unlock(TDB_CONTEXT *tdb, int list, int ltype __attribute__((unused))) { int ret = -1; if (tdb->flags & TDB_NOLOCK) return 0; /* Sanity checks */ if (list < -1 || list >= (int)tdb->header.hash_size) { TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size)); return ret; } if (tdb->locked[list+1].count==0) { TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n")); return ret; } if (tdb->locked[list+1].count == 1) { /* Down to last nested lock: unlock underneath */ ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0); } else { ret = 0; } tdb->locked[list+1].count--; if (ret) TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n")); return ret; } /* This is based on the hash algorithm from gdbm */ static uint32_t default_tdb_hash(TDB_DATA *key) { uint32_t value; /* Used to compute the hash value. */ uint32_t i; /* Used to cycle through random values. */ /* Set the initial value from the key size. */ for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++) value = (value + (key->dptr[i] << (i*5 % 24))); return (1103515243 * value + 12345); } /* check for an out of bounds access - if it is out of bounds then see if the database has been expanded by someone else and expand if necessary note that "len" is the minimum length needed for the db */ static int tdb_oob(TDB_CONTEXT *tdb, tdb_off len, int probe) { struct stat st; if (len <= tdb->map_size) return 0; if (tdb->flags & TDB_INTERNAL) { if (!probe) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n", (int)len, (int)tdb->map_size)); } return TDB_ERRCODE(TDB_ERR_IO, -1); } if (fstat(tdb->fd, &st) == -1) return TDB_ERRCODE(TDB_ERR_IO, -1); if (st.st_size < (off_t)len) { if (!probe) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n", (int)len, (int)st.st_size)); } return TDB_ERRCODE(TDB_ERR_IO, -1); } /* Unmap, update size, remap */ if (tdb_munmap(tdb) == -1) return TDB_ERRCODE(TDB_ERR_IO, -1); tdb->map_size = st.st_size; tdb_mmap(tdb); return 0; } /* write a lump of data at a specified offset */ static int tdb_write(TDB_CONTEXT *tdb, tdb_off off, void *buf, tdb_len len) { if (tdb_oob(tdb, off + len, 0) != 0) return -1; if (tdb->map_ptr) memcpy(off + (char *)tdb->map_ptr, buf, len); #ifdef HAVE_PWRITE else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) { #else else if (lseek(tdb->fd, off, SEEK_SET) != (off_t)off || write(tdb->fd, buf, len) != (off_t)len) { #endif /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n", off, len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_IO, -1); } return 0; } /* read a lump of data at a specified offset, maybe convert */ static int tdb_read(TDB_CONTEXT *tdb,tdb_off off,void *buf,tdb_len len,int cv) { if (tdb_oob(tdb, off + len, 0) != 0) return -1; if (tdb->map_ptr) memcpy(buf, off + (char *)tdb->map_ptr, len); #ifdef HAVE_PREAD else if (pread(tdb->fd, buf, len, off) != (off_t)len) { #else else if (lseek(tdb->fd, off, SEEK_SET) != (off_t)off || read(tdb->fd, buf, len) != (off_t)len) { #endif /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d (%s)\n", off, len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_IO, -1); } if (cv) convert(buf, len); return 0; } /* don't allocate memory: used in tdb_delete path. */ static int tdb_key_eq(TDB_CONTEXT *tdb, tdb_off off, TDB_DATA key) { char buf[64]; uint32_t len; if (tdb_oob(tdb, off + key.dsize, 0) != 0) return -1; if (tdb->map_ptr) return !memcmp(off + (char*)tdb->map_ptr, key.dptr, key.dsize); while (key.dsize) { len = key.dsize; if (len > sizeof(buf)) len = sizeof(buf); if (tdb_read(tdb, off, buf, len, 0) != 0) return -1; if (memcmp(buf, key.dptr, len) != 0) return 0; key.dptr += len; key.dsize -= len; off += len; } return 1; } /* read a lump of data, allocating the space for it */ static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len) { char *buf; if (!(buf = talloc_size(tdb, len))) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_OOM; TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n", len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_OOM, buf); } if (tdb_read(tdb, offset, buf, len, 0) == -1) { SAFE_FREE(buf); return NULL; } return buf; } /* read/write a tdb_off */ static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d) { return tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV()); } static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d) { tdb_off off = *d; return tdb_write(tdb, offset, CONVERT(off), sizeof(*d)); } /* read/write a record */ static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) { if (tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1) return -1; if (TDB_BAD_MAGIC(rec)) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, 0,"rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } return tdb_oob(tdb, rec->next+sizeof(*rec), 0); } static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) { struct list_struct r = *rec; return tdb_write(tdb, offset, CONVERT(r), sizeof(r)); } /* read a freelist record and check for simple errors */ static int rec_free_read(TDB_CONTEXT *tdb, tdb_off off, struct list_struct *rec) { if (tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1) return -1; if (rec->magic == TDB_MAGIC) { /* this happens when a app is showdown while deleting a record - we should not completely fail when this happens */ TDB_LOG((tdb, 0,"rec_free_read non-free magic 0x%x at offset=%d - fixing\n", rec->magic, off)); rec->magic = TDB_FREE_MAGIC; if (tdb_write(tdb, off, rec, sizeof(*rec)) == -1) return -1; } if (rec->magic != TDB_FREE_MAGIC) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n", rec->magic, off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } if (tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0) return -1; return 0; } /* update a record tailer (must hold allocation lock) */ static int update_tailer(TDB_CONTEXT *tdb, tdb_off offset, const struct list_struct *rec) { tdb_off totalsize; /* Offset of tailer from record header */ totalsize = sizeof(*rec) + rec->rec_len; return ofs_write(tdb, offset + totalsize - sizeof(tdb_off), &totalsize); } /* Remove an element from the freelist. Must have alloc lock. */ static int remove_from_freelist(TDB_CONTEXT *tdb, tdb_off off, tdb_off next) { tdb_off last_ptr, i; /* read in the freelist top */ last_ptr = FREELIST_TOP; while (ofs_read(tdb, last_ptr, &i) != -1 && i != 0) { if (i == off) { /* We've found it! */ return ofs_write(tdb, last_ptr, &next); } /* Follow chain (next offset is at start of record) */ last_ptr = i; } TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } /* Add an element into the freelist. Merge adjacent records if neccessary. */ static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) { tdb_off right, left; /* Allocation and tailer lock */ if (tdb_lock(tdb, -1, F_WRLCK) != 0) return -1; /* set an initial tailer, so if we fail we don't leave a bogus record */ if (update_tailer(tdb, offset, rec) != 0) { TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n")); goto fail; } /* Look right first (I'm an Australian, dammit) */ right = offset + sizeof(*rec) + rec->rec_len; if (right + sizeof(*rec) <= tdb->map_size) { struct list_struct r; if (tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) { TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right)); goto left; } /* If it's free, expand to include it. */ if (r.magic == TDB_FREE_MAGIC) { if (remove_from_freelist(tdb, right, r.next) == -1) { TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right)); goto left; } rec->rec_len += sizeof(r) + r.rec_len; } } left: /* Look left */ left = offset - sizeof(tdb_off); if (left > TDB_DATA_START(tdb->header.hash_size)) { struct list_struct l; tdb_off leftsize; /* Read in tailer and jump back to header */ if (ofs_read(tdb, left, &leftsize) == -1) { TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left)); goto update; } left = offset - leftsize; /* Now read in record */ if (tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) { TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize)); goto update; } /* If it's free, expand to include it. */ if (l.magic == TDB_FREE_MAGIC) { if (remove_from_freelist(tdb, left, l.next) == -1) { TDB_LOG((tdb, 0, "tdb_free: left free failed at %u\n", left)); goto update; } else { offset = left; rec->rec_len += leftsize; } } } update: if (update_tailer(tdb, offset, rec) == -1) { TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset)); goto fail; } /* Now, prepend to free list */ rec->magic = TDB_FREE_MAGIC; if (ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 || rec_write(tdb, offset, rec) == -1 || ofs_write(tdb, FREELIST_TOP, &offset) == -1) { TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset)); goto fail; } /* And we're done. */ tdb_unlock(tdb, -1, F_WRLCK); return 0; fail: tdb_unlock(tdb, -1, F_WRLCK); return -1; } /* expand a file. we prefer to use ftruncate, as that is what posix says to use for mmap expansion */ static int expand_file(TDB_CONTEXT *tdb, tdb_off size, tdb_off addition) { char buf[1024]; #ifdef HAVE_FTRUNCATE_EXTEND if (ftruncate(tdb->fd, size+addition) != 0) { TDB_LOG((tdb, 0, "expand_file ftruncate to %d failed (%s)\n", size+addition, strerror(errno))); return -1; } #else char b = 0; #ifdef HAVE_PWRITE if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) { #else if (lseek(tdb->fd, (size+addition) - 1, SEEK_SET) != (off_t)(size+addition) - 1 || write(tdb->fd, &b, 1) != 1) { #endif TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n", size+addition, strerror(errno))); return -1; } #endif /* now fill the file with something. This ensures that the file isn't sparse, which would be very bad if we ran out of disk. This must be done with write, not via mmap */ memset(buf, 0x42, sizeof(buf)); while (addition) { int n = addition>sizeof(buf)?sizeof(buf):addition; #ifdef HAVE_PWRITE int ret = pwrite(tdb->fd, buf, n, size); #else int ret; if (lseek(tdb->fd, size, SEEK_SET) != (off_t)size) return -1; ret = write(tdb->fd, buf, n); #endif if (ret != n) { TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n", n, strerror(errno))); return -1; } addition -= n; size += n; } return 0; } /* expand the database at least size bytes by expanding the underlying file and doing the mmap again if necessary */ static int tdb_expand(TDB_CONTEXT *tdb, tdb_off size) { struct list_struct rec; tdb_off offset; if (tdb_lock(tdb, -1, F_WRLCK) == -1) { TDB_LOG((tdb, 0, "lock failed in tdb_expand\n")); return -1; } /* must know about any previous expansions by another process */ tdb_oob(tdb, tdb->map_size + 1, 1); /* always make room for at least 10 more records, and round the database up to a multiple of TDB_PAGE_SIZE */ size = TDB_ALIGN(tdb->map_size + size*10, TDB_PAGE_SIZE) - tdb->map_size; if (!(tdb->flags & TDB_INTERNAL)) tdb_munmap(tdb); /* * We must ensure the file is unmapped before doing this * to ensure consistency with systems like OpenBSD where * writes and mmaps are not consistent. */ /* expand the file itself */ if (!(tdb->flags & TDB_INTERNAL)) { if (expand_file(tdb, tdb->map_size, size) != 0) goto fail; } tdb->map_size += size; if (tdb->flags & TDB_INTERNAL) { char *new_map_ptr = talloc_realloc_size(tdb, tdb->map_ptr, tdb->map_size); if (!new_map_ptr) { tdb->map_size -= size; goto fail; } tdb->map_ptr = new_map_ptr; } else { /* * We must ensure the file is remapped before adding the space * to ensure consistency with systems like OpenBSD where * writes and mmaps are not consistent. */ /* We're ok if the mmap fails as we'll fallback to read/write */ tdb_mmap(tdb); } /* form a new freelist record */ memset(&rec,'\0',sizeof(rec)); rec.rec_len = size - sizeof(rec); /* link it into the free list */ offset = tdb->map_size - size; if (tdb_free(tdb, offset, &rec) == -1) goto fail; tdb_unlock(tdb, -1, F_WRLCK); return 0; fail: tdb_unlock(tdb, -1, F_WRLCK); return -1; } /* the core of tdb_allocate - called when we have decided which free list entry to use */ static tdb_off tdb_allocate_ofs(TDB_CONTEXT *tdb, tdb_len length, tdb_off rec_ptr, struct list_struct *rec, tdb_off last_ptr) { struct list_struct newrec; tdb_off newrec_ptr; memset(&newrec, '\0', sizeof(newrec)); /* found it - now possibly split it up */ if (rec->rec_len > length + MIN_REC_SIZE) { /* Length of left piece */ length = TDB_ALIGN(length, TDB_ALIGNMENT); /* Right piece to go on free list */ newrec.rec_len = rec->rec_len - (sizeof(*rec) + length); newrec_ptr = rec_ptr + sizeof(*rec) + length; /* And left record is shortened */ rec->rec_len = length; } else { newrec_ptr = 0; } /* Remove allocated record from the free list */ if (ofs_write(tdb, last_ptr, &rec->next) == -1) { return 0; } /* Update header: do this before we drop alloc lock, otherwise tdb_free() might try to merge with us, thinking we're free. (Thanks Jeremy Allison). */ rec->magic = TDB_MAGIC; if (rec_write(tdb, rec_ptr, rec) == -1) { return 0; } /* Did we create new block? */ if (newrec_ptr) { /* Update allocated record tailer (we shortened it). */ if (update_tailer(tdb, rec_ptr, rec) == -1) { return 0; } /* Free new record */ if (tdb_free(tdb, newrec_ptr, &newrec) == -1) { return 0; } } /* all done - return the new record offset */ return rec_ptr; } /* allocate some space from the free list. The offset returned points to a unconnected list_struct within the database with room for at least length bytes of total data 0 is returned if the space could not be allocated */ static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length, struct list_struct *rec) { tdb_off rec_ptr, last_ptr, newrec_ptr; struct { tdb_off rec_ptr, last_ptr; tdb_len rec_len; } bestfit = { 0, 0, 0 }; if (tdb_lock(tdb, -1, F_WRLCK) == -1) return 0; /* Extra bytes required for tailer */ length += sizeof(tdb_off); again: last_ptr = FREELIST_TOP; /* read in the freelist top */ if (ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) goto fail; bestfit.rec_ptr = 0; /* this is a best fit allocation strategy. Originally we used a first fit strategy, but it suffered from massive fragmentation issues when faced with a slowly increasing record size. */ while (rec_ptr) { if (rec_free_read(tdb, rec_ptr, rec) == -1) { goto fail; } if (rec->rec_len >= length) { if (bestfit.rec_ptr == 0 || rec->rec_len < bestfit.rec_len) { bestfit.rec_len = rec->rec_len; bestfit.rec_ptr = rec_ptr; bestfit.last_ptr = last_ptr; /* consider a fit to be good enough if we aren't wasting more than half the space */ if (bestfit.rec_len < 2*length) { break; } } } /* move to the next record */ last_ptr = rec_ptr; rec_ptr = rec->next; } if (bestfit.rec_ptr != 0) { if (rec_free_read(tdb, bestfit.rec_ptr, rec) == -1) { goto fail; } newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr, rec, bestfit.last_ptr); tdb_unlock(tdb, -1, F_WRLCK); return newrec_ptr; } /* we didn't find enough space. See if we can expand the database and if we can then try again */ if (tdb_expand(tdb, length + sizeof(*rec)) == 0) goto again; fail: tdb_unlock(tdb, -1, F_WRLCK); return 0; } /* initialise a new database with a specified hash size */ static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size) { struct tdb_header *newdb; int size, ret = -1; /* We make it up in memory, then write it out if not internal */ size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off); if (!(newdb = talloc_zero_size(tdb, size))) return TDB_ERRCODE(TDB_ERR_OOM, -1); /* Fill in the header */ newdb->version = TDB_VERSION; newdb->hash_size = hash_size; if (tdb->flags & TDB_INTERNAL) { tdb->map_size = size; tdb->map_ptr = (char *)newdb; memcpy(&tdb->header, newdb, sizeof(tdb->header)); /* Convert the `ondisk' version if asked. */ CONVERT(*newdb); return 0; } if (lseek(tdb->fd, 0, SEEK_SET) == -1) goto fail; if (ftruncate(tdb->fd, 0) == -1) goto fail; /* This creates an endian-converted header, as if read from disk */ CONVERT(*newdb); memcpy(&tdb->header, newdb, sizeof(tdb->header)); /* Don't endian-convert the magic food! */ memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1); if (write(tdb->fd, newdb, size) != size) ret = -1; else ret = 0; fail: SAFE_FREE(newdb); return ret; } /* Returns 0 on fail. On success, return offset of record, and fills in rec */ static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, uint32_t hash, struct list_struct *r) { tdb_off rec_ptr; /* read in the hash top */ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) return 0; /* keep looking until we find the right record */ while (rec_ptr) { if (rec_read(tdb, rec_ptr, r) == -1) return 0; if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) { /* a very likely hit - read the key */ int cmp = tdb_key_eq(tdb, rec_ptr + sizeof(*r), key); if (cmp < 0) return 0; else if (cmp > 0) return rec_ptr; } rec_ptr = r->next; } return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); } /* As tdb_find, but if you succeed, keep the lock */ static tdb_off tdb_find_lock_hash(TDB_CONTEXT *tdb, TDB_DATA key, uint32_t hash, int locktype, struct list_struct *rec) { uint32_t rec_ptr; if (tdb_lock(tdb, BUCKET(hash), locktype) == -1) return 0; if (!(rec_ptr = tdb_find(tdb, key, hash, rec))) tdb_unlock(tdb, BUCKET(hash), locktype); return rec_ptr; } enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb) { return tdb->ecode; } static struct tdb_errname { enum TDB_ERROR ecode; const char *estring; } emap[] = { {TDB_SUCCESS, "Success"}, {TDB_ERR_CORRUPT, "Corrupt database"}, {TDB_ERR_IO, "IO Error"}, {TDB_ERR_LOCK, "Locking error"}, {TDB_ERR_OOM, "Out of memory"}, {TDB_ERR_EXISTS, "Record exists"}, {TDB_ERR_NOLOCK, "Lock exists on other keys"}, {TDB_ERR_NOEXIST, "Record does not exist"} }; /* Error string for the last tdb error */ const char *tdb_errorstr(TDB_CONTEXT *tdb) { uint32_t i; for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++) if (tdb->ecode == emap[i].ecode) return emap[i].estring; return "Invalid error code"; } /* update an entry in place - this only works if the new data size is <= the old data size and the key exists. on failure return -1. */ static int tdb_update_hash(TDB_CONTEXT *tdb, TDB_DATA key, uint32_t hash, TDB_DATA dbuf) { struct list_struct rec; tdb_off rec_ptr; /* find entry */ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) return -1; /* must be long enough key, data and tailer */ if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) { tdb->ecode = TDB_SUCCESS; /* Not really an error */ return -1; } if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len, dbuf.dptr, dbuf.dsize) == -1) return -1; if (dbuf.dsize != rec.data_len) { /* update size */ rec.data_len = dbuf.dsize; return rec_write(tdb, rec_ptr, &rec); } return 0; } /* find an entry in the database given a key */ /* If an entry doesn't exist tdb_err will be set to * TDB_ERR_NOEXIST. If a key has no data attached * then the TDB_DATA will have zero length but * a non-zero pointer */ TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key) { tdb_off rec_ptr; struct list_struct rec; TDB_DATA ret; uint32_t hash; /* find which hash bucket it is in */ hash = tdb->hash_fn(&key); if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) return tdb_null; ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, rec.data_len); ret.dsize = rec.data_len; tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); return ret; } /* check if an entry in the database exists note that 1 is returned if the key is found and 0 is returned if not found this doesn't match the conventions in the rest of this module, but is compatible with gdbm */ static int tdb_exists_hash(TDB_CONTEXT *tdb, TDB_DATA key, uint32_t hash) { struct list_struct rec; if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0) return 0; tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); return 1; } /* record lock stops delete underneath */ static int lock_record(TDB_CONTEXT *tdb, tdb_off off) { return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0; } /* Write locks override our own fcntl readlocks, so check it here. Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not an error to fail to get the lock here. */ static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off) { struct tdb_traverse_lock *i; for (i = &tdb->travlocks; i; i = i->next) if (i->off == off) return -1; return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1); } /* Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not an error to fail to get the lock here. */ static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off) { return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0); } /* fcntl locks don't stack: avoid unlocking someone else's */ static int unlock_record(TDB_CONTEXT *tdb, tdb_off off) { struct tdb_traverse_lock *i; uint32_t count = 0; if (off == 0) return 0; for (i = &tdb->travlocks; i; i = i->next) if (i->off == off) count++; return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0); } /* actually delete an entry in the database given the offset */ static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec) { tdb_off last_ptr, i; struct list_struct lastrec; if (tdb->read_only) return -1; if (write_lock_record(tdb, rec_ptr) == -1) { /* Someone traversing here: mark it as dead */ rec->magic = TDB_DEAD_MAGIC; return rec_write(tdb, rec_ptr, rec); } if (write_unlock_record(tdb, rec_ptr) != 0) return -1; /* find previous record in hash chain */ if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1) return -1; for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next) if (rec_read(tdb, i, &lastrec) == -1) return -1; /* unlink it: next ptr is at start of record. */ if (last_ptr == 0) last_ptr = TDB_HASH_TOP(rec->full_hash); if (ofs_write(tdb, last_ptr, &rec->next) == -1) return -1; /* recover the space */ if (tdb_free(tdb, rec_ptr, rec) == -1) return -1; return 0; } /* Uses traverse lock: 0 = finish, -1 = error, other = record offset */ static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock, struct list_struct *rec) { int want_next = (tlock->off != 0); /* Lock each chain from the start one. */ for (; tlock->hash < tdb->header.hash_size; tlock->hash++) { /* this is an optimisation for the common case where the hash chain is empty, which is particularly common for the use of tdb with ldb, where large hashes are used. In that case we spend most of our time in tdb_brlock(), locking empty hash chains. To avoid this, we do an unlocked pre-check to see if the hash chain is empty before starting to look inside it. If it is empty then we can avoid that hash chain. If it isn't empty then we can't believe the value we get back, as we read it without a lock, so instead we get the lock and re-fetch the value below. Notice that not doing this optimisation on the first hash chain is critical. We must guarantee that we have done at least one fcntl lock at the start of a search to guarantee that memory is coherent on SMP systems. If records are added by others during the search then thats OK, and we could possibly miss those with this trick, but we could miss them anyway without this trick, so the semantics don't change. With a non-indexed ldb search this trick gains us a factor of around 80 in speed on a linux 2.6.x system (testing using ldbtest). */ if (!tlock->off && tlock->hash != 0) { uint32_t off; if (tdb->map_ptr) { for (;tlock->hash < tdb->header.hash_size;tlock->hash++) { if (0 != *(uint32_t *)(TDB_HASH_TOP(tlock->hash) + (unsigned char *)tdb->map_ptr)) { break; } } if (tlock->hash == tdb->header.hash_size) { continue; } } else { if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash), &off) == 0 && off == 0) { continue; } } } if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1) return -1; /* No previous record? Start at top of chain. */ if (!tlock->off) { if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash), &tlock->off) == -1) goto fail; } else { /* Otherwise unlock the previous record. */ if (unlock_record(tdb, tlock->off) != 0) goto fail; } if (want_next) { /* We have offset of old record: grab next */ if (rec_read(tdb, tlock->off, rec) == -1) goto fail; tlock->off = rec->next; } /* Iterate through chain */ while( tlock->off) { tdb_off current; if (rec_read(tdb, tlock->off, rec) == -1) goto fail; /* Detect infinite loops. From "Shlomi Yaakobovich" . */ if (tlock->off == rec->next) { TDB_LOG((tdb, 0, "tdb_next_lock: loop detected.\n")); goto fail; } if (!TDB_DEAD(rec)) { /* Woohoo: we found one! */ if (lock_record(tdb, tlock->off) != 0) goto fail; return tlock->off; } /* Try to clean dead ones from old traverses */ current = tlock->off; tlock->off = rec->next; if (!tdb->read_only && do_delete(tdb, current, rec) != 0) goto fail; } tdb_unlock(tdb, tlock->hash, F_WRLCK); want_next = 0; } /* We finished iteration without finding anything */ return TDB_ERRCODE(TDB_SUCCESS, 0); fail: tlock->off = 0; if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n")); return -1; } /* traverse the entire database - calling fn(tdb, key, data) on each element. return -1 on error or the record count traversed if fn is NULL then it is not called a non-zero return value from fn() indicates that the traversal should stop */ int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *private) { TDB_DATA key, dbuf; struct list_struct rec; struct tdb_traverse_lock tl = { NULL, 0, 0 }; int ret, count = 0; /* This was in the initializaton, above, but the IRIX compiler * did not like it. crh */ tl.next = tdb->travlocks.next; /* fcntl locks don't stack: beware traverse inside traverse */ tdb->travlocks.next = &tl; /* tdb_next_lock places locks on the record returned, and its chain */ while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) { count++; /* now read the full record */ key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec), rec.key_len + rec.data_len); if (!key.dptr) { ret = -1; if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) goto out; if (unlock_record(tdb, tl.off) != 0) TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); goto out; } key.dsize = rec.key_len; dbuf.dptr = key.dptr + rec.key_len; dbuf.dsize = rec.data_len; /* Drop chain lock, call out */ if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) { ret = -1; goto out; } if (fn && fn(tdb, key, dbuf, private)) { /* They want us to terminate traversal */ ret = count; if (unlock_record(tdb, tl.off) != 0) { TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n")); ret = -1; } tdb->travlocks.next = tl.next; SAFE_FREE(key.dptr); return count; } SAFE_FREE(key.dptr); } out: tdb->travlocks.next = tl.next; if (ret < 0) return -1; else return count; } /* find the first entry in the database and return its key */ TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb) { TDB_DATA key; struct list_struct rec; /* release any old lock */ if (unlock_record(tdb, tdb->travlocks.off) != 0) return tdb_null; tdb->travlocks.off = tdb->travlocks.hash = 0; if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0) return tdb_null; /* now read the key */ key.dsize = rec.key_len; key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize); if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n")); return key; } /* find the next entry in the database, returning its key */ TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey) { uint32_t oldhash; TDB_DATA key = tdb_null; struct list_struct rec; char *k = NULL; /* Is locked key the old key? If so, traverse will be reliable. */ if (tdb->travlocks.off) { if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK)) return tdb_null; if (rec_read(tdb, tdb->travlocks.off, &rec) == -1 || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec), rec.key_len)) || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) { /* No, it wasn't: unlock it and start from scratch */ if (unlock_record(tdb, tdb->travlocks.off) != 0) return tdb_null; if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0) return tdb_null; tdb->travlocks.off = 0; } SAFE_FREE(k); } if (!tdb->travlocks.off) { /* No previous element: do normal find, and lock record */ tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), F_WRLCK, &rec); if (!tdb->travlocks.off) return tdb_null; tdb->travlocks.hash = BUCKET(rec.full_hash); if (lock_record(tdb, tdb->travlocks.off) != 0) { TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno))); return tdb_null; } } oldhash = tdb->travlocks.hash; /* Grab next record: locks chain and returned record, unlocks old record */ if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) { key.dsize = rec.key_len; key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec), key.dsize); /* Unlock the chain of this new record */ if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n")); } /* Unlock the chain of old record */ if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n")); return key; } /* delete an entry in the database given a key */ static int tdb_delete_hash(TDB_CONTEXT *tdb, TDB_DATA key, uint32_t hash) { tdb_off rec_ptr; struct list_struct rec; int ret; if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec))) return -1; ret = do_delete(tdb, rec_ptr, &rec); if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0) TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n")); return ret; } int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key) { uint32_t hash = tdb->hash_fn(&key); return tdb_delete_hash(tdb, key, hash); } /* store an element in the database, replacing any existing element with the same key return 0 on success, -1 on failure */ int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) { struct list_struct rec; uint32_t hash; tdb_off rec_ptr; char *p = NULL; int ret = 0; /* find which hash bucket it is in */ hash = tdb->hash_fn(&key); if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) return -1; /* check for it existing, on insert. */ if (flag == TDB_INSERT) { if (tdb_exists_hash(tdb, key, hash)) { tdb->ecode = TDB_ERR_EXISTS; goto fail; } } else { /* first try in-place update, on modify or replace. */ if (tdb_update_hash(tdb, key, hash, dbuf) == 0) goto out; if (tdb->ecode == TDB_ERR_NOEXIST && flag == TDB_MODIFY) { /* if the record doesn't exist and we are in TDB_MODIFY mode then we should fail the store */ goto fail; } } /* reset the error code potentially set by the tdb_update() */ tdb->ecode = TDB_SUCCESS; /* delete any existing record - if it doesn't exist we don't care. Doing this first reduces fragmentation, and avoids coalescing with `allocated' block before it's updated. */ if (flag != TDB_INSERT) tdb_delete_hash(tdb, key, hash); /* Copy key+value *before* allocating free space in case malloc fails and we are left with a dead spot in the tdb. */ if (!(p = (char *)talloc_size(tdb, key.dsize + dbuf.dsize))) { tdb->ecode = TDB_ERR_OOM; goto fail; } memcpy(p, key.dptr, key.dsize); if (dbuf.dsize) memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize); /* we have to allocate some space */ if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec))) goto fail; /* Read hash top into next ptr */ if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1) goto fail; rec.key_len = key.dsize; rec.data_len = dbuf.dsize; rec.full_hash = hash; rec.magic = TDB_MAGIC; /* write out and point the top of the hash chain at it */ if (rec_write(tdb, rec_ptr, &rec) == -1 || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1 || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) { /* Need to tdb_unallocate() here */ goto fail; } out: SAFE_FREE(p); tdb_unlock(tdb, BUCKET(hash), F_WRLCK); return ret; fail: ret = -1; goto out; } static int tdb_already_open(dev_t device, ino_t ino) { TDB_CONTEXT *i; for (i = tdbs; i; i = i->next) { if (i->device == device && i->inode == ino) { return 1; } } return 0; } /* a default logging function */ static void null_log_fn(TDB_CONTEXT *tdb __attribute__((unused)), int level __attribute__((unused)), const char *fmt __attribute__((unused)), ...) { } TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, tdb_log_func log_fn, tdb_hash_func hash_fn) { TDB_CONTEXT *tdb; struct stat st; int rev = 0, locked = 0; uint8_t *vp; uint32_t vertest; if (!(tdb = talloc_zero(name, TDB_CONTEXT))) { /* Can't log this */ errno = ENOMEM; goto fail; } tdb->fd = -1; tdb->name = NULL; tdb->map_ptr = NULL; tdb->flags = tdb_flags; tdb->open_flags = open_flags; tdb->log_fn = log_fn?log_fn:null_log_fn; tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash; if ((open_flags & O_ACCMODE) == O_WRONLY) { TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n", name)); errno = EINVAL; goto fail; } if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE; if ((open_flags & O_ACCMODE) == O_RDONLY) { tdb->read_only = 1; /* read only databases don't do locking or clear if first */ tdb->flags |= TDB_NOLOCK; tdb->flags &= ~TDB_CLEAR_IF_FIRST; } /* internal databases don't mmap or lock, and start off cleared */ if (tdb->flags & TDB_INTERNAL) { tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP); tdb->flags &= ~TDB_CLEAR_IF_FIRST; if (tdb_new_database(tdb, hash_size) != 0) { TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!")); goto fail; } goto internal; } if ((tdb->fd = open(name, open_flags, mode)) == -1) { TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n", name, strerror(errno))); goto fail; /* errno set by open(2) */ } /* ensure there is only one process initialising at once */ if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) { TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n", name, strerror(errno))); goto fail; /* errno set by tdb_brlock */ } /* we need to zero database if we are the only one with it open */ if ((tdb_flags & TDB_CLEAR_IF_FIRST) && (locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0))) { open_flags |= O_CREAT; if (ftruncate(tdb->fd, 0) == -1) { TDB_LOG((tdb, 0, "tdb_open_ex: " "failed to truncate %s: %s\n", name, strerror(errno))); goto fail; /* errno set by ftruncate */ } } if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header) || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0 || (tdb->header.version != TDB_VERSION && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) { /* its not a valid database - possibly initialise it */ if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) { errno = EIO; /* ie bad format or something */ goto fail; } rev = (tdb->flags & TDB_CONVERT); } vp = (uint8_t *)&tdb->header.version; vertest = (((uint32_t)vp[0]) << 24) | (((uint32_t)vp[1]) << 16) | (((uint32_t)vp[2]) << 8) | (uint32_t)vp[3]; tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0; if (!rev) tdb->flags &= ~TDB_CONVERT; else { tdb->flags |= TDB_CONVERT; convert(&tdb->header, sizeof(tdb->header)); } if (fstat(tdb->fd, &st) == -1) goto fail; /* Is it already in the open list? If so, fail. */ if (tdb_already_open(st.st_dev, st.st_ino)) { TDB_LOG((tdb, 2, "tdb_open_ex: " "%s (%d,%d) is already open in this process\n", name, (int)st.st_dev, (int)st.st_ino)); errno = EBUSY; goto fail; } if (!(tdb->name = (char *)talloc_strdup(tdb, name))) { errno = ENOMEM; goto fail; } tdb->map_size = st.st_size; tdb->device = st.st_dev; tdb->inode = st.st_ino; tdb->locked = talloc_zero_array(tdb, struct tdb_lock_type, tdb->header.hash_size+1); if (!tdb->locked) { TDB_LOG((tdb, 2, "tdb_open_ex: " "failed to allocate lock structure for %s\n", name)); errno = ENOMEM; goto fail; } tdb_mmap(tdb); if (locked) { if (tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) { TDB_LOG((tdb, 0, "tdb_open_ex: " "failed to take ACTIVE_LOCK on %s: %s\n", name, strerror(errno))); goto fail; } } /* We always need to do this if the CLEAR_IF_FIRST flag is set, even if we didn't get the initial exclusive lock as we need to let all other users know we're using it. */ if (tdb_flags & TDB_CLEAR_IF_FIRST) { /* leave this lock in place to indicate it's in use */ if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1) goto fail; } internal: /* Internal (memory-only) databases skip all the code above to * do with disk files, and resume here by releasing their * global lock and hooking into the active list. */ if (tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1) goto fail; tdb->next = tdbs; tdbs = tdb; return tdb; fail: { int save_errno = errno; if (!tdb) return NULL; if (tdb->map_ptr) { if (tdb->flags & TDB_INTERNAL) SAFE_FREE(tdb->map_ptr); else tdb_munmap(tdb); } SAFE_FREE(tdb->name); if (tdb->fd != -1) if (close(tdb->fd) != 0) TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n")); SAFE_FREE(tdb->locked); SAFE_FREE(tdb); errno = save_errno; return NULL; } } /** * Close a database. * * @returns -1 for error; 0 for success. **/ int tdb_close(TDB_CONTEXT *tdb) { TDB_CONTEXT **i; int ret = 0; if (tdb->map_ptr) { if (tdb->flags & TDB_INTERNAL) SAFE_FREE(tdb->map_ptr); else tdb_munmap(tdb); } SAFE_FREE(tdb->name); if (tdb->fd != -1) ret = close(tdb->fd); SAFE_FREE(tdb->locked); /* Remove from contexts list */ for (i = &tdbs; *i; i = &(*i)->next) { if (*i == tdb) { *i = tdb->next; break; } } memset(tdb, 0, sizeof(*tdb)); SAFE_FREE(tdb); return ret; } xen-4.9.2/tools/xenstore/talloc_guide.txt0000664000175000017500000005204313256712137016664 0ustar smbsmbUsing talloc in Samba4 ---------------------- Andrew Tridgell September 2004 The most current version of this document is available at http://samba.org/ftp/unpacked/samba4/source/lib/talloc/talloc_guide.txt If you are used to talloc from Samba3 then please read this carefully, as talloc has changed a lot. The new talloc is a hierarchical, reference counted memory pool system with destructors. Quite a mounthful really, but not too bad once you get used to it. Perhaps the biggest change from Samba3 is that there is no distinction between a "talloc context" and a "talloc pointer". Any pointer returned from talloc() is itself a valid talloc context. This means you can do this: struct foo *X = talloc(mem_ctx, struct foo); X->name = talloc_strdup(X, "foo"); and the pointer X->name would be a "child" of the talloc context "X" which is itself a child of mem_ctx. So if you do talloc_free(mem_ctx) then it is all destroyed, whereas if you do talloc_free(X) then just X and X->name are destroyed, and if you do talloc_free(X->name) then just the name element of X is destroyed. If you think about this, then what this effectively gives you is an n-ary tree, where you can free any part of the tree with talloc_free(). If you find this confusing, then I suggest you run the testsuite to watch talloc in action. You may also like to add your own tests to testsuite.c to clarify how some particular situation is handled. Performance ----------- All the additional features of talloc() over malloc() do come at a price. We have a simple performance test in Samba4 that measures talloc() versus malloc() performance, and it seems that talloc() is about 10% slower than malloc() on my x86 Debian Linux box. For Samba, the great reduction in code complexity that we get by using talloc makes this worthwhile, especially as the total overhead of talloc/malloc in Samba is already quite small. talloc API ---------- The following is a complete guide to the talloc API. Read it all at least twice. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- (type *)talloc(const void *context, type); The talloc() macro is the core of the talloc library. It takes a memory context and a type, and returns a pointer to a new area of memory of the given type. The returned pointer is itself a talloc context, so you can use it as the context argument to more calls to talloc if you wish. The returned pointer is a "child" of the supplied context. This means that if you talloc_free() the context then the new child disappears as well. Alternatively you can free just the child. The context argument to talloc() can be NULL, in which case a new top level context is created. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_size(const void *context, size_t size); The function talloc_size() should be used when you don't have a convenient type to pass to talloc(). Unlike talloc(), it is not type safe (as it returns a void *), so you are on your own for type checking. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- int talloc_free(void *ptr); The talloc_free() function frees a piece of talloc memory, and all its children. You can call talloc_free() on any pointer returned by talloc(). The return value of talloc_free() indicates success or failure, with 0 returned for success and -1 for failure. The only possible failure condition is if the pointer had a destructor attached to it and the destructor returned -1. See talloc_set_destructor() for details on destructors. If this pointer has an additional parent when talloc_free() is called then the memory is not actually released, but instead the most recently established parent is destroyed. See talloc_reference() for details on establishing additional parents. For more control on which parent is removed, see talloc_unlink() talloc_free() operates recursively on its children. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- int talloc_free_children(void *ptr); The talloc_free_children() walks along the list of all children of a talloc context and talloc_free()s only the children, not the context itself. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_reference(const void *context, const void *ptr); The talloc_reference() function makes "context" an additional parent of "ptr". The return value of talloc_reference() is always the original pointer "ptr", unless talloc ran out of memory in creating the reference in which case it will return NULL (each additional reference consumes around 48 bytes of memory on intel x86 platforms). If "ptr" is NULL, then the function is a no-op, and simply returns NULL. After creating a reference you can free it in one of the following ways: - you can talloc_free() any parent of the original pointer. That will reduce the number of parents of this pointer by 1, and will cause this pointer to be freed if it runs out of parents. - you can talloc_free() the pointer itself. That will destroy the most recently established parent to the pointer and leave the pointer as a child of its current parent. For more control on which parent to remove, see talloc_unlink() =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- int talloc_unlink(const void *context, const void *ptr); The talloc_unlink() function removes a specific parent from ptr. The context passed must either be a context used in talloc_reference() with this pointer, or must be a direct parent of ptr. Note that if the parent has already been removed using talloc_free() then this function will fail and will return -1. Likewise, if "ptr" is NULL, then the function will make no modifications and return -1. Usually you can just use talloc_free() instead of talloc_unlink(), but sometimes it is useful to have the additional control on which parent is removed. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); The function talloc_set_destructor() sets the "destructor" for the pointer "ptr". A destructor is a function that is called when the memory used by a pointer is about to be released. The destructor receives the pointer as an argument, and should return 0 for success and -1 for failure. The destructor can do anything it wants to, including freeing other pieces of memory. A common use for destructors is to clean up operating system resources (such as open file descriptors) contained in the structure the destructor is placed on. You can only place one destructor on a pointer. If you need more than one destructor then you can create a zero-length child of the pointer and place an additional destructor on that. To remove a destructor call talloc_set_destructor() with NULL for the destructor. If your destructor attempts to talloc_free() the pointer that it is the destructor for then talloc_free() will return -1 and the free will be ignored. This would be a pointless operation anyway, as the destructor is only called when the memory is just about to go away. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void talloc_increase_ref_count(const void *ptr); The talloc_increase_ref_count(ptr) function is exactly equivalent to: talloc_reference(NULL, ptr); You can use either syntax, depending on which you think is clearer in your code. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void talloc_set_name(const void *ptr, const char *fmt, ...); Each talloc pointer has a "name". The name is used principally for debugging purposes, although it is also possible to set and get the name on a pointer in as a way of "marking" pointers in your code. The main use for names on pointer is for "talloc reports". See talloc_report() and talloc_report_full() for details. Also see talloc_enable_leak_report() and talloc_enable_leak_report_full(). The talloc_set_name() function allocates memory as a child of the pointer. It is logically equivalent to: talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...)); Note that multiple calls to talloc_set_name() will allocate more memory without releasing the name. All of the memory is released when the ptr is freed using talloc_free(). =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void talloc_set_name_const(const void *ptr, const char *name); The function talloc_set_name_const() is just like talloc_set_name(), but it takes a string constant, and is much faster. It is extensively used by the "auto naming" macros, such as talloc_p(). This function does not allocate any memory. It just copies the supplied pointer into the internal representation of the talloc ptr. This means you must not pass a name pointer to memory that will disappear before the ptr is freed with talloc_free(). =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_named(const void *context, size_t size, const char *fmt, ...); The talloc_named() function creates a named talloc pointer. It is equivalent to: ptr = talloc_size(context, size); talloc_set_name(ptr, fmt, ....); =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_named_const(const void *context, size_t size, const char *name); This is equivalent to: ptr = talloc_size(context, size); talloc_set_name_const(ptr, name); =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- const char *talloc_get_name(const void *ptr); This returns the current name for the given talloc pointer. See talloc_set_name() for details. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_init(const char *fmt, ...); This function creates a zero length named talloc context as a top level context. It is equivalent to: talloc_named(NULL, 0, fmt, ...); =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_new(void *ctx); This is a utility macro that creates a new memory context hanging off an exiting context, automatically naming it "talloc_new: __location__" where __location__ is the source line it is called from. It is particularly useful for creating a new temporary working context. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- (type *)talloc_realloc(const void *context, void *ptr, type, count); The talloc_realloc() macro changes the size of a talloc pointer. The "count" argument is the number of elements of type "type" that you want the resulting pointer to hold. talloc_realloc() has the following equivalences: talloc_realloc(context, NULL, type, 1) ==> talloc(context, type); talloc_realloc(context, NULL, type, N) ==> talloc_array(context, type, N); talloc_realloc(context, ptr, type, 0) ==> talloc_free(ptr); The "context" argument is only used if "ptr" is not NULL, otherwise it is ignored. talloc_realloc() returns the new pointer, or NULL on failure. The call will fail either due to a lack of memory, or because the pointer has more than one parent (see talloc_reference()). =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_realloc_size(const void *context, void *ptr, size_t size); the talloc_realloc_size() function is useful when the type is not known so the typesafe talloc_realloc() cannot be used. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_steal(const void *new_ctx, const void *ptr); The talloc_steal() function changes the parent context of a talloc pointer. It is typically used when the context that the pointer is currently a child of is going to be freed and you wish to keep the memory for a longer time. The talloc_steal() function returns the pointer that you pass it. It does not have any failure modes. NOTE: It is possible to produce loops in the parent/child relationship if you are not careful with talloc_steal(). No guarantees are provided as to your sanity or the safety of your data if you do this. talloc_steal (new_ctx, NULL) will return NULL with no sideeffects. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- off_t talloc_total_size(const void *ptr); The talloc_total_size() function returns the total size in bytes used by this pointer and all child pointers. Mostly useful for debugging. Passing NULL is allowed, but it will only give a meaningful result if talloc_enable_leak_report() or talloc_enable_leak_report_full() has been called. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- off_t talloc_total_blocks(const void *ptr); The talloc_total_blocks() function returns the total memory block count used by this pointer and all child pointers. Mostly useful for debugging. Passing NULL is allowed, but it will only give a meaningful result if talloc_enable_leak_report() or talloc_enable_leak_report_full() has been called. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void talloc_report(const void *ptr, FILE *f); The talloc_report() function prints a summary report of all memory used by ptr. One line of report is printed for each immediate child of ptr, showing the total memory and number of blocks used by that child. You can pass NULL for the pointer, in which case a report is printed for the top level memory context, but only if talloc_enable_leak_report() or talloc_enable_leak_report_full() has been called. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void talloc_report_full(const void *ptr, FILE *f); This provides a more detailed report than talloc_report(). It will recursively print the ensire tree of memory referenced by the pointer. References in the tree are shown by giving the name of the pointer that is referenced. You can pass NULL for the pointer, in which case a report is printed for the top level memory context, but only if talloc_enable_leak_report() or talloc_enable_leak_report_full() has been called. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void talloc_enable_leak_report(void); This enables calling of talloc_report(NULL, stderr) when the program exits. In Samba4 this is enabled by using the --leak-report command line option. For it to be useful, this function must be called before any other talloc function as it establishes a "null context" that acts as the top of the tree. If you don't call this function first then passing NULL to talloc_report() or talloc_report_full() won't give you the full tree printout. Here is a typical talloc report: talloc report on 'null_context' (total 267 bytes in 15 blocks) libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks iconv(UTF8,CP850) contains 42 bytes in 2 blocks libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks iconv(CP850,UTF8) contains 42 bytes in 2 blocks iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void talloc_enable_leak_report_full(void); This enables calling of talloc_report_full(NULL, stderr) when the program exits. In Samba4 this is enabled by using the --leak-report-full command line option. For it to be useful, this function must be called before any other talloc function as it establishes a "null context" that acts as the top of the tree. If you don't call this function first then passing NULL to talloc_report() or talloc_report_full() won't give you the full tree printout. Here is a typical full report: full talloc report on 'root' (total 18 bytes in 8 blocks) p1 contains 18 bytes in 7 blocks (ref 0) r1 contains 13 bytes in 2 blocks (ref 0) reference to: p2 p2 contains 1 bytes in 1 blocks (ref 1) x3 contains 1 bytes in 1 blocks (ref 0) x2 contains 1 bytes in 1 blocks (ref 0) x1 contains 1 bytes in 1 blocks (ref 0) =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void talloc_enable_null_tracking(void); This enables tracking of the NULL memory context without enabling leak reporting on exit. Useful for when you want to do your own leak reporting call via talloc_report_null_full(); =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- (type *)talloc_zero(const void *ctx, type); The talloc_zero() macro is equivalent to: ptr = talloc(ctx, type); if (ptr) memset(ptr, 0, sizeof(type)); =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_zero_size(const void *ctx, size_t size) The talloc_zero_size() function is useful when you don't have a known type =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_memdup(const void *ctx, const void *p, size_t size); The talloc_memdup() function is equivalent to: ptr = talloc_size(ctx, size); if (ptr) memcpy(ptr, p, size); =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- char *talloc_strdup(const void *ctx, const char *p); The talloc_strdup() function is equivalent to: ptr = talloc_size(ctx, strlen(p)+1); if (ptr) memcpy(ptr, p, strlen(p)+1); This functions sets the name of the new pointer to the passed string. This is equivalent to: talloc_set_name_const(ptr, ptr) =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- char *talloc_strndup(const void *t, const char *p, size_t n); The talloc_strndup() function is the talloc equivalent of the C library function strndup() This functions sets the name of the new pointer to the passed string. This is equivalent to: talloc_set_name_const(ptr, ptr) =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- char *talloc_vasprintf(const void *t, const char *fmt, va_list ap); The talloc_vasprintf() function is the talloc equivalent of the C library function vasprintf() =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- char *talloc_asprintf(const void *t, const char *fmt, ...); The talloc_asprintf() function is the talloc equivalent of the C library function asprintf() This functions sets the name of the new pointer to the passed string. This is equivalent to: talloc_set_name_const(ptr, ptr) =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- char *talloc_asprintf_append(char *s, const char *fmt, ...); The talloc_asprintf_append() function appends the given formatted string to the given string. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- (type *)talloc_array(const void *ctx, type, uint_t count); The talloc_array() macro is equivalent to: (type *)talloc_size(ctx, sizeof(type) * count); except that it provides integer overflow protection for the multiply, returning NULL if the multiply overflows. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_array_size(const void *ctx, size_t size, uint_t count); The talloc_array_size() function is useful when the type is not known. It operates in the same way as talloc_array(), but takes a size instead of a type. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_realloc_fn(const void *ctx, void *ptr, size_t size); This is a non-macro version of talloc_realloc(), which is useful as libraries sometimes want a ralloc function pointer. A realloc() implementation encapsulates the functionality of malloc(), free() and realloc() in one call, which is why it is useful to be able to pass around a single function pointer. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_autofree_context(void); This is a handy utility function that returns a talloc context which will be automatically freed on program exit. This can be used to reduce the noise in memory leak reports. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- void *talloc_check_name(const void *ptr, const char *name); This function checks if a pointer has the specified name. If it does then the pointer is returned. It it doesn't then NULL is returned. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- (type *)talloc_get_type(const void *ptr, type); This macro allows you to do type checking on talloc pointers. It is particularly useful for void* private pointers. It is equivalent to this: (type *)talloc_check_name(ptr, #type) =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- talloc_set_type(const void *ptr, type); This macro allows you to force the name of a pointer to be a particular type. This can be used in conjunction with talloc_get_type() to do type checking on void* pointers. It is equivalent to this: talloc_set_name_const(ptr, #type) =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- talloc_get_size(const void *ctx); This function lets you know the amount of memory alloced so far by this context. It does NOT account for subcontext memory. This can be used to calculate the size of an array. xen-4.9.2/tools/xenstore/xenstored_control.h0000664000175000017500000000147313256712137017415 0ustar smbsmb/* Interactive commands for Xen Store Daemon. Copyright (C) 2017 Juergen Gross, SUSE Linux GmbH This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ int do_control(struct connection *conn, struct buffered_data *in); xen-4.9.2/tools/xenstore/talloc.h0000664000175000017500000001462513256712137015123 0ustar smbsmb#ifndef _TALLOC_H_ #define _TALLOC_H_ /* Unix SMB/CIFS implementation. Samba temporary memory allocation functions Copyright (C) Andrew Tridgell 2004-2005 ** NOTE! The following LGPL license applies to the talloc ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . */ #include /* this is only needed for compatibility with the old talloc */ typedef void TALLOC_CTX; /* this uses a little trick to allow __LINE__ to be stringified */ #define _STRING_LINE_(s) #s #define _STRING_LINE2_(s) _STRING_LINE_(s) #define __LINESTR__ _STRING_LINE2_(__LINE__) #define __location__ __FILE__ ":" __LINESTR__ #ifndef TALLOC_DEPRECATED #define TALLOC_DEPRECATED 0 #endif /* useful macros for creating type checked pointers */ #define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) #define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) #define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) #define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) #define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) #define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) #define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) #define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) #define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) #define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) #define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) #define malloc_p(type) (type *)malloc(sizeof(type)) #define malloc_array_p(type, count) (type *)realloc_array(NULL, sizeof(type), count) #define realloc_p(p, type, count) (type *)realloc_array(p, sizeof(type), count) #if 0 /* Not correct for Samba3. */ #define data_blob(ptr, size) data_blob_named(ptr, size, "DATA_BLOB: "__location__) #define data_blob_talloc(ctx, ptr, size) data_blob_talloc_named(ctx, ptr, size, "DATA_BLOB: "__location__) #define data_blob_dup_talloc(ctx, blob) data_blob_talloc_named(ctx, (blob)->data, (blob)->length, "DATA_BLOB: "__location__) #endif #define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) #define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) #define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) #if TALLOC_DEPRECATED #define talloc_zero_p(ctx, type) talloc_zero(ctx, type) #define talloc_p(ctx, type) talloc(ctx, type) #define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) #define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) #define talloc_destroy(ctx) talloc_free(ctx) #endif #ifndef PRINTF_ATTRIBUTE #if (__GNUC__ >= 3) /** Use gcc attribute to check printf fns. a1 is the 1-based index of * the parameter containing the format, and a2 the index of the first * argument. Note that some gcc 2.x versions don't handle this * properly **/ #define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) #else #define PRINTF_ATTRIBUTE(a1, a2) #endif #endif /* The following definitions come from talloc.c */ void *_talloc(const void *context, size_t size); void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); void talloc_increase_ref_count(const void *ptr); void *talloc_reference(const void *context, const void *ptr); int talloc_unlink(const void *context, void *ptr); void talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); void talloc_set_name_const(const void *ptr, const char *name); void *talloc_named(const void *context, size_t size, const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); void *talloc_named_const(const void *context, size_t size, const char *name); const char *talloc_get_name(const void *ptr); void *talloc_check_name(const void *ptr, const char *name); void talloc_report_depth(const void *ptr, FILE *f, int depth); void *talloc_parent(const void *ptr); void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); int talloc_free(void *ptr); void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); void *talloc_steal(const void *new_ctx, const void *ptr); off_t talloc_total_size(const void *ptr); off_t talloc_total_blocks(const void *ptr); void talloc_report_full(const void *ptr, FILE *f); void talloc_report(const void *ptr, FILE *f); void talloc_enable_null_tracking(void); void talloc_enable_leak_report(void); void talloc_enable_leak_report_full(void); void *_talloc_zero(const void *ctx, size_t size, const char *name); void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); char *talloc_strdup(const void *t, const char *p); char *talloc_strndup(const void *t, const char *p, size_t n); char *talloc_append_string(const void *t, char *orig, const char *append); char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); void *talloc_realloc_fn(const void *context, void *ptr, size_t size); void *talloc_autofree_context(void); size_t talloc_get_size(const void *ctx); void *talloc_find_parent_byname(const void *ctx, const char *name); void talloc_show_parents(const void *context, FILE *file); #endif xen-4.9.2/tools/xenstore/xs.c0000664000175000017500000010051113256712137014260 0ustar smbsmb/* Xen Store Daemon interface providing simple tree-like database. Copyright (C) 2005 Rusty Russell IBM Corporation This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xenstore.h" #include "list.h" #include "utils.h" struct xs_stored_msg { struct list_head list; struct xsd_sockmsg hdr; char *body; }; #ifdef USE_PTHREAD #include struct xs_handle { /* Communications channel to xenstore daemon. */ int fd; /* * A read thread which pulls messages off the comms channel and * signals waiters. */ pthread_t read_thr; int read_thr_exists; /* * A list of fired watch messages, protected by a mutex. Users can * wait on the conditional variable until a watch is pending. */ struct list_head watch_list; pthread_mutex_t watch_mutex; pthread_cond_t watch_condvar; /* Clients can select() on this pipe to wait for a watch to fire. */ int watch_pipe[2]; /* Filtering watch event in unwatch function? */ bool unwatch_filter; /* * A list of replies. Currently only one will ever be outstanding * because we serialise requests. The requester can wait on the * conditional variable for its response. */ struct list_head reply_list; pthread_mutex_t reply_mutex; pthread_cond_t reply_condvar; /* One request at a time. */ pthread_mutex_t request_mutex; /* Lock discipline: * Only holder of the request lock may write to h->fd. * Only holder of the request lock may access read_thr_exists. * If read_thr_exists==0, only holder of request lock may read h->fd; * If read_thr_exists==1, only the read thread may read h->fd. * Only holder of the reply lock may access reply_list. * Only holder of the watch lock may access watch_list. * Lock hierarchy: * The order in which to acquire locks is * request_mutex * reply_mutex * watch_mutex */ }; #define mutex_lock(m) pthread_mutex_lock(m) #define mutex_unlock(m) pthread_mutex_unlock(m) #define condvar_signal(c) pthread_cond_signal(c) #define condvar_wait(c,m) pthread_cond_wait(c,m) #define cleanup_push(f, a) \ pthread_cleanup_push((void (*)(void *))(f), (void *)(a)) /* * Some definitions of pthread_cleanup_pop() are a macro starting with an * end-brace. GCC then complains if we immediately precede that with a label. * Hence we insert a dummy statement to appease the compiler in this situation. */ #define cleanup_pop(run) ((void)0); pthread_cleanup_pop(run) #define read_thread_exists(h) (h->read_thr_exists) /* Because pthread_cleanup_p* are not available when USE_PTHREAD is * disabled, use these macros which convert appropriately. */ #define cleanup_push_heap(p) cleanup_push(free, p) #define cleanup_pop_heap(run, p) cleanup_pop((run)) static void *read_thread(void *arg); #else /* !defined(USE_PTHREAD) */ struct xs_handle { int fd; struct list_head reply_list; struct list_head watch_list; /* Clients can select() on this pipe to wait for a watch to fire. */ int watch_pipe[2]; /* Filtering watch event in unwatch function? */ bool unwatch_filter; }; #define mutex_lock(m) ((void)0) #define mutex_unlock(m) ((void)0) #define condvar_signal(c) ((void)0) #define condvar_wait(c,m) ((void)0) #define cleanup_push(f, a) ((void)0) #define cleanup_pop(run) ((void)0) #define read_thread_exists(h) (0) #define cleanup_push_heap(p) ((void)0) #define cleanup_pop_heap(run, p) do { if ((run)) free(p); } while(0) #endif static int read_message(struct xs_handle *h, int nonblocking); static bool setnonblock(int fd, int nonblock) { int flags = fcntl(fd, F_GETFL); if (flags == -1) return false; if (nonblock) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) return false; return true; } int xs_fileno(struct xs_handle *h) { char c = 0; mutex_lock(&h->watch_mutex); if ((h->watch_pipe[0] == -1) && (pipe(h->watch_pipe) != -1)) { /* Kick things off if the watch list is already non-empty. */ if (!list_empty(&h->watch_list)) while (write(h->watch_pipe[1], &c, 1) != 1) continue; } mutex_unlock(&h->watch_mutex); return h->watch_pipe[0]; } static int get_socket(const char *connect_to) { struct sockaddr_un addr; int sock, saved_errno, flags; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) return -1; if ((flags = fcntl(sock, F_GETFD)) < 0) goto error; flags |= FD_CLOEXEC; if (fcntl(sock, F_SETFD, flags) < 0) goto error; addr.sun_family = AF_UNIX; if(strlen(connect_to) >= sizeof(addr.sun_path)) { errno = EINVAL; goto error; } strcpy(addr.sun_path, connect_to); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) goto error; return sock; error: saved_errno = errno; close(sock); errno = saved_errno; return -1; } static int get_dev(const char *connect_to) { /* We cannot open read-only because requests are writes */ return open(connect_to, O_RDWR); } static struct xs_handle *get_handle(const char *connect_to) { struct stat buf; struct xs_handle *h = NULL; int fd = -1, saved_errno; if (stat(connect_to, &buf) != 0) return NULL; if (S_ISSOCK(buf.st_mode)) fd = get_socket(connect_to); else fd = get_dev(connect_to); if (fd == -1) return NULL; h = malloc(sizeof(*h)); if (h == NULL) { saved_errno = errno; close(fd); errno = saved_errno; return NULL; } memset(h, 0, sizeof(*h)); h->fd = fd; INIT_LIST_HEAD(&h->reply_list); INIT_LIST_HEAD(&h->watch_list); /* Watch pipe is allocated on demand in xs_fileno(). */ h->watch_pipe[0] = h->watch_pipe[1] = -1; h->unwatch_filter = false; #ifdef USE_PTHREAD pthread_mutex_init(&h->watch_mutex, NULL); pthread_cond_init(&h->watch_condvar, NULL); pthread_mutex_init(&h->reply_mutex, NULL); pthread_cond_init(&h->reply_condvar, NULL); pthread_mutex_init(&h->request_mutex, NULL); #endif return h; } struct xs_handle *xs_daemon_open(void) { return xs_open(0); } struct xs_handle *xs_daemon_open_readonly(void) { return xs_open(XS_OPEN_READONLY); } struct xs_handle *xs_domain_open(void) { return xs_open(0); } struct xs_handle *xs_open(unsigned long flags) { struct xs_handle *xsh = NULL; if (flags & XS_OPEN_READONLY) xsh = get_handle(xs_daemon_socket_ro()); else xsh = get_handle(xs_daemon_socket()); if (!xsh && !(flags & XS_OPEN_SOCKETONLY)) xsh = get_handle(xs_domain_dev()); if (xsh && (flags & XS_UNWATCH_FILTER)) xsh->unwatch_filter = true; return xsh; } static void close_free_msgs(struct xs_handle *h) { struct xs_stored_msg *msg, *tmsg; list_for_each_entry_safe(msg, tmsg, &h->reply_list, list) { free(msg->body); free(msg); } list_for_each_entry_safe(msg, tmsg, &h->watch_list, list) { free(msg->body); free(msg); } } static void close_fds_free(struct xs_handle *h) { if (h->watch_pipe[0] != -1) { close(h->watch_pipe[0]); close(h->watch_pipe[1]); } close(h->fd); free(h); } void xs_daemon_destroy_postfork(struct xs_handle *h) { close_free_msgs(h); close_fds_free(h); } void xs_daemon_close(struct xs_handle *h) { #ifdef USE_PTHREAD if (h->read_thr_exists) { pthread_cancel(h->read_thr); pthread_join(h->read_thr, NULL); } #endif mutex_lock(&h->request_mutex); mutex_lock(&h->reply_mutex); mutex_lock(&h->watch_mutex); close_free_msgs(h); mutex_unlock(&h->request_mutex); mutex_unlock(&h->reply_mutex); mutex_unlock(&h->watch_mutex); close_fds_free(h); } void xs_close(struct xs_handle* xsh) { if (xsh) xs_daemon_close(xsh); } static bool read_all(int fd, void *data, unsigned int len, int nonblocking) /* With nonblocking, either reads either everything requested, * or nothing. */ { if (!len) return true; if (nonblocking && !setnonblock(fd, 1)) return false; while (len) { int done; done = read(fd, data, len); if (done < 0) { if (errno == EINTR) continue; goto out_false; } if (done == 0) { /* It closed fd on us? EBADF is appropriate. */ errno = EBADF; goto out_false; } data += done; len -= done; if (nonblocking) { nonblocking = 0; if (!setnonblock(fd, 0)) goto out_false; } } return true; out_false: if (nonblocking) setnonblock(fd, 0); return false; } #ifdef XSTEST #define read_all read_all_choice #define xs_write_all write_all_choice #endif static int get_error(const char *errorstring) { unsigned int i; for (i = 0; !streq(errorstring, xsd_errors[i].errstring); i++) if (i == ARRAY_SIZE(xsd_errors) - 1) return EINVAL; return xsd_errors[i].errnum; } /* Adds extra nul terminator, because we generally (always?) hold strings. */ static void *read_reply( struct xs_handle *h, enum xsd_sockmsg_type *type, unsigned int *len) { struct xs_stored_msg *msg; char *body; int read_from_thread; read_from_thread = read_thread_exists(h); /* Read from comms channel ourselves if there is no reader thread. */ if (!read_from_thread && (read_message(h, 0) == -1)) return NULL; mutex_lock(&h->reply_mutex); #ifdef USE_PTHREAD while (list_empty(&h->reply_list) && read_from_thread && h->fd != -1) condvar_wait(&h->reply_condvar, &h->reply_mutex); #endif if (list_empty(&h->reply_list)) { mutex_unlock(&h->reply_mutex); errno = EINVAL; return NULL; } msg = list_top(&h->reply_list, struct xs_stored_msg, list); list_del(&msg->list); assert(list_empty(&h->reply_list)); mutex_unlock(&h->reply_mutex); *type = msg->hdr.type; if (len) *len = msg->hdr.len; body = msg->body; free(msg); return body; } /* Send message to xs, get malloc'ed reply. NULL and set errno on error. */ static void *xs_talkv(struct xs_handle *h, xs_transaction_t t, enum xsd_sockmsg_type type, const struct iovec *iovec, unsigned int num_vecs, unsigned int *len) { struct xsd_sockmsg msg; void *ret = NULL; int saved_errno; unsigned int i; struct sigaction ignorepipe, oldact; msg.tx_id = t; msg.req_id = 0; msg.type = type; msg.len = 0; for (i = 0; i < num_vecs; i++) msg.len += iovec[i].iov_len; if (msg.len > XENSTORE_PAYLOAD_MAX) { errno = E2BIG; return 0; } ignorepipe.sa_handler = SIG_IGN; sigemptyset(&ignorepipe.sa_mask); ignorepipe.sa_flags = 0; sigaction(SIGPIPE, &ignorepipe, &oldact); mutex_lock(&h->request_mutex); if (!xs_write_all(h->fd, &msg, sizeof(msg))) goto fail; for (i = 0; i < num_vecs; i++) if (!xs_write_all(h->fd, iovec[i].iov_base, iovec[i].iov_len)) goto fail; ret = read_reply(h, &msg.type, len); if (!ret) goto fail; mutex_unlock(&h->request_mutex); sigaction(SIGPIPE, &oldact, NULL); if (msg.type == XS_ERROR) { saved_errno = get_error(ret); free(ret); errno = saved_errno; return NULL; } if (msg.type != type) { free(ret); saved_errno = EBADF; goto close_fd; } return ret; fail: /* We're in a bad state, so close fd. */ saved_errno = errno; mutex_unlock(&h->request_mutex); sigaction(SIGPIPE, &oldact, NULL); close_fd: close(h->fd); h->fd = -1; errno = saved_errno; return NULL; } /* free(), but don't change errno. */ static void free_no_errno(void *p) { int saved_errno = errno; free(p); errno = saved_errno; } /* Simplified version of xs_talkv: single message. */ static void *xs_single(struct xs_handle *h, xs_transaction_t t, enum xsd_sockmsg_type type, const char *string, unsigned int *len) { struct iovec iovec; iovec.iov_base = (void *)string; iovec.iov_len = strlen(string) + 1; return xs_talkv(h, t, type, &iovec, 1, len); } static bool xs_bool(char *reply) { if (!reply) return false; free(reply); return true; } static char **xs_directory_common(char *strings, unsigned int len, unsigned int *num) { char *p, **ret; /* Count the strings. */ *num = xs_count_strings(strings, len); /* Transfer to one big alloc for easy freeing. */ ret = malloc(*num * sizeof(char *) + len); if (!ret) { free_no_errno(strings); return NULL; } memcpy(&ret[*num], strings, len); free_no_errno(strings); strings = (char *)&ret[*num]; for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1) ret[(*num)++] = p; return ret; } static char **xs_directory_part(struct xs_handle *h, xs_transaction_t t, const char *path, unsigned int *num) { unsigned int off, result_len; char gen[24], offstr[8]; struct iovec iovec[2]; char *result = NULL, *strings = NULL; memset(gen, 0, sizeof(gen)); iovec[0].iov_base = (void *)path; iovec[0].iov_len = strlen(path) + 1; for (off = 0;;) { snprintf(offstr, sizeof(offstr), "%u", off); iovec[1].iov_base = (void *)offstr; iovec[1].iov_len = strlen(offstr) + 1; result = xs_talkv(h, t, XS_DIRECTORY_PART, iovec, 2, &result_len); /* If XS_DIRECTORY_PART isn't supported return E2BIG. */ if (!result) { if (errno == ENOSYS) errno = E2BIG; return NULL; } if (off) { if (strcmp(gen, result)) { free(result); free(strings); strings = NULL; off = 0; continue; } } else strncpy(gen, result, sizeof(gen) - 1); result_len -= strlen(result) + 1; strings = realloc(strings, off + result_len); memcpy(strings + off, result + strlen(result) + 1, result_len); free(result); off += result_len; if (off <= 1 || strings[off - 2] == 0) break; } if (off > 1) off--; return xs_directory_common(strings, off, num); } char **xs_directory(struct xs_handle *h, xs_transaction_t t, const char *path, unsigned int *num) { char *strings; unsigned int len; strings = xs_single(h, t, XS_DIRECTORY, path, &len); if (!strings) { if (errno != E2BIG) return NULL; return xs_directory_part(h, t, path, num); } return xs_directory_common(strings, len, num); } /* Get the value of a single file, nul terminated. * Returns a malloced value: call free() on it after use. * len indicates length in bytes, not including the nul. */ void *xs_read(struct xs_handle *h, xs_transaction_t t, const char *path, unsigned int *len) { return xs_single(h, t, XS_READ, path, len); } /* Write the value of a single file. * Returns false on failure. */ bool xs_write(struct xs_handle *h, xs_transaction_t t, const char *path, const void *data, unsigned int len) { struct iovec iovec[2]; iovec[0].iov_base = (void *)path; iovec[0].iov_len = strlen(path) + 1; iovec[1].iov_base = (void *)data; iovec[1].iov_len = len; return xs_bool(xs_talkv(h, t, XS_WRITE, iovec, ARRAY_SIZE(iovec), NULL)); } /* Create a new directory. * Returns false on failure, or success if it already exists. */ bool xs_mkdir(struct xs_handle *h, xs_transaction_t t, const char *path) { return xs_bool(xs_single(h, t, XS_MKDIR, path, NULL)); } /* Destroy a file or directory (directories must be empty). * Returns false on failure, or success if it doesn't exist. */ bool xs_rm(struct xs_handle *h, xs_transaction_t t, const char *path) { return xs_bool(xs_single(h, t, XS_RM, path, NULL)); } /* Get permissions of node (first element is owner). * Returns malloced array, or NULL: call free() after use. */ struct xs_permissions *xs_get_permissions(struct xs_handle *h, xs_transaction_t t, const char *path, unsigned int *num) { char *strings; unsigned int len; struct xs_permissions *ret; strings = xs_single(h, t, XS_GET_PERMS, path, &len); if (!strings) return NULL; /* Count the strings: each one perms then domid. */ *num = xs_count_strings(strings, len); /* Transfer to one big alloc for easy freeing. */ ret = malloc(*num * sizeof(struct xs_permissions)); if (!ret) { free_no_errno(strings); return NULL; } if (!xs_strings_to_perms(ret, *num, strings)) { free_no_errno(ret); ret = NULL; } free(strings); return ret; } /* Set permissions of node (must be owner). * Returns false on failure. */ bool xs_set_permissions(struct xs_handle *h, xs_transaction_t t, const char *path, struct xs_permissions *perms, unsigned int num_perms) { unsigned int i; struct iovec iov[1+num_perms]; iov[0].iov_base = (void *)path; iov[0].iov_len = strlen(path) + 1; for (i = 0; i < num_perms; i++) { char buffer[MAX_STRLEN(unsigned int)+1]; if (!xs_perm_to_string(&perms[i], buffer, sizeof(buffer))) goto unwind; iov[i+1].iov_base = strdup(buffer); iov[i+1].iov_len = strlen(buffer) + 1; if (!iov[i+1].iov_base) goto unwind; } if (!xs_bool(xs_talkv(h, t, XS_SET_PERMS, iov, 1+num_perms, NULL))) goto unwind; for (i = 0; i < num_perms; i++) free(iov[i+1].iov_base); return true; unwind: num_perms = i; for (i = 0; i < num_perms; i++) free_no_errno(iov[i+1].iov_base); return false; } /* Watch a node for changes (poll on fd to detect, or call read_watch()). * When the node (or any child) changes, fd will become readable. * Token is returned when watch is read, to allow matching. * Returns false on failure. */ bool xs_watch(struct xs_handle *h, const char *path, const char *token) { struct iovec iov[2]; #ifdef USE_PTHREAD #define DEFAULT_THREAD_STACKSIZE (16 * 1024) #define READ_THREAD_STACKSIZE \ ((DEFAULT_THREAD_STACKSIZE < PTHREAD_STACK_MIN) ? \ PTHREAD_STACK_MIN : DEFAULT_THREAD_STACKSIZE) /* We dynamically create a reader thread on demand. */ mutex_lock(&h->request_mutex); if (!h->read_thr_exists) { sigset_t set, old_set; pthread_attr_t attr; if (pthread_attr_init(&attr) != 0) { mutex_unlock(&h->request_mutex); return false; } if (pthread_attr_setstacksize(&attr, READ_THREAD_STACKSIZE) != 0) { pthread_attr_destroy(&attr); mutex_unlock(&h->request_mutex); return false; } sigfillset(&set); pthread_sigmask(SIG_SETMASK, &set, &old_set); if (pthread_create(&h->read_thr, &attr, read_thread, h) != 0) { pthread_sigmask(SIG_SETMASK, &old_set, NULL); pthread_attr_destroy(&attr); mutex_unlock(&h->request_mutex); return false; } h->read_thr_exists = 1; pthread_sigmask(SIG_SETMASK, &old_set, NULL); pthread_attr_destroy(&attr); } mutex_unlock(&h->request_mutex); #endif iov[0].iov_base = (void *)path; iov[0].iov_len = strlen(path) + 1; iov[1].iov_base = (void *)token; iov[1].iov_len = strlen(token) + 1; return xs_bool(xs_talkv(h, XBT_NULL, XS_WATCH, iov, ARRAY_SIZE(iov), NULL)); } /* Clear the pipe token if there are no more pending watchs. * We suppose the watch_mutex is already taken. */ static void xs_maybe_clear_watch_pipe(struct xs_handle *h) { char c; if (list_empty(&h->watch_list) && (h->watch_pipe[0] != -1)) while (read(h->watch_pipe[0], &c, 1) != 1) continue; } /* Find out what node change was on (will block if nothing pending). * Returns array of two pointers: path and token, or NULL. * Call free() after use. */ static char **read_watch_internal(struct xs_handle *h, unsigned int *num, int nonblocking) { struct xs_stored_msg *msg; char **ret, *strings; unsigned int num_strings, i; mutex_lock(&h->watch_mutex); #ifdef USE_PTHREAD /* Wait on the condition variable for a watch to fire. * If the reader thread doesn't exist yet, then that's because * we haven't called xs_watch. Presumably the application * will do so later; in the meantime we just block. */ while (list_empty(&h->watch_list) && h->fd != -1) { if (nonblocking) { mutex_unlock(&h->watch_mutex); errno = EAGAIN; return 0; } condvar_wait(&h->watch_condvar, &h->watch_mutex); } #else /* !defined(USE_PTHREAD) */ /* Read from comms channel ourselves if there are no threads * and therefore no reader thread. */ assert(!read_thread_exists(h)); /* not threadsafe but worth a check */ if ((read_message(h, nonblocking) == -1)) return NULL; #endif /* !defined(USE_PTHREAD) */ if (list_empty(&h->watch_list)) { mutex_unlock(&h->watch_mutex); errno = EINVAL; return NULL; } msg = list_top(&h->watch_list, struct xs_stored_msg, list); list_del(&msg->list); xs_maybe_clear_watch_pipe(h); mutex_unlock(&h->watch_mutex); assert(msg->hdr.type == XS_WATCH_EVENT); strings = msg->body; num_strings = xs_count_strings(strings, msg->hdr.len); ret = malloc(sizeof(char*) * num_strings + msg->hdr.len); if (!ret) { free_no_errno(strings); free_no_errno(msg); return NULL; } ret[0] = (char *)(ret + num_strings); memcpy(ret[0], strings, msg->hdr.len); free(strings); free(msg); for (i = 1; i < num_strings; i++) ret[i] = ret[i - 1] + strlen(ret[i - 1]) + 1; *num = num_strings; return ret; } char **xs_check_watch(struct xs_handle *h) { unsigned int num; char **ret; ret = read_watch_internal(h, &num, 1); if (ret) assert(num >= 2); return ret; } /* Find out what node change was on (will block if nothing pending). * Returns array of two pointers: path and token, or NULL. * Call free() after use. */ char **xs_read_watch(struct xs_handle *h, unsigned int *num) { return read_watch_internal(h, num, 0); } /* Remove a watch on a node. * Returns false on failure (no watch on that node). */ bool xs_unwatch(struct xs_handle *h, const char *path, const char *token) { struct iovec iov[2]; struct xs_stored_msg *msg, *tmsg; bool res; char *s, *p; unsigned int i; char *l_token, *l_path; iov[0].iov_base = (char *)path; iov[0].iov_len = strlen(path) + 1; iov[1].iov_base = (char *)token; iov[1].iov_len = strlen(token) + 1; res = xs_bool(xs_talkv(h, XBT_NULL, XS_UNWATCH, iov, ARRAY_SIZE(iov), NULL)); if (!h->unwatch_filter) /* Don't filter the watch list */ return res; /* Filter the watch list to remove potential message */ mutex_lock(&h->watch_mutex); if (list_empty(&h->watch_list)) { mutex_unlock(&h->watch_mutex); return res; } list_for_each_entry_safe(msg, tmsg, &h->watch_list, list) { assert(msg->hdr.type == XS_WATCH_EVENT); s = msg->body; l_token = NULL; l_path = NULL; for (p = s, i = 0; p < msg->body + msg->hdr.len; p++) { if (*p == '\0') { if (i == XS_WATCH_TOKEN) l_token = s; else if (i == XS_WATCH_PATH) l_path = s; i++; s = p + 1; } } if (l_token && !strcmp(token, l_token) && l_path && xs_path_is_subpath(path, l_path)) { list_del(&msg->list); free(msg); } } xs_maybe_clear_watch_pipe(h); mutex_unlock(&h->watch_mutex); return res; } /* Start a transaction: changes by others will not be seen during this * transaction, and changes will not be visible to others until end. * Returns XBT_NULL on failure. */ xs_transaction_t xs_transaction_start(struct xs_handle *h) { char *id_str; xs_transaction_t id; id_str = xs_single(h, XBT_NULL, XS_TRANSACTION_START, "", NULL); if (id_str == NULL) return XBT_NULL; id = strtoul(id_str, NULL, 0); free(id_str); return id; } /* End a transaction. * If abandon is true, transaction is discarded instead of committed. * Returns false on failure, which indicates an error: transactions will * not fail spuriously. */ bool xs_transaction_end(struct xs_handle *h, xs_transaction_t t, bool abort) { char abortstr[2]; if (abort) strcpy(abortstr, "F"); else strcpy(abortstr, "T"); return xs_bool(xs_single(h, t, XS_TRANSACTION_END, abortstr, NULL)); } /* Introduce a new domain. * This tells the store daemon about a shared memory page and event channel * associated with a domain: the domain uses these to communicate. */ bool xs_introduce_domain(struct xs_handle *h, unsigned int domid, unsigned long mfn, unsigned int eventchn) { char domid_str[MAX_STRLEN(domid)]; char mfn_str[MAX_STRLEN(mfn)]; char eventchn_str[MAX_STRLEN(eventchn)]; struct iovec iov[3]; snprintf(domid_str, sizeof(domid_str), "%u", domid); snprintf(mfn_str, sizeof(mfn_str), "%lu", mfn); snprintf(eventchn_str, sizeof(eventchn_str), "%u", eventchn); iov[0].iov_base = domid_str; iov[0].iov_len = strlen(domid_str) + 1; iov[1].iov_base = mfn_str; iov[1].iov_len = strlen(mfn_str) + 1; iov[2].iov_base = eventchn_str; iov[2].iov_len = strlen(eventchn_str) + 1; return xs_bool(xs_talkv(h, XBT_NULL, XS_INTRODUCE, iov, ARRAY_SIZE(iov), NULL)); } bool xs_set_target(struct xs_handle *h, unsigned int domid, unsigned int target) { char domid_str[MAX_STRLEN(domid)]; char target_str[MAX_STRLEN(target)]; struct iovec iov[2]; snprintf(domid_str, sizeof(domid_str), "%u", domid); snprintf(target_str, sizeof(target_str), "%u", target); iov[0].iov_base = domid_str; iov[0].iov_len = strlen(domid_str) + 1; iov[1].iov_base = target_str; iov[1].iov_len = strlen(target_str) + 1; return xs_bool(xs_talkv(h, XBT_NULL, XS_SET_TARGET, iov, ARRAY_SIZE(iov), NULL)); } static void * single_with_domid(struct xs_handle *h, enum xsd_sockmsg_type type, unsigned int domid) { char domid_str[MAX_STRLEN(domid)]; snprintf(domid_str, sizeof(domid_str), "%u", domid); return xs_single(h, XBT_NULL, type, domid_str, NULL); } bool xs_release_domain(struct xs_handle *h, unsigned int domid) { return xs_bool(single_with_domid(h, XS_RELEASE, domid)); } /* clear the shutdown bit for the given domain */ bool xs_resume_domain(struct xs_handle *h, unsigned int domid) { return xs_bool(single_with_domid(h, XS_RESUME, domid)); } char *xs_get_domain_path(struct xs_handle *h, unsigned int domid) { char domid_str[MAX_STRLEN(domid)]; snprintf(domid_str, sizeof(domid_str), "%u", domid); return xs_single(h, XBT_NULL, XS_GET_DOMAIN_PATH, domid_str, NULL); } bool xs_path_is_subpath(const char *parent, const char *child) { size_t childlen = strlen(child); size_t parentlen = strlen(parent); if (childlen < parentlen) return false; if (memcmp(child, parent, parentlen)) return false; if (childlen > parentlen && child[parentlen] != '/') return false; return true; } bool xs_is_domain_introduced(struct xs_handle *h, unsigned int domid) { char *domain = single_with_domid(h, XS_IS_DOMAIN_INTRODUCED, domid); int rc = strcmp("F", domain); free(domain); return rc; } int xs_suspend_evtchn_port(int domid) { char path[128]; char *portstr; int port; unsigned int plen; struct xs_handle *xs; xs = xs_daemon_open(); if (!xs) return -1; sprintf(path, "/local/domain/%d/device/suspend/event-channel", domid); portstr = xs_read(xs, XBT_NULL, path, &plen); xs_daemon_close(xs); if (!portstr || !plen) { port = -1; goto out; } port = atoi(portstr); out: free(portstr); return port; } char *xs_control_command(struct xs_handle *h, const char *cmd, void *data, unsigned int len) { struct iovec iov[2]; iov[0].iov_base = (void *)cmd; iov[0].iov_len = strlen(cmd) + 1; iov[1].iov_base = data; iov[1].iov_len = len; return xs_talkv(h, XBT_NULL, XS_CONTROL, iov, ARRAY_SIZE(iov), NULL); } char *xs_debug_command(struct xs_handle *h, const char *cmd, void *data, unsigned int len) { return xs_control_command(h, cmd, data, len); } static int read_message(struct xs_handle *h, int nonblocking) { /* IMPORTANT: It is forbidden to call this function without * acquiring the request lock and checking that h->read_thr_exists * is false. See "Lock discipline" in struct xs_handle, above. */ /* If nonblocking==1, this function will always read either * nothing, returning -1 and setting errno==EAGAIN, or we read * whole amount requested. Ie as soon as we have the start of * the message we block until we get all of it. */ struct xs_stored_msg *msg = NULL; char *body = NULL; int saved_errno = 0; int ret = -1; /* Allocate message structure and read the message header. */ msg = malloc(sizeof(*msg)); if (msg == NULL) goto error; cleanup_push_heap(msg); if (!read_all(h->fd, &msg->hdr, sizeof(msg->hdr), nonblocking)) { /* Cancellation point */ saved_errno = errno; goto error_freemsg; } /* Sanity check message body length. */ if (msg->hdr.len > XENSTORE_PAYLOAD_MAX) { saved_errno = E2BIG; goto error_freemsg; } /* Allocate and read the message body. */ body = msg->body = malloc(msg->hdr.len + 1); if (body == NULL) goto error_freemsg; cleanup_push_heap(body); if (!read_all(h->fd, body, msg->hdr.len, 0)) { /* Cancellation point */ saved_errno = errno; goto error_freebody; } body[msg->hdr.len] = '\0'; if (msg->hdr.type == XS_WATCH_EVENT) { mutex_lock(&h->watch_mutex); cleanup_push(pthread_mutex_unlock, &h->watch_mutex); /* Kick users out of their select() loop. */ if (list_empty(&h->watch_list) && (h->watch_pipe[1] != -1)) while (write(h->watch_pipe[1], body, 1) != 1) /* Cancellation point */ continue; list_add_tail(&msg->list, &h->watch_list); condvar_signal(&h->watch_condvar); cleanup_pop(1); } else { mutex_lock(&h->reply_mutex); /* There should only ever be one response pending! */ if (!list_empty(&h->reply_list)) { mutex_unlock(&h->reply_mutex); saved_errno = EEXIST; goto error_freebody; } list_add_tail(&msg->list, &h->reply_list); condvar_signal(&h->reply_condvar); mutex_unlock(&h->reply_mutex); } ret = 0; error_freebody: cleanup_pop_heap(ret == -1, body); error_freemsg: cleanup_pop_heap(ret == -1, msg); error: errno = saved_errno; return ret; } #ifdef USE_PTHREAD static void *read_thread(void *arg) { struct xs_handle *h = arg; int fd; while (read_message(h, 0) != -1) continue; /* An error return from read_message leaves the socket in an undefined * state; we might have read only the header and not the message after * it, or (more commonly) the other end has closed the connection. * Since further communication is unsafe, close the socket. */ fd = h->fd; h->fd = -1; close(fd); /* wake up all waiters */ pthread_mutex_lock(&h->reply_mutex); pthread_cond_broadcast(&h->reply_condvar); pthread_mutex_unlock(&h->reply_mutex); pthread_mutex_lock(&h->watch_mutex); pthread_cond_broadcast(&h->watch_condvar); pthread_mutex_unlock(&h->watch_mutex); return NULL; } #endif char *expanding_buffer_ensure(struct expanding_buffer *ebuf, int min_avail) { int want; char *got; if (ebuf->avail >= min_avail) return ebuf->buf; if (min_avail >= INT_MAX/3) return 0; want = ebuf->avail + min_avail + 10; got = realloc(ebuf->buf, want); if (!got) return 0; ebuf->buf = got; ebuf->avail = want; return ebuf->buf; } char *sanitise_value(struct expanding_buffer *ebuf, const char *val, unsigned len) { int used, remain, c; unsigned char *ip; #define ADD(c) (ebuf->buf[used++] = (c)) #define ADDF(f,c) (used += sprintf(ebuf->buf+used, (f), (c))) assert(len < INT_MAX/5); ip = (unsigned char *)val; used = 0; remain = len; if (!expanding_buffer_ensure(ebuf, remain + 1)) return NULL; while (remain-- > 0) { c= *ip++; if (c >= ' ' && c <= '~' && c != '\\') { ADD(c); continue; } if (!expanding_buffer_ensure(ebuf, used + remain + 5)) /* for "\\nnn\0" */ return 0; ADD('\\'); switch (c) { case '\t': ADD('t'); break; case '\n': ADD('n'); break; case '\r': ADD('r'); break; case '\\': ADD('\\'); break; default: if (c < 010) ADDF("%03o", c); else ADDF("x%02x", c); } } ADD(0); assert(used <= ebuf->avail); return ebuf->buf; #undef ADD #undef ADDF } void unsanitise_value(char *out, unsigned *out_len_r, const char *in) { const char *ip; char *op; unsigned c; int n; for (ip = in, op = out; (c = *ip++); *op++ = c) { if (c == '\\') { c = *ip++; #define GETF(f) do { \ n = 0; \ sscanf(ip, f "%n", &c, &n); \ ip += n; \ } while (0) switch (c) { case 't': c= '\t'; break; case 'n': c= '\n'; break; case 'r': c= '\r'; break; case '\\': c= '\\'; break; case 'x': GETF("%2x"); break; case '0': case '4': case '1': case '5': case '2': case '6': case '3': case '7': --ip; GETF("%3o"); break; case 0: --ip; break; default:; } #undef GETF } } *op = 0; if (out_len_r) *out_len_r = op - out; } /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/xenstore/xenstore.pc.in0000664000175000017500000000042413256712137016264 0ustar smbsmbprefix=@@prefix@@ includedir=@@incdir@@ libdir=@@libdir@@ Name: Xenstore Description: The Xenstore library for Xen hypervisor Version: @@version@@ Cflags: -I${includedir} @@cflagslocal@@ Libs: @@libsflag@@${libdir} -lxenstore Requires.private: xenevtchn,xencontrol,xengnttab xen-4.9.2/tools/xenstore/xenstored_transaction.c0000664000175000017500000003624213256712137020257 0ustar smbsmb/* Transaction code for Xen Store Daemon. Copyright (C) 2005 Rusty Russell IBM Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include "talloc.h" #include "list.h" #include "xenstored_transaction.h" #include "xenstored_watch.h" #include "xenstored_domain.h" #include "xenstore_lib.h" #include "utils.h" /* * Some notes regarding detection and handling of transaction conflicts: * * Basic source of reference is the 'generation' count. Each writing access * (either normal write or in a transaction) to the tdb data base will set * the node specific generation count to the global generation count. * For being able to identify a transaction the transaction specific generation * count is initialized with the global generation count when starting the * transaction. * Each time the global generation count is copied to either a node or a * transaction it is incremented. This ensures all nodes and/or transactions * are having a unique generation count. * * Transaction conflicts are detected by checking the generation count of all * nodes read in the transaction to match with the generation count in the * global data base at the end of the transaction. Nodes which have been * modified in the transaction don't have to be checked to match even if they * have been read, as the modified node will be globally visible after the * succeeded transaction possibly overwriting another modification which may * have occurred concurrent to the transaction. * * Examples: * --------- * The following notation is used: * I: initial state * G global generation count * g(X) generation count of node X * G(1) generation count of transaction 1 * g(1:Y) saved generation count of node Y in transaction 1 * TA1: operation in transaction 1 * X=1:X replace global node X with transaction 1 specific value of X * * 1. Simple transaction doing: read node A, write node B * I: g(A) = 1, g(B) = 2, G = 3 * Start transaction 1: G(1) = 3, G = 4 * TA1: read node A: g(1:A) = 1 * TA1: write node B: g(1:B) = 4, G = 5 * End TA1: g(1:A) == g(A) => okay, B = 1:B, g(B) = 5, G = 6 * * 2. Transaction with conflicting write * I: g(A) = 1, g(B) = 2, G = 3 * Start transaction 1: G(1) = 3, G = 4 * TA1: read node A: g(1:A) = 1 * write node A: g(A) = 4, G = 5 * TA1: write node B: g(1:B) = 5, G = 6 * End TA1: g(1:A) != g(A) => EAGAIN * * 3. Transaction with conflicting delete * I: g(A) = 1, g(B) = 2, G = 3 * Start transaction 1: G(1) = 3, G = 4 * TA1: read node A: g(1:A) = 1 * delete node A: g(A) = ~0 * TA1: write node B: g(1:B) = 4, G = 5 * End TA1: g(1:A) != g(A) => EAGAIN * * 4. Two interfering transactions * I: g(A) = 1, g(B) = 2, G = 3 * Start transaction 1: G(1) = 3, G = 4 * Start transaction 2: G(2) = 4, G = 5 * TA1: read node A: g(1:A) = 1 * TA2: read node B: g(2:B) = 2 * TA1: write node B: g(1:B) = 5, G = 6 * TA2: write node A: g(2:A) = 6, G = 7 * End TA1: g(1:A) == g(A) => okay, B = 1:B, g(B) = 7, G = 8 * End TA2: g(2:B) != g(B) => EAGAIN */ struct accessed_node { /* List of all changed nodes in the context of this transaction. */ struct list_head list; /* The name of the node. */ char *node; /* Generation count (or NO_GENERATION) for conflict checking. */ uint64_t generation; /* Generation count checking required? */ bool check_gen; /* Modified? */ bool modified; /* Transaction node in data base? */ bool ta_node; }; struct changed_domain { /* List of all changed domains in the context of this transaction. */ struct list_head list; /* Identifier of the changed domain. */ unsigned int domid; /* Amount by which this domain's nbentry field has changed. */ int nbentry; }; struct transaction { /* List of all transactions active on this connection. */ struct list_head list; /* Connection-local identifier for this transaction. */ uint32_t id; /* Generation when transaction started. */ uint64_t generation; /* List of accessed nodes. */ struct list_head accessed; /* List of changed domains - to record the changed domain entry number */ struct list_head changed_domains; /* Flag for letting transaction fail. */ bool fail; }; extern int quota_max_transaction; static uint64_t generation; static void set_tdb_key(const char *name, TDB_DATA *key) { key->dptr = (char *)name; key->dsize = strlen(name); } static struct accessed_node *find_accessed_node(struct transaction *trans, const char *name) { struct accessed_node *i; list_for_each_entry(i, &trans->accessed, list) if (streq(i->node, name)) return i; return NULL; } static char *transaction_get_node_name(void *ctx, struct transaction *trans, const char *name) { return talloc_asprintf(ctx, "%"PRIu64"/%s", trans->generation, name); } /* * Prepend the transaction to name if node has been modified in the current * transaction. */ int transaction_prepend(struct connection *conn, const char *name, TDB_DATA *key) { char *tdb_name; if (!conn || !conn->transaction || !find_accessed_node(conn->transaction, name)) { set_tdb_key(name, key); return 0; } tdb_name = transaction_get_node_name(conn->transaction, conn->transaction, name); if (!tdb_name) return errno; set_tdb_key(tdb_name, key); return 0; } /* * A node has been accessed. * * Modifying accesses (write, delete) always update the generation (global and * node->generation). * * Accesses in a transaction will be added to the list of accessed nodes * if not already done. Read type accesses will copy the node to the * transaction specific data base part, write type accesses go there * anyway. * * If not NULL, key will be supplied with name and length of name of the node * to be accessed in the data base. */ int access_node(struct connection *conn, struct node *node, enum node_access_type type, TDB_DATA *key) { struct accessed_node *i = NULL; struct transaction *trans; TDB_DATA local_key; const char *trans_name = NULL; int ret; bool introduce = false; if (type != NODE_ACCESS_READ) { node->generation = generation++; if (conn && !conn->transaction) wrl_apply_debit_direct(conn); } if (!conn || !conn->transaction) { /* They're changing the global database. */ if (key) set_tdb_key(node->name, key); return 0; } trans = conn->transaction; trans_name = transaction_get_node_name(node, trans, node->name); if (!trans_name) goto nomem; i = find_accessed_node(trans, node->name); if (!i) { i = talloc_zero(trans, struct accessed_node); if (!i) goto nomem; i->node = talloc_strdup(i, node->name); if (!i->node) goto nomem; introduce = true; i->ta_node = false; /* * Additional transaction-specific node for read type. We only * have to verify read nodes if we didn't write them. * * The node is created and written to DB here to distinguish * from the write types. */ if (type == NODE_ACCESS_READ) { i->generation = node->generation; i->check_gen = true; if (node->generation != NO_GENERATION) { set_tdb_key(trans_name, &local_key); ret = write_node_raw(conn, &local_key, node); if (ret) goto err; i->ta_node = true; } } list_add_tail(&i->list, &trans->accessed); } if (type != NODE_ACCESS_READ) i->modified = true; if (introduce && type == NODE_ACCESS_DELETE) /* Nothing to delete. */ return -1; if (key) { set_tdb_key(trans_name, key); if (type == NODE_ACCESS_WRITE) i->ta_node = true; if (type == NODE_ACCESS_DELETE) i->ta_node = false; } return 0; nomem: ret = ENOMEM; err: talloc_free((void *)trans_name); talloc_free(i); trans->fail = true; errno = ret; return ret; } /* * Finalize transaction: * Walk through accessed nodes and check generation against global data. * If all entries match, read the transaction entries and write them without * transaction prepended. Delete all transaction specific nodes in the data * base. */ static int finalize_transaction(struct connection *conn, struct transaction *trans) { struct accessed_node *i; TDB_DATA key, ta_key, data; struct xs_tdb_record_hdr *hdr; uint64_t gen; char *trans_name; int ret; list_for_each_entry(i, &trans->accessed, list) { if (!i->check_gen) continue; set_tdb_key(i->node, &key); data = tdb_fetch(tdb_ctx, key); hdr = (void *)data.dptr; if (!data.dptr) { if (tdb_error(tdb_ctx) != TDB_ERR_NOEXIST) return EIO; gen = NO_GENERATION; } else gen = hdr->generation; talloc_free(data.dptr); if (i->generation != gen) return EAGAIN; } while ((i = list_top(&trans->accessed, struct accessed_node, list))) { trans_name = transaction_get_node_name(i, trans, i->node); if (!trans_name) /* We are doomed: the transaction is only partial. */ goto err; set_tdb_key(trans_name, &ta_key); if (i->modified) { set_tdb_key(i->node, &key); if (i->ta_node) { data = tdb_fetch(tdb_ctx, ta_key); if (!data.dptr) goto err; hdr = (void *)data.dptr; hdr->generation = generation++; ret = tdb_store(tdb_ctx, key, data, TDB_REPLACE); talloc_free(data.dptr); if (ret) goto err; } else if (tdb_delete(tdb_ctx, key)) goto err; fire_watches(conn, trans, i->node, false); } if (i->ta_node && tdb_delete(tdb_ctx, ta_key)) goto err; list_del(&i->list); talloc_free(i); } return 0; err: corrupt(conn, "Partial transaction"); return EIO; } static int destroy_transaction(void *_transaction) { struct transaction *trans = _transaction; struct accessed_node *i; char *trans_name; TDB_DATA key; wrl_ntransactions--; trace_destroy(trans, "transaction"); while ((i = list_top(&trans->accessed, struct accessed_node, list))) { if (i->ta_node) { trans_name = transaction_get_node_name(i, trans, i->node); if (trans_name) { set_tdb_key(trans_name, &key); tdb_delete(tdb_ctx, key); } } list_del(&i->list); talloc_free(i); } return 0; } struct transaction *transaction_lookup(struct connection *conn, uint32_t id) { struct transaction *trans; if (id == 0) return NULL; list_for_each_entry(trans, &conn->transaction_list, list) if (trans->id == id) return trans; return ERR_PTR(-ENOENT); } int do_transaction_start(struct connection *conn, struct buffered_data *in) { struct transaction *trans, *exists; char id_str[20]; /* We don't support nested transactions. */ if (conn->transaction) return EBUSY; if (conn->id && conn->transaction_started > quota_max_transaction) return ENOSPC; /* Attach transaction to input for autofree until it's complete */ trans = talloc_zero(in, struct transaction); if (!trans) return ENOMEM; INIT_LIST_HEAD(&trans->accessed); INIT_LIST_HEAD(&trans->changed_domains); trans->fail = false; trans->generation = generation++; /* Pick an unused transaction identifier. */ do { trans->id = conn->next_transaction_id; exists = transaction_lookup(conn, conn->next_transaction_id++); } while (!IS_ERR(exists)); /* Now we own it. */ list_add_tail(&trans->list, &conn->transaction_list); talloc_steal(conn, trans); talloc_set_destructor(trans, destroy_transaction); conn->transaction_started++; wrl_ntransactions++; snprintf(id_str, sizeof(id_str), "%u", trans->id); send_reply(conn, XS_TRANSACTION_START, id_str, strlen(id_str)+1); return 0; } static int transaction_fix_domains(struct transaction *trans, bool update) { struct changed_domain *d; int cnt; list_for_each_entry(d, &trans->changed_domains, list) { cnt = domain_entry_fix(d->domid, d->nbentry, update); if (!update && cnt >= quota_nb_entry_per_domain) return ENOSPC; } return 0; } int do_transaction_end(struct connection *conn, struct buffered_data *in) { const char *arg = onearg(in); struct transaction *trans; int ret; if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) return EINVAL; if ((trans = conn->transaction) == NULL) return ENOENT; conn->transaction = NULL; list_del(&trans->list); conn->transaction_started--; /* Attach transaction to in for auto-cleanup */ talloc_steal(in, trans); if (streq(arg, "T")) { if (trans->fail) return ENOMEM; ret = transaction_fix_domains(trans, false); if (ret) return ret; if (finalize_transaction(conn, trans)) return EAGAIN; wrl_apply_debit_trans_commit(conn); /* fix domain entry for each changed domain */ transaction_fix_domains(trans, true); } send_ack(conn, XS_TRANSACTION_END); return 0; } void transaction_entry_inc(struct transaction *trans, unsigned int domid) { struct changed_domain *d; list_for_each_entry(d, &trans->changed_domains, list) if (d->domid == domid) { d->nbentry++; return; } d = talloc(trans, struct changed_domain); if (!d) { /* Let the transaction fail. */ trans->fail = true; return; } d->domid = domid; d->nbentry = 1; list_add_tail(&d->list, &trans->changed_domains); } void transaction_entry_dec(struct transaction *trans, unsigned int domid) { struct changed_domain *d; list_for_each_entry(d, &trans->changed_domains, list) if (d->domid == domid) { d->nbentry--; return; } d = talloc(trans, struct changed_domain); if (!d) { /* Let the transaction fail. */ trans->fail = true; return; } d->domid = domid; d->nbentry = -1; list_add_tail(&d->list, &trans->changed_domains); } void conn_delete_all_transactions(struct connection *conn) { struct transaction *trans; while ((trans = list_top(&conn->transaction_list, struct transaction, list))) { list_del(&trans->list); talloc_free(trans); } assert(conn->transaction == NULL); conn->transaction_started = 0; } int check_transactions(struct hashtable *hash) { struct connection *conn; struct transaction *trans; struct accessed_node *i; char *tname, *tnode; list_for_each_entry(conn, &connections, list) { list_for_each_entry(trans, &conn->transaction_list, list) { tname = talloc_asprintf(trans, "%"PRIu64, trans->generation); if (!tname || !remember_string(hash, tname)) goto nomem; list_for_each_entry(i, &trans->accessed, list) { if (!i->ta_node) continue; tnode = transaction_get_node_name(tname, trans, i->node); if (!tnode || !remember_string(hash, tnode)) goto nomem; talloc_free(tnode); } talloc_free(tname); } } return 0; nomem: talloc_free(tname); return ENOMEM; } /* * Local variables: * c-file-style: "linux" * indent-tabs-mode: t * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ xen-4.9.2/tools/xentrace/0000775000175000017500000000000013256712137013426 5ustar smbsmbxen-4.9.2/tools/xentrace/setsize.c0000664000175000017500000000170713256712137015265 0ustar smbsmb#include #include #include #include int main(int argc, char * argv[]) { unsigned long size; xc_interface *xc_handle = xc_interface_open(0,0,0); if ( xc_tbuf_get_size(xc_handle, &size) != 0 ) { perror("Failure to get tbuf info from Xen. Guess size is 0"); printf("This may mean that tracing is not enabled in xen.\n"); } else { printf("Current tbuf size: 0x%lx\n", size); } if (argc < 2) exit(0); size = atol(argv[1]); if ( xc_tbuf_set_size(xc_handle, size) != 0 ) { perror("set_size Hypercall failure"); exit(1); } printf("set_size succeeded.\n"); if (xc_tbuf_get_size(xc_handle, &size) != 0) perror("Failure to get tbuf info from Xen." " Tracing must be enabled first"); else printf("New tbuf size: 0x%lx\n", size); xc_interface_close(xc_handle); return 0; } xen-4.9.2/tools/xentrace/pv.h0000664000175000017500000000160013256712137014221 0ustar smbsmb/* * PV event decoding. * * Copyright (C) 2012 Citrix Systems R&D Ltd. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. */ #ifndef __PV_H #include "analyze.h" #include #ifdef __cplusplus extern "C" { #endif #define ARG_MISSING 0x0 #define ARG_32BIT 0x1 #define ARG_64BIT 0x2 #define MMU_UPDATE_PREEMPTED (~(~0U>>1)) static inline uint32_t pv_hypercall_op(const struct record_info *ri) { return ri->d[0] & ~TRC_PV_HYPERCALL_V2_ARG_MASK; } static inline int pv_hypercall_arg_present(const struct record_info *ri, int arg) { return (ri->d[0] >> (20 + 2*arg)) & 0x3; } void pv_hypercall_gather_args(const struct record_info *ri, uint64_t *args); #ifdef __cplusplus } /* extern "C" */ #endif #endif xen-4.9.2/tools/xentrace/analyze.h0000664000175000017500000000455313256712137015251 0ustar smbsmb#ifndef __ANALYZE_H # define __ANALYZE_H #include #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #define TRC_GEN_MAIN 0 #define TRC_SCHED_MAIN 1 #define TRC_DOM0OP_MAIN 2 #define TRC_HVM_MAIN 3 #define TRC_MEM_MAIN 4 #define TRC_PV_MAIN 5 #define TRC_SHADOW_MAIN 6 #define TRC_HW_MAIN 7 #define TRC_LOST_RECORDS_END (TRC_GEN + 50) #define NR_CPUS 128 #if __x86_64__ # define BITS_PER_LONG 64 #else # define BITS_PER_LONG 32 #endif #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)] typedef struct cpumask{ DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t; enum { TRCE_SFLAG_SET_AD, TRCE_SFLAG_SET_A, TRCE_SFLAG_SHADOW_L1_GET_REF, TRCE_SFLAG_SHADOW_L1_PUT_REF, TRCE_SFLAG_L2_PROPAGATE, TRCE_SFLAG_SET_CHANGED, TRCE_SFLAG_SET_FLUSH, TRCE_SFLAG_SET_ERROR, TRCE_SFLAG_DEMOTE, TRCE_SFLAG_PROMOTE, TRCE_SFLAG_WRMAP, TRCE_SFLAG_WRMAP_GUESS_FOUND, TRCE_SFLAG_WRMAP_BRUTE_FORCE, TRCE_SFLAG_EARLY_UNSHADOW, TRCE_SFLAG_EMULATION_2ND_PT_WRITTEN, TRCE_SFLAG_EMULATION_LAST_FAILED, TRCE_SFLAG_EMULATE_FULL_PT, TRCE_SFLAG_PREALLOC_UNPIN, TRCE_SFLAG_PREALLOC_UNHOOK }; #define TRC_HVM_OP_DESTROY_PROC (TRC_HVM_HANDLER + 0x100) typedef unsigned long long tsc_t; /* -- on-disk trace buffer definitions -- */ struct trace_record { union { struct { unsigned event:28, extra_words:3, cycle_flag:1; union { struct { uint32_t tsc_lo, tsc_hi; uint32_t data[7]; } tsc; struct { uint32_t data[7]; } notsc; } u; }; uint32_t raw[8]; }; }; /* -- General info about a current record -- */ struct time_struct { unsigned long long time; unsigned int s, ns; }; #define DUMP_HEADER_MAX 256 struct record_info { int cpu; tsc_t tsc; union { unsigned event; struct { unsigned minor:12, sub:4, main:12, unused:4; } evt; }; int extra_words; int size; uint32_t *d; char dump_header[DUMP_HEADER_MAX]; struct time_struct t; struct trace_record rec; }; #endif xen-4.9.2/tools/xentrace/xentrace.c0000664000175000017500000010255613256712137015414 0ustar smbsmb/****************************************************************************** * tools/xentrace/xentrace.c * * Tool for collecting trace buffer data from Xen. * * Copyright (C) 2004 by Intel Research Cambridge * * Author: Mark Williamson, mark.a.williamson@intel.com * Date: February 2004 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define XC_WANT_COMPAT_MAP_FOREIGN_API #include #include #define PERROR(_m, _a...) \ do { \ int __saved_errno = errno; \ fprintf(stderr, "ERROR: " _m " (%d = %s)\n" , ## _a , \ __saved_errno, strerror(__saved_errno)); \ errno = __saved_errno; \ } while (0) /***** Compile time configuration of defaults ********************************/ /* sleep for this long (milliseconds) between checking the trace buffers */ #define POLL_SLEEP_MILLIS 100 #define DEFAULT_TBUF_SIZE 32 /***** The code **************************************************************/ typedef struct settings_st { char *outfile; unsigned long poll_sleep; /* milliseconds to sleep between polls */ uint32_t evt_mask; char *cpu_mask_str; unsigned long tbuf_size; unsigned long disk_rsvd; unsigned long timeout; unsigned long memory_buffer; uint8_t discard:1, disable_tracing:1, start_disabled:1; } settings_t; struct t_struct { const struct t_info *t_info; /* Structure with information about individual buffers */ struct t_buf **meta; /* Pointers to trace buffer metadata */ unsigned char **data; /* Pointers to trace buffer data areas */ }; settings_t opts; int interrupted = 0; /* gets set if we get a SIGHUP */ static xc_interface *xc_handle; static xenevtchn_handle *xce_handle = NULL; static int virq_port = -1; static int outfd = 1; static void close_handler(int signal) { interrupted = 1; } static struct { char * buf; unsigned long prod, cons, size; unsigned long pending_size, pending_prod; } membuf = { 0 }; #define MEMBUF_INDEX_RESET_THRESHOLD (1<<29) /* FIXME -- make a power of 2 so we can mask instead. */ #define MEMBUF_POINTER(_i) (membuf.buf + ((_i) % membuf.size)) #define MEMBUF_CONS_INCREMENT(_n) \ do { \ membuf.cons += (_n); \ } while(0) #define MEMBUF_PROD_SET(_x) \ do { \ if ( (_x) < membuf.prod ) { \ fprintf(stderr, "%s: INTERNAL_ERROR: prod %lu, trying to set to %lu!\n", \ __func__, membuf.prod, (unsigned long)(_x)); \ exit(1); \ } \ membuf.prod = (_x); \ if ( (_x) > MEMBUF_INDEX_RESET_THRESHOLD ) \ { \ membuf.prod %= membuf.size; \ membuf.cons %= membuf.size; \ if( membuf.prod < membuf.cons ) \ membuf.prod += membuf.size; \ } \ } while(0) struct cpu_change_record { uint32_t header; struct { int cpu; unsigned window_size; } data; }; #define CPU_CHANGE_HEADER \ (TRC_TRACE_CPU_CHANGE \ | (((sizeof(struct cpu_change_record)/sizeof(uint32_t)) - 1) \ << TRACE_EXTRA_SHIFT) ) void membuf_alloc(unsigned long size) { membuf.buf = malloc(size); if(!membuf.buf) { fprintf(stderr, "%s: Couldn't malloc %lu bytes!\n", __func__, size); exit(1); } membuf.prod = membuf.cons = 0; membuf.size = size; } /* * Reserve a new window in the buffer. Move the 'consumer' forward size * bytes, re-adjusting the cpu window sizes as necessary, and insert a * cpu_change record. */ void membuf_reserve_window(unsigned cpu, unsigned long window_size) { struct cpu_change_record *rec; long need_to_consume, free, freed; int last_cpu = -1; if ( membuf.pending_size > 0 ) { fprintf(stderr, "%s: INTERNAL_ERROR: pending_size %lu\n", __func__, membuf.pending_size); exit(1); } need_to_consume = window_size + sizeof(*rec); if ( window_size > membuf.size ) { fprintf(stderr, "%s: reserve size %lu larger than buffer size %lu!\n", __func__, window_size, membuf.size); exit(1); } /* Subtract free space already in buffer. */ free = membuf.size - (membuf.prod - membuf.cons); if( need_to_consume < free) goto start_window; need_to_consume -= free; /* * "Free" up full windows until we have enough for this window. * It's a bit wasteful to throw away partial buffers, but the only * other option is to scan throught he buffer headers. Since the * common case is that it's going to be thrown away next anyway, I * think minimizing the overall impact is more important. */ do { rec = (struct cpu_change_record *)MEMBUF_POINTER(membuf.cons); if( rec->header != CPU_CHANGE_HEADER ) { fprintf(stderr, "%s: INTERNAL ERROR: no cpu_change record at consumer!\n", __func__); exit(EXIT_FAILURE); } freed = sizeof(*rec) + rec->data.window_size; if ( need_to_consume > 0 ) { last_cpu = rec->data.cpu; MEMBUF_CONS_INCREMENT(freed); need_to_consume -= freed; } } while( need_to_consume > 0 ); /* For good tsc consistency, we need to start at a low-cpu buffer. Keep * skipping until the cpu goes down or stays the same. */ rec = (struct cpu_change_record *)MEMBUF_POINTER(membuf.cons); while ( rec->data.cpu > last_cpu ) { last_cpu = rec->data.cpu; freed = sizeof(*rec) + rec->data.window_size; MEMBUF_CONS_INCREMENT(freed); rec = (struct cpu_change_record *)MEMBUF_POINTER(membuf.cons); } start_window: /* * Start writing "pending" data. Update prod once all this data is * written. */ membuf.pending_prod = membuf.prod; membuf.pending_size = window_size; rec = (struct cpu_change_record *)MEMBUF_POINTER(membuf.pending_prod); rec->header = CPU_CHANGE_HEADER; rec->data.cpu = cpu; rec->data.window_size = window_size; membuf.pending_prod += sizeof(*rec); } void membuf_write(void *start, unsigned long size) { char * p; unsigned long wsize; if( (membuf.size - (membuf.prod - membuf.cons)) < size ) { fprintf(stderr, "%s: INTERNAL ERROR: need %lu bytes, only have %lu!\n", __func__, size, membuf.prod - membuf.cons); exit(1); } if( size > membuf.pending_size ) { fprintf(stderr, "%s: INTERNAL ERROR: size %lu, pending %lu!\n", __func__, size, membuf.pending_size); exit(1); } wsize = size; p = MEMBUF_POINTER(membuf.pending_prod); /* If the buffer overlaps the "wrap", do an extra write */ if ( p + size > membuf.buf + membuf.size ) { int usize = ( membuf.buf + membuf.size ) - p; memcpy(p, start, usize); start += usize; wsize -= usize; p = membuf.buf; } memcpy(p, start, wsize); membuf.pending_prod += size; membuf.pending_size -= size; if ( membuf.pending_size == 0 ) { MEMBUF_PROD_SET(membuf.pending_prod); } } void membuf_dump(void) { /* Dump circular memory buffer */ int cons, prod, wsize, written; char * wstart; fprintf(stderr, "Dumping memory buffer.\n"); cons = membuf.cons % membuf.size; prod = membuf.prod % membuf.size; if(prod > cons) { /* Write in one go */ wstart = membuf.buf + cons; wsize = prod - cons; written = write(outfd, wstart, wsize); if ( written != wsize ) goto fail; } else { /* Write in two pieces: cons->end, beginning->prod. */ wstart = membuf.buf + cons; wsize = membuf.size - cons; written = write(outfd, wstart, wsize); if ( written != wsize ) { fprintf(stderr, "Write failed! (size %d, returned %d)\n", wsize, written); goto fail; } wstart = membuf.buf; wsize = prod; written = write(outfd, wstart, wsize); if ( written != wsize ) { fprintf(stderr, "Write failed! (size %d, returned %d)\n", wsize, written); goto fail; } } membuf.cons = membuf.prod = 0; return; fail: exit(1); return; } /** * write_buffer - write a section of the trace buffer * @cpu - source buffer CPU ID * @start * @size - size of write (may be less than total window size) * @total_size - total size of the window (0 on 2nd write of wrapped windows) * @out - output stream * * Outputs the trace buffer to a filestream, prepending the CPU and size * of the buffer write. */ static void write_buffer(unsigned int cpu, unsigned char *start, int size, int total_size) { struct statvfs stat; size_t written = 0; if ( opts.memory_buffer == 0 && opts.disk_rsvd != 0 ) { unsigned long long freespace; /* Check that filesystem has enough space. */ if ( fstatvfs (outfd, &stat) ) { fprintf(stderr, "Statfs failed!\n"); goto fail; } freespace = stat.f_frsize * (unsigned long long)stat.f_bfree; if ( total_size ) freespace -= total_size; else freespace -= size; freespace >>= 20; /* Convert to MB */ if ( freespace <= opts.disk_rsvd ) { fprintf(stderr, "Disk space limit reached (free space: %lluMB, limit: %luMB).\n", freespace, opts.disk_rsvd); exit (EXIT_FAILURE); } } /* Write a CPU_BUF record on each buffer "window" written. Wrapped * windows may involve two writes, so only write the record on the * first write. */ if ( total_size != 0 ) { if ( opts.memory_buffer ) { membuf_reserve_window(cpu, total_size); } else { struct cpu_change_record rec; rec.header = CPU_CHANGE_HEADER; rec.data.cpu = cpu; rec.data.window_size = total_size; written = write(outfd, &rec, sizeof(rec)); if ( written != sizeof(rec) ) { fprintf(stderr, "Cannot write cpu change (write returned %zd)\n", written); goto fail; } } } if ( opts.memory_buffer ) { membuf_write(start, size); } else { written = write(outfd, start, size); if ( written != size ) { fprintf(stderr, "Write failed! (size %d, returned %zd)\n", size, written); goto fail; } } return; fail: PERROR("Failed to write trace data"); exit(EXIT_FAILURE); } static void disable_tbufs(void) { xc_interface *xc_handle = xc_interface_open(0,0,0); if ( !xc_handle ) { perror("Couldn't open xc handle to disable tbufs."); return; } if ( xc_tbuf_disable(xc_handle) != 0 ) { perror("Couldn't disable trace buffers"); } xc_interface_close(xc_handle); } static void get_tbufs(unsigned long *mfn, unsigned long *size) { int ret; if(!opts.tbuf_size) opts.tbuf_size = DEFAULT_TBUF_SIZE; ret = xc_tbuf_enable(xc_handle, opts.tbuf_size, mfn, size); if ( ret != 0 ) { perror("Couldn't enable trace buffers"); exit(1); } } /** * map_tbufs - memory map Xen trace buffers into user space * @tbufs_mfn: mfn of the trace buffers * @num: number of trace buffers to map * @size: size of each trace buffer * * Maps the Xen trace buffers them into process address space. */ static struct t_struct *map_tbufs(unsigned long tbufs_mfn, unsigned int num, unsigned long tinfo_size) { static struct t_struct tbufs = { 0 }; int i; /* Map t_info metadata structure */ tbufs.t_info = xc_map_foreign_range(xc_handle, DOMID_XEN, tinfo_size, PROT_READ, tbufs_mfn); if ( tbufs.t_info == 0 ) { PERROR("Failed to mmap trace buffers"); exit(EXIT_FAILURE); } if ( tbufs.t_info->tbuf_size == 0 ) { fprintf(stderr, "%s: tbuf_size 0!\n", __func__); exit(EXIT_FAILURE); } /* Map per-cpu buffers */ tbufs.meta = (struct t_buf **)calloc(num, sizeof(struct t_buf *)); tbufs.data = (unsigned char **)calloc(num, sizeof(unsigned char *)); if ( tbufs.meta == NULL || tbufs.data == NULL ) { PERROR( "Failed to allocate memory for buffer pointers\n"); exit(EXIT_FAILURE); } for(i=0; imfn_offset[i]; int j; xen_pfn_t pfn_list[tbufs.t_info->tbuf_size]; for ( j=0; jtbuf_size; j++) pfn_list[j] = (xen_pfn_t)mfn_list[j]; tbufs.meta[i] = xc_map_foreign_pages(xc_handle, DOMID_XEN, PROT_READ | PROT_WRITE, pfn_list, tbufs.t_info->tbuf_size); if ( tbufs.meta[i] == NULL ) { PERROR("Failed to map cpu buffer!"); exit(EXIT_FAILURE); } tbufs.data[i] = (unsigned char *)(tbufs.meta[i]+1); } return &tbufs; } void print_cpu_mask(xc_cpumap_t map) { unsigned int v, had_printed = 0; int i; fprintf(stderr, "change cpumask to 0x"); for ( i = xc_get_cpumap_size(xc_handle); i >= 0; i-- ) { v = map[i]; if ( v || had_printed || !i ) { if (had_printed) fprintf(stderr,"%02x", v); else fprintf(stderr,"%x", v); had_printed = 1; } } fprintf(stderr, "\n"); } static int set_cpu_mask(xc_cpumap_t map) { int ret = xc_tbuf_set_cpu_mask(xc_handle, map); if ( ret == 0 ) { print_cpu_mask(map); return 0; } PERROR("Failure to get trace buffer pointer from Xen and set the new mask"); return EXIT_FAILURE; } /** * set_mask - set the event mask in HV * @mask: the new mask * @type: the new mask type,0-event mask, 1-cpu mask * */ static void set_evt_mask(uint32_t mask) { int ret = 0; ret = xc_tbuf_set_evt_mask(xc_handle, mask); fprintf(stderr, "change evtmask to 0x%x\n", mask); if ( ret != 0 ) { PERROR("Failure to get trace buffer pointer from Xen and set the new mask"); exit(EXIT_FAILURE); } } /** * get_num_cpus - get the number of logical CPUs */ static unsigned int get_num_cpus(void) { xc_physinfo_t physinfo = { 0 }; int ret; ret = xc_physinfo(xc_handle, &physinfo); if ( ret != 0 ) { PERROR("Failure to get logical CPU count from Xen"); exit(EXIT_FAILURE); } return physinfo.nr_cpus; } /** * event_init - setup to receive the VIRQ_TBUF event */ static void event_init(void) { int rc; xce_handle = xenevtchn_open(NULL, 0); if (xce_handle == NULL) { perror("event channel open"); exit(EXIT_FAILURE); } rc = xenevtchn_bind_virq(xce_handle, VIRQ_TBUF); if (rc == -1) { PERROR("failed to bind to VIRQ port"); exit(EXIT_FAILURE); } virq_port = rc; } /** * wait_for_event_or_timeout - sleep for the specified number of milliseconds, * or until an VIRQ_TBUF event occurs */ static void wait_for_event_or_timeout(unsigned long milliseconds) { int rc; struct pollfd fd = { .fd = xenevtchn_fd(xce_handle), .events = POLLIN | POLLERR }; int port; rc = poll(&fd, 1, milliseconds); if (rc == -1) { if (errno == EINTR) return; PERROR("poll exitted with an error"); exit(EXIT_FAILURE); } if (rc == 1) { port = xenevtchn_pending(xce_handle); if (port == -1) { PERROR("failed to read port from evtchn"); exit(EXIT_FAILURE); } if (port != virq_port) { fprintf(stderr, "unexpected port returned from evtchn (got %d vs expected %d)\n", port, virq_port); exit(EXIT_FAILURE); } rc = xenevtchn_unmask(xce_handle, port); if (rc == -1) { PERROR("failed to write port to evtchn"); exit(EXIT_FAILURE); } } } /** * monitor_tbufs - monitor the contents of tbufs and output to a file * @logfile: the FILE * representing the file to log to */ static int monitor_tbufs(void) { int i; struct t_struct *tbufs; /* Pointer to hypervisor maps */ struct t_buf **meta; /* pointers to the trace buffer metadata */ unsigned char **data; /* pointers to the trace buffer data areas * where they are mapped into user space. */ unsigned long tbufs_mfn; /* mfn of the tbufs */ unsigned int num; /* number of trace buffers / logical CPUS */ unsigned long tinfo_size; /* size of t_info metadata map */ unsigned long size; /* size of a single trace buffer */ unsigned long data_size; int last_read = 1; /* prepare to listen for VIRQ_TBUF */ event_init(); /* get number of logical CPUs (and therefore number of trace buffers) */ num = get_num_cpus(); /* setup access to trace buffers */ get_tbufs(&tbufs_mfn, &tinfo_size); if ( opts.start_disabled ) disable_tbufs(); tbufs = map_tbufs(tbufs_mfn, num, tinfo_size); size = tbufs->t_info->tbuf_size * XC_PAGE_SIZE; data_size = size - sizeof(struct t_buf); meta = tbufs->meta; data = tbufs->data; if ( opts.discard ) for ( i = 0; i < num; i++ ) meta[i]->cons = meta[i]->prod; /* now, scan buffers for events */ while ( 1 ) { for ( i = 0; i < num; i++ ) { unsigned long start_offset, end_offset, window_size, cons, prod; /* Read window information only once. */ cons = meta[i]->cons; prod = meta[i]->prod; xen_rmb(); /* read prod, then read item. */ if ( cons == prod ) continue; assert(cons < 2*data_size); assert(prod < 2*data_size); // NB: if (prod 0); assert(window_size <= data_size); start_offset = cons % data_size; end_offset = prod % data_size; if ( end_offset > start_offset ) { /* If window does not wrap, write in one big chunk */ write_buffer(i, data[i]+start_offset, window_size, window_size); } else { /* If wrapped, write in two chunks: * - first, start to the end of the buffer * - second, start of buffer to end of window */ write_buffer(i, data[i] + start_offset, data_size - start_offset, window_size); write_buffer(i, data[i], end_offset, 0); } xen_mb(); /* read buffer, then update cons. */ meta[i]->cons = prod; } if ( interrupted ) { if ( last_read ) { /* Disable tracing, then read through all the buffers one last time */ if ( opts.disable_tracing ) disable_tbufs(); last_read = 0; continue; } else break; } wait_for_event_or_timeout(opts.poll_sleep); } if ( opts.memory_buffer ) membuf_dump(); /* cleanup */ free(meta); free(data); /* don't need to munmap - cleanup is automatic */ close(outfd); return 0; } /****************************************************************************** * Command line handling *****************************************************************************/ #define xstr(x) str(x) #define str(x) #x const char *program_version = "xentrace v1.2"; const char *program_bug_address = ""; static void usage(void) { #define USAGE_STR \ "Usage: xentrace [OPTION...] [output file]\n" \ "Tool to capture Xen trace buffer data\n" \ "\n" \ " -c, --cpu-mask=c Set cpu-mask, using either hex, CPU ranges, or\n" \ " for all CPUs\n" \ " -e, --evt-mask=e Set evt-mask\n" \ " -s, --poll-sleep=p Set sleep time, p, in milliseconds between\n" \ " polling the trace buffer for new data\n" \ " (default " xstr(POLL_SLEEP_MILLIS) ").\n" \ " -S, --trace-buf-size=N Set trace buffer size in pages (default " \ xstr(DEFAULT_TBUF_SIZE) ").\n" \ " N.B. that the trace buffer cannot be resized.\n" \ " if it has already been set this boot cycle,\n" \ " this argument will be ignored.\n" \ " -D --discard-buffers Discard all records currently in the trace\n" \ " buffers before beginning.\n" \ " -x --dont-disable-tracing\n" \ " By default, xentrace will disable tracing when\n" \ " it exits. Selecting this option will tell it to\n" \ " keep tracing on. Traces will be collected in\n" \ " Xen's trace buffers until they become full.\n" \ " -X --start-disabled Setup trace buffers and listen, but don't enable\n" \ " tracing. (Useful if tracing will be enabled by\n" \ " else.)\n" \ " -T --time-interval=s Run xentrace for s seconds and quit.\n" \ " -?, --help Show this message\n" \ " -V, --version Print program version\n" \ " -M, --memory-buffer=b Copy trace records to a circular memory buffer.\n" \ " Dump to file on exit.\n" \ " -r --reserve-disk-space=n Before writing trace records to disk, check to see\n" \ " that after the write there will be at least n space\n" \ " left on the disk.\n" \ "\n" \ "This tool is used to capture trace buffer data from Xen. The\n" \ "data is output in a binary format, in the following order:\n" \ "\n" \ " CPU(uint) TSC(uint64_t) EVENT(uint32_t) D1 D2 D3 D4 D5 (all uint32_t)\n" \ "\n" \ "The output should be parsed using the tool xentrace_format,\n" \ "which can produce human-readable output in ASCII format.\n" printf(USAGE_STR); printf("\nReport bugs to %s\n", program_bug_address); exit(EXIT_FAILURE); } /* convert the argument string pointed to by arg to a long int representation, * including suffixes such as 'M' and 'k'. */ #define MB (1024*1024) #define KB (1024) long sargtol(const char *restrict arg, int base) { char *endp; long val; errno = 0; val = strtol(arg, &endp, base); if ( errno != 0 ) { fprintf(stderr, "Invalid option argument: %s\n", arg); fprintf(stderr, "Error: %s\n\n", strerror(errno)); usage(); } else if (endp == arg) { goto invalid; } switch(*endp) { case '\0': break; case 'M': val *= MB; break; case 'K': case 'k': val *= KB; break; default: fprintf(stderr, "Unknown suffix %c\n", *endp); exit(1); } return val; invalid: fprintf(stderr, "Invalid option argument: %s\n\n", arg); usage(); return 0; /* not actually reached */ } /* convert the argument string pointed to by arg to a long int representation */ static long argtol(const char *restrict arg, int base) { char *endp; long val; errno = 0; val = strtol(arg, &endp, base); if (errno != 0) { fprintf(stderr, "Invalid option argument: %s\n", arg); fprintf(stderr, "Error: %s\n\n", strerror(errno)); usage(); } else if (endp == arg || *endp != '\0') { fprintf(stderr, "Invalid option argument: %s\n\n", arg); usage(); } return val; } static int parse_evtmask(char *arg) { /* search filtering class */ if (strcmp(arg, "gen") == 0){ opts.evt_mask |= TRC_GEN; } else if(strcmp(arg, "sched") == 0){ opts.evt_mask |= TRC_SCHED; } else if(strcmp(arg, "dom0op") == 0){ opts.evt_mask |= TRC_DOM0OP; } else if(strcmp(arg, "hvm") == 0){ opts.evt_mask |= TRC_HVM; } else if(strcmp(arg, "all") == 0){ opts.evt_mask |= TRC_ALL; } else { opts.evt_mask = argtol(arg, 0); } return 0; } #define ZERO_DIGIT '0' #define is_terminator(c) ((c)=='\0' || (c)==',') static int parse_cpumask_range(const char *mask_str, xc_cpumap_t map) { unsigned int a, b; int nmaskbits; char c; int in_range; const char *s; nmaskbits = xc_get_max_cpus(xc_handle); if ( nmaskbits <= 0 ) { fprintf(stderr, "Failed to get max number of CPUs! rc: %d\n", nmaskbits); return EXIT_FAILURE; } c = 0; s = mask_str; do { in_range = 0; a = b = 0; /* Process until we find a range terminator */ for ( c=*s++; !is_terminator(c); c=*s++ ) { if ( c == '-' ) { if ( in_range ) goto err_out; b = 0; in_range = 1; continue; } if ( !isdigit(c) ) { fprintf(stderr, "Invalid character in cpumask: %s\n", mask_str); goto err_out; } b = b * 10 + (c - ZERO_DIGIT); if ( !in_range ) a = b; } /* Syntax: -[,] - expand to number of CPUs. */ if ( b == 0 && in_range ) b = nmaskbits-1; if ( a > b ) { fprintf(stderr, "Wrong order of %d and %d\n", a, b); goto err_out; } if ( b >= nmaskbits ) { fprintf(stderr, "Specified higher value then there are CPUS!\n"); goto err_out; } while ( a <= b ) { xc_cpumap_setcpu(a, map); a++; } } while ( c ); return 0; err_out: errno = EINVAL; return EXIT_FAILURE; } /** * Figure out which of the CPU types the user has provided - either the hex * variant, the cpu-list, or 'all'. Once done set the CPU mask. */ static int parse_cpu_mask(void) { int i, ret = EXIT_FAILURE; xc_cpumap_t map; map = xc_cpumap_alloc(xc_handle); if ( !map ) goto out; if ( strlen(opts.cpu_mask_str) < 1 ) { errno = ENOSPC; goto out; } ret = 0; if ( strncmp("0x", opts.cpu_mask_str, 2) == 0 ) { uint32_t v; v = argtol(opts.cpu_mask_str, 0); /* * If mask is set, copy the bits out of it. This still works for * systems with more than 32 cpus, as the shift will just shift * mask down to zero. */ for ( i = 0; i < sizeof(uint32_t); i++ ) map[i] = (v >> (i * 8)) & 0xff; } else if ( strcmp("all", opts.cpu_mask_str) == 0 ) { for ( i = 0; i < xc_get_cpumap_size(xc_handle); i++ ) map[i] = 0xff; } else ret = parse_cpumask_range(opts.cpu_mask_str, map); if ( !ret ) ret = set_cpu_mask(map); out: /* We don't use them pass this point. */ free(map); free(opts.cpu_mask_str); opts.cpu_mask_str = NULL; return ret; } /* parse command line arguments */ static void parse_args(int argc, char **argv) { int option; static struct option long_options[] = { { "log-thresh", required_argument, 0, 't' }, { "poll-sleep", required_argument, 0, 's' }, { "cpu-mask", required_argument, 0, 'c' }, { "evt-mask", required_argument, 0, 'e' }, { "trace-buf-size", required_argument, 0, 'S' }, { "reserve-disk-space", required_argument, 0, 'r' }, { "time-interval", required_argument, 0, 'T' }, { "memory-buffer", required_argument, 0, 'M' }, { "discard-buffers", no_argument, 0, 'D' }, { "dont-disable-tracing", no_argument, 0, 'x' }, { "start-disabled", no_argument, 0, 'X' }, { "help", no_argument, 0, '?' }, { "version", no_argument, 0, 'V' }, { 0, 0, 0, 0 } }; while ( (option = getopt_long(argc, argv, "t:s:c:e:S:r:T:M:DxX?V", long_options, NULL)) != -1) { switch ( option ) { case 's': /* set sleep time (given in milliseconds) */ opts.poll_sleep = argtol(optarg, 0); break; case 'c': /* set new cpu mask for filtering (when xch is set). */ opts.cpu_mask_str = strdup(optarg); break; case 'e': /* set new event mask for filtering*/ parse_evtmask(optarg); break; case 'S': /* set tbuf size (given in pages) */ opts.tbuf_size = argtol(optarg, 0); break; case 'V': /* print program version */ printf("%s\n", program_version); exit(EXIT_SUCCESS); break; case 'D': /* Discard traces currently in buffer */ opts.discard = 1; break; case 'r': /* Disk-space reservation */ opts.disk_rsvd = argtol(optarg, 0); break; case 'x': /* Don't disable tracing */ opts.disable_tracing = 0; break; case 'X': /* Start disabled */ opts.start_disabled = 1; break; case 'T': opts.timeout = argtol(optarg, 0); break; case 'M': opts.memory_buffer = sargtol(optarg, 0); break; default: usage(); } } /* get outfile (required last argument) */ if (optind != (argc-1)) usage(); opts.outfile = argv[optind]; } /* *BSD has no O_LARGEFILE */ #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif int main(int argc, char **argv) { int ret; struct sigaction act; opts.outfile = 0; opts.poll_sleep = POLL_SLEEP_MILLIS; opts.evt_mask = 0; opts.cpu_mask_str = NULL; opts.disk_rsvd = 0; opts.disable_tracing = 1; opts.start_disabled = 0; opts.timeout = 0; parse_args(argc, argv); xc_handle = xc_interface_open(0,0,0); if ( !xc_handle ) { perror("xenctrl interface open"); exit(EXIT_FAILURE); } if ( opts.evt_mask != 0 ) set_evt_mask(opts.evt_mask); if ( opts.cpu_mask_str ) { if ( parse_cpu_mask() ) exit(EXIT_FAILURE); } if ( opts.timeout != 0 ) alarm(opts.timeout); if ( opts.outfile ) outfd = open(opts.outfile, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); if ( outfd < 0 ) { perror("Could not open output file"); exit(EXIT_FAILURE); } if ( isatty(outfd) ) { fprintf(stderr, "Cannot output to a TTY, specify a log file.\n"); exit(EXIT_FAILURE); } if ( opts.memory_buffer > 0 ) membuf_alloc(opts.memory_buffer); /* ensure that if we get a signal, we'll do cleanup, then exit */ act.sa_handler = close_handler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGHUP, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGALRM, &act, NULL); ret = monitor_tbufs(); return ret; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xentrace/xenalyze.c0000664000175000017500000114521713256712137015444 0ustar smbsmb/* * xenalyze.c: Analyzing xentrace output * * Written by George Dunlap. * * Copyright (c) 2006-2007, XenSource Inc. * Copyright (c) 2007-2008, Citrix Systems R&D Ltd, UK * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #define _XOPEN_SOURCE 600 #include #include #include #include #include #include #include #include #include #include #include "analyze.h" #include "mread.h" #include "pv.h" #include #include #include #include struct mread_ctrl; #define DEFAULT_CPU_HZ 2400000000LL #define QHZ_FROM_HZ(_hz) (((_hz) << 10)/ 1000000000) #define ADDR_SPACE_BITS 48 #define DEFAULT_SAMPLE_SIZE 1024 #define DEFAULT_SAMPLE_MAX 1024*1024*32 #define DEFAULT_INTERVAL_LENGTH 1000 struct array_struct { unsigned long long *values; int count; }; #define warn_once(_x...) \ do { \ static int _w=1; \ if ( _w ) { \ _w=0; \ fprintf(warn, ##_x); \ } \ } while(0) \ /* -- Global variables -- */ struct { int fd; struct mread_ctrl *mh; struct symbol_struct * symbols; char * symbol_file; char * trace_file; int output_defined; off_t file_size; struct { off_t update_offset; int pipe[2]; FILE* out; int pid; } progress; } G = { .fd=-1, .symbols = NULL, .symbol_file = NULL, .trace_file = NULL, .output_defined = 0, .file_size = 0, .progress = { .update_offset = 0 }, }; /* Kinds of errors: Unexpected values - RIP with information in high bits (not all 0 or 1) - exit reason too high Unexpected record layout - x64 bit set in PIO,PV_PTWR_EMULATION_PAE, - Unknown minor type (PV_PTWR_EMULATION, RUNSTATE_CHANGE - Wrong record size - More than one bit set in evt.main field Unexpected sequences - wake tsc tracking - TSC dependency loop - Mismatch between non-running old event states - Runstate continue while running on another pcpu - lost_record_end seen in non-lost pcpu - Unexpected non-CPU_CHANGE record during new_pcpu scan - record tsc < interval start tsc - lost_record tsc !> order tsc Limited resources - interrupt interval slots - record cpu > MAX_CPUS Algorithm asserts - Duplicate CR3/domain values - Logic holes - domain runstates - runstate / tsc skew - vcpu_{prev,next}_update p->current{==,!=}null - vcpu start conditions - lost_cpu count higher than # of seen cpus / < 0 - lost cpu has non-null p->current Symbol file -file doesn't open -file not ordered System - short read - malloc failed Args - Invalid cpu_hz value / suffix - No trace file - Can't open trace file */ enum error_level { ERR_NONE=0, ERR_STRICT, /* Be unreasonably picky */ ERR_WARN, /* Something midly unexpected */ ERR_SANITY, /* Sanity checks: RIP with info in high bits */ ERR_RECORD, /* Something that keeps you from processing the record */ ERR_FILE, /* Probably caused by a corrupt file */ ERR_LIMIT, /* Exceeded limits; data will be lost */ ERR_MAX_TOLERABLE=ERR_LIMIT, /* -- Unrecoverable past this point -- */ ERR_ASSERT, /* Algoritm assert */ ERR_SYSTEM, /* System error: cannot allocate memory, short read, &c */ }; int verbosity = 5; struct { unsigned scatterplot_interrupt_eip:1, scatterplot_unpin_promote:1, scatterplot_cr3_switch:1, scatterplot_wake_to_halt:1, scatterplot_io:1, scatterplot_vmexit_eip:1, scatterplot_runstate:1, scatterplot_runstate_time:1, scatterplot_pcpu:1, scatterplot_extint_cycles:1, scatterplot_rdtsc:1, scatterplot_irq:1, histogram_interrupt_eip:1, interval_mode:1, dump_all:1, dump_raw_process:1, dump_raw_reads:1, dump_no_processing:1, dump_ipi_latency:1, dump_trace_volume_on_lost_record:1, dump_show_power_states:1, with_cr3_enumeration:1, with_pio_enumeration:1, with_mmio_enumeration:1, with_interrupt_eip_enumeration:1, show_default_domain_summary:1, mmio_enumeration_skip_vga:1, progress:1, svm_mode:1, summary:1, report_pcpu:1, tsc_loop_fatal:1, summary_info; long long cpu_qhz, cpu_hz; int scatterplot_interrupt_vector; int scatterplot_extint_cycles_vector; int scatterplot_io_port; int histogram_interrupt_vector; unsigned long long histogram_interrupt_increment; int interrupt_eip_enumeration_vector; int default_guest_paging_levels; int sample_size, sample_max; enum error_level tolerance; /* Tolerate up to this level of error */ struct { tsc_t cycles; /* Used if interval is specified in seconds to delay calculating * time_interval until all arguments have been processed (specifically, * cpu_hz). */ unsigned msec; enum { INTERVAL_CR3_SCHEDULE_TIME, INTERVAL_CR3_SCHEDULE_ORDERED, INTERVAL_CR3_SHORT_SUMMARY, INTERVAL_DOMAIN_TOTAL_TIME, INTERVAL_DOMAIN_SHORT_SUMMARY, INTERVAL_DOMAIN_GUEST_INTERRUPT, INTERVAL_DOMAIN_GRANT_MAPS } output; enum { INTERVAL_MODE_CUSTOM, INTERVAL_MODE_ARRAY, INTERVAL_MODE_LIST } mode; enum { INTERVAL_CHECK_NONE, INTERVAL_CHECK_CR3, INTERVAL_CHECK_DOMAIN } check; /* Options for specific interval output types */ union { struct array_struct array; }; int count; } interval; } opt = { .scatterplot_interrupt_eip=0, .scatterplot_unpin_promote=0, .scatterplot_cr3_switch=0, .scatterplot_wake_to_halt=0, .scatterplot_vmexit_eip=0, .scatterplot_runstate=0, .scatterplot_runstate_time=0, .scatterplot_pcpu=0, .scatterplot_extint_cycles=0, .scatterplot_rdtsc=0, .scatterplot_irq=0, .histogram_interrupt_eip=0, .dump_all = 0, .dump_raw_process = 0, .dump_raw_reads = 0, .dump_no_processing = 0, .dump_ipi_latency = 0, .dump_trace_volume_on_lost_record = 0, .dump_show_power_states = 0, .with_cr3_enumeration = 0, .with_pio_enumeration = 1, .with_mmio_enumeration = 0, .with_interrupt_eip_enumeration = 0, .show_default_domain_summary = 0, .mmio_enumeration_skip_vga = 1, .progress = 0, .svm_mode = 0, .summary = 0, .report_pcpu = 0, .tsc_loop_fatal = 0, .cpu_hz = DEFAULT_CPU_HZ, /* Pre-calculate a multiplier that makes the rest of the * calculations easier */ .cpu_qhz = QHZ_FROM_HZ(DEFAULT_CPU_HZ), .default_guest_paging_levels = 2, .sample_size = DEFAULT_SAMPLE_SIZE, .sample_max = DEFAULT_SAMPLE_MAX, .tolerance = ERR_SANITY, .interval = { .msec = DEFAULT_INTERVAL_LENGTH }, }; FILE *warn = NULL; /* -- Summary data -- */ struct cycle_framework { tsc_t first_tsc, last_tsc, total_cycles; }; struct interval_element { int count; long long cycles; long long instructions; }; struct cycle_summary { int event_count, count, sample_size; unsigned long long cycles; long long *sample; struct interval_element interval; }; /* -- Symbol list information -- */ #define SYMBOL_ENTRIES_PER_STRUCT 1023 #define SYMBOL_NAME_SIZE 124 struct symbol_struct { int count; struct { unsigned long long addr; char name[SYMBOL_NAME_SIZE]; } symbols[SYMBOL_ENTRIES_PER_STRUCT]; struct symbol_struct *next; }; void error(enum error_level l, struct record_info *ri); void parse_symbol_file(char *fn) { unsigned long long last_addr = 0; FILE * symbol_file; struct symbol_struct ** p=&G.symbols; if((symbol_file=fopen(fn, "rb"))==NULL) { fprintf(stderr, "Could not open symbol file %s\n", fn); perror("open"); error(ERR_SYSTEM, NULL); } while(!feof(symbol_file)) { /* Allocate a new struct if we need it */ if(!*p) { *p = malloc(sizeof(**p)); if(!*p) { fprintf(stderr, "Malloc failed!\n"); error(ERR_SYSTEM, NULL); } (*p)->count=0; (*p)->next=NULL; } /* FIXME -- use SYMBOL_NAME_SIZE */ /* FIXME -- use regexp. This won't work for symbols with spaces (yes they exist) */ (*p)->symbols[(*p)->count].addr = 0xDEADBEEF; if ( fscanf(symbol_file, "%llx %128s", &(*p)->symbols[(*p)->count].addr, (*p)->symbols[(*p)->count].name) == 0 ) break; if( ((*p)->symbols[(*p)->count].addr > 0) && ((*p)->symbols[(*p)->count].addr < last_addr) ) { fprintf(stderr, "Symbol file not properly ordered: %llx %s < %llx!\n", (*p)->symbols[(*p)->count].addr, (*p)->symbols[(*p)->count].name, last_addr); /* Could be recovered from; just free existing strings and set symbols to NULL */ error(ERR_ASSERT, NULL); } else last_addr = (*p)->symbols[(*p)->count].addr; (*p)->count++; /* If this struct is full, point to the next. It will be allocated if needed. */ if((*p)->count == SYMBOL_ENTRIES_PER_STRUCT) { p=&((*p)->next); } } fclose(symbol_file); } /* WARNING not thread safe */ char * find_symbol(unsigned long long addr) { struct symbol_struct * p=G.symbols; int i; char * lastname="ZERO"; unsigned long long offset=addr; static char name[128]; if(!p) { name[0]=0; return name; } while(1) { if(!p) goto finish; for(i=0; icount; i++) { if(p->symbols[i].addr > addr) goto finish; else { lastname=p->symbols[i].name; offset=addr - p->symbols[i].addr; } } p=p->next; } finish: snprintf(name, 128, "(%s +%llx)", lastname, offset); return name; } /* -- Eip list data -- */ enum { EIP_LIST_TYPE_NONE=0, EIP_LIST_TYPE_MAX }; struct eip_list_struct { struct eip_list_struct *next; unsigned long long eip; struct cycle_summary summary; int type; void * extra; }; struct { void (*update)(struct eip_list_struct *, void *); void (*new)(struct eip_list_struct *, void *); void (*dump)(struct eip_list_struct *); } eip_list_type[EIP_LIST_TYPE_MAX] = { [EIP_LIST_TYPE_NONE] = { .update=NULL, .new=NULL, .dump=NULL }, }; /* --- HVM class of events --- */ /* * -- Algorithms -- * * Interrupt Wake-to-halt detection * * Purpose: To correlate device interrupts to vcpu runtime. * * Diagram: * ... * blocked -> runnable <- set to waking * ... * runnable -> running * inj_virq A <- Note "waking" interrupt * vmenter <- Start tsc of "wake-to-halt" interval. Turn off 'waking'. * ... * inj_virq B <- Note alternate interrupt * vmenter <- Start tsc of "interrupt-to-halt" interval * ... * vmexit <- End tsc of "x-to-halt" interval * running -> blocked <- Process * * The "waking" interrupts we want to sub-classify into * "wake-only" (when interrupt was the only interrupt from wake to halt) and * "wake-all" (whether this was the only interrupt or not). */ /* VMX data */ #define EXIT_REASON_EXCEPTION_NMI 0 #define EXIT_REASON_EXTERNAL_INTERRUPT 1 #define EXIT_REASON_TRIPLE_FAULT 2 #define EXIT_REASON_INIT 3 #define EXIT_REASON_SIPI 4 #define EXIT_REASON_IO_SMI 5 #define EXIT_REASON_OTHER_SMI 6 #define EXIT_REASON_PENDING_INTERRUPT 7 #define EXIT_REASON_PENDING_VIRT_NMI 8 #define EXIT_REASON_TASK_SWITCH 9 #define EXIT_REASON_CPUID 10 #define EXIT_REASON_GETSEC 11 #define EXIT_REASON_HLT 12 #define EXIT_REASON_INVD 13 #define EXIT_REASON_INVLPG 14 #define EXIT_REASON_RDPMC 15 #define EXIT_REASON_RDTSC 16 #define EXIT_REASON_RSM 17 #define EXIT_REASON_VMCALL 18 #define EXIT_REASON_VMCLEAR 19 #define EXIT_REASON_VMLAUNCH 20 #define EXIT_REASON_VMPTRLD 21 #define EXIT_REASON_VMPTRST 22 #define EXIT_REASON_VMREAD 23 #define EXIT_REASON_VMRESUME 24 #define EXIT_REASON_VMWRITE 25 #define EXIT_REASON_VMOFF 26 #define EXIT_REASON_VMON 27 #define EXIT_REASON_CR_ACCESS 28 #define EXIT_REASON_DR_ACCESS 29 #define EXIT_REASON_IO_INSTRUCTION 30 #define EXIT_REASON_MSR_READ 31 #define EXIT_REASON_MSR_WRITE 32 #define EXIT_REASON_INVALID_GUEST_STATE 33 #define EXIT_REASON_MSR_LOADING 34 #define EXIT_REASON_MWAIT_INSTRUCTION 36 #define EXIT_REASON_MONITOR_TRAP_FLAG 37 #define EXIT_REASON_MONITOR_INSTRUCTION 39 #define EXIT_REASON_PAUSE_INSTRUCTION 40 #define EXIT_REASON_MACHINE_CHECK 41 #define EXIT_REASON_TPR_BELOW_THRESHOLD 43 #define EXIT_REASON_APIC_ACCESS 44 #define EXIT_REASON_ACCESS_GDTR_OR_IDTR 46 #define EXIT_REASON_ACCESS_LDTR_OR_TR 47 #define EXIT_REASON_EPT_VIOLATION 48 #define EXIT_REASON_EPT_MISCONFIG 49 #define EXIT_REASON_INVEPT 50 #define EXIT_REASON_RDTSCP 51 #define EXIT_REASON_VMX_PREEMPTION_TIMER_EXPIRED 52 #define EXIT_REASON_INVVPID 53 #define EXIT_REASON_WBINVD 54 #define EXIT_REASON_XSETBV 55 #define HVM_VMX_EXIT_REASON_MAX (EXIT_REASON_XSETBV+1) char * hvm_vmx_exit_reason_name[HVM_VMX_EXIT_REASON_MAX] = { [EXIT_REASON_EXCEPTION_NMI]="EXCEPTION_NMI", [EXIT_REASON_EXTERNAL_INTERRUPT]="EXTERNAL_INTERRUPT", [EXIT_REASON_TRIPLE_FAULT]="TRIPLE_FAULT", [EXIT_REASON_INIT]="INIT", [EXIT_REASON_SIPI]="SIPI", [EXIT_REASON_IO_SMI]="IO_SMI", [EXIT_REASON_OTHER_SMI]="OTHER_SMI", [EXIT_REASON_PENDING_INTERRUPT]="PENDING_INTERRUPT", [EXIT_REASON_PENDING_VIRT_NMI]="PENDING_VIRT_NMI", [EXIT_REASON_TASK_SWITCH]="TASK_SWITCH", [EXIT_REASON_CPUID]="CPUID", [EXIT_REASON_GETSEC]="GETSEC", [EXIT_REASON_HLT]="HLT", [EXIT_REASON_INVD]="INVD", [EXIT_REASON_INVLPG]="INVLPG", [EXIT_REASON_RDPMC]="RDPMC", [EXIT_REASON_RDTSC]="RDTSC", [EXIT_REASON_RSM]="RSM", [EXIT_REASON_VMCALL]="VMCALL", [EXIT_REASON_VMCLEAR]="VMCLEAR", [EXIT_REASON_VMLAUNCH]="VMLAUNCH", [EXIT_REASON_VMPTRLD]="VMPTRLD", [EXIT_REASON_VMPTRST]="VMPTRST", [EXIT_REASON_VMREAD]="VMREAD", [EXIT_REASON_VMRESUME]="VMRESUME", [EXIT_REASON_VMWRITE]="VMWRITE", [EXIT_REASON_VMOFF]="VMOFF", [EXIT_REASON_VMON]="VMON", [EXIT_REASON_CR_ACCESS]="CR_ACCESS", [EXIT_REASON_DR_ACCESS]="DR_ACCESS", [EXIT_REASON_IO_INSTRUCTION]="IO_INSTRUCTION", [EXIT_REASON_MSR_READ]="MSR_READ", [EXIT_REASON_MSR_WRITE]="MSR_WRITE", [EXIT_REASON_INVALID_GUEST_STATE]="INVALID_GUEST_STATE", [EXIT_REASON_MSR_LOADING]="MSR_LOADING", [EXIT_REASON_MWAIT_INSTRUCTION]="MWAIT_INSTRUCTION", [EXIT_REASON_MONITOR_TRAP_FLAG]="MONITOR_TRAP_FLAG", [EXIT_REASON_MONITOR_INSTRUCTION]="MONITOR_INSTRUCTION", [EXIT_REASON_PAUSE_INSTRUCTION]="PAUSE_INSTRUCTION", [EXIT_REASON_MACHINE_CHECK]="MACHINE_CHECK", [EXIT_REASON_TPR_BELOW_THRESHOLD]="TPR_BELOW_THRESHOLD", [EXIT_REASON_APIC_ACCESS]="APIC_ACCESS", [EXIT_REASON_EPT_VIOLATION]="EPT_VIOLATION", [EXIT_REASON_EPT_MISCONFIG]="EPT_MISCONFIG", [EXIT_REASON_INVEPT]="INVEPT", [EXIT_REASON_RDTSCP]="RDTSCP", [EXIT_REASON_VMX_PREEMPTION_TIMER_EXPIRED]="VMX_PREEMPTION_TIMER_EXPIRED", [EXIT_REASON_INVVPID]="INVVPID", [EXIT_REASON_WBINVD]="WBINVD", [EXIT_REASON_XSETBV]="XSETBV", }; /* SVM data */ enum VMEXIT_EXITCODE { /* control register read exitcodes */ VMEXIT_CR0_READ = 0, VMEXIT_CR1_READ = 1, VMEXIT_CR2_READ = 2, VMEXIT_CR3_READ = 3, VMEXIT_CR4_READ = 4, VMEXIT_CR5_READ = 5, VMEXIT_CR6_READ = 6, VMEXIT_CR7_READ = 7, VMEXIT_CR8_READ = 8, VMEXIT_CR9_READ = 9, VMEXIT_CR10_READ = 10, VMEXIT_CR11_READ = 11, VMEXIT_CR12_READ = 12, VMEXIT_CR13_READ = 13, VMEXIT_CR14_READ = 14, VMEXIT_CR15_READ = 15, /* control register write exitcodes */ VMEXIT_CR0_WRITE = 16, VMEXIT_CR1_WRITE = 17, VMEXIT_CR2_WRITE = 18, VMEXIT_CR3_WRITE = 19, VMEXIT_CR4_WRITE = 20, VMEXIT_CR5_WRITE = 21, VMEXIT_CR6_WRITE = 22, VMEXIT_CR7_WRITE = 23, VMEXIT_CR8_WRITE = 24, VMEXIT_CR9_WRITE = 25, VMEXIT_CR10_WRITE = 26, VMEXIT_CR11_WRITE = 27, VMEXIT_CR12_WRITE = 28, VMEXIT_CR13_WRITE = 29, VMEXIT_CR14_WRITE = 30, VMEXIT_CR15_WRITE = 31, /* debug register read exitcodes */ VMEXIT_DR0_READ = 32, VMEXIT_DR1_READ = 33, VMEXIT_DR2_READ = 34, VMEXIT_DR3_READ = 35, VMEXIT_DR4_READ = 36, VMEXIT_DR5_READ = 37, VMEXIT_DR6_READ = 38, VMEXIT_DR7_READ = 39, VMEXIT_DR8_READ = 40, VMEXIT_DR9_READ = 41, VMEXIT_DR10_READ = 42, VMEXIT_DR11_READ = 43, VMEXIT_DR12_READ = 44, VMEXIT_DR13_READ = 45, VMEXIT_DR14_READ = 46, VMEXIT_DR15_READ = 47, /* debug register write exitcodes */ VMEXIT_DR0_WRITE = 48, VMEXIT_DR1_WRITE = 49, VMEXIT_DR2_WRITE = 50, VMEXIT_DR3_WRITE = 51, VMEXIT_DR4_WRITE = 52, VMEXIT_DR5_WRITE = 53, VMEXIT_DR6_WRITE = 54, VMEXIT_DR7_WRITE = 55, VMEXIT_DR8_WRITE = 56, VMEXIT_DR9_WRITE = 57, VMEXIT_DR10_WRITE = 58, VMEXIT_DR11_WRITE = 59, VMEXIT_DR12_WRITE = 60, VMEXIT_DR13_WRITE = 61, VMEXIT_DR14_WRITE = 62, VMEXIT_DR15_WRITE = 63, /* processor exception exitcodes (VMEXIT_EXCP[0-31]) */ VMEXIT_EXCEPTION_DE = 64, /* divide-by-zero-error */ VMEXIT_EXCEPTION_DB = 65, /* debug */ VMEXIT_EXCEPTION_NMI = 66, /* non-maskable-interrupt */ VMEXIT_EXCEPTION_BP = 67, /* breakpoint */ VMEXIT_EXCEPTION_OF = 68, /* overflow */ VMEXIT_EXCEPTION_BR = 69, /* bound-range */ VMEXIT_EXCEPTION_UD = 70, /* invalid-opcode*/ VMEXIT_EXCEPTION_NM = 71, /* device-not-available */ VMEXIT_EXCEPTION_DF = 72, /* double-fault */ VMEXIT_EXCEPTION_09 = 73, /* unsupported (reserved) */ VMEXIT_EXCEPTION_TS = 74, /* invalid-tss */ VMEXIT_EXCEPTION_NP = 75, /* segment-not-present */ VMEXIT_EXCEPTION_SS = 76, /* stack */ VMEXIT_EXCEPTION_GP = 77, /* general-protection */ VMEXIT_EXCEPTION_PF = 78, /* page-fault */ VMEXIT_EXCEPTION_15 = 79, /* reserved */ VMEXIT_EXCEPTION_MF = 80, /* x87 floating-point exception-pending */ VMEXIT_EXCEPTION_AC = 81, /* alignment-check */ VMEXIT_EXCEPTION_MC = 82, /* machine-check */ VMEXIT_EXCEPTION_XF = 83, /* simd floating-point */ /* exceptions 20-31 (exitcodes 84-95) are reserved */ /* ...and the rest of the #VMEXITs */ VMEXIT_INTR = 96, VMEXIT_NMI = 97, VMEXIT_SMI = 98, VMEXIT_INIT = 99, VMEXIT_VINTR = 100, VMEXIT_CR0_SEL_WRITE = 101, VMEXIT_IDTR_READ = 102, VMEXIT_GDTR_READ = 103, VMEXIT_LDTR_READ = 104, VMEXIT_TR_READ = 105, VMEXIT_IDTR_WRITE = 106, VMEXIT_GDTR_WRITE = 107, VMEXIT_LDTR_WRITE = 108, VMEXIT_TR_WRITE = 109, VMEXIT_RDTSC = 110, VMEXIT_RDPMC = 111, VMEXIT_PUSHF = 112, VMEXIT_POPF = 113, VMEXIT_CPUID = 114, VMEXIT_RSM = 115, VMEXIT_IRET = 116, VMEXIT_SWINT = 117, VMEXIT_INVD = 118, VMEXIT_PAUSE = 119, VMEXIT_HLT = 120, VMEXIT_INVLPG = 121, VMEXIT_INVLPGA = 122, VMEXIT_IOIO = 123, VMEXIT_MSR = 124, VMEXIT_TASK_SWITCH = 125, VMEXIT_FERR_FREEZE = 126, VMEXIT_SHUTDOWN = 127, VMEXIT_VMRUN = 128, VMEXIT_VMMCALL = 129, VMEXIT_VMLOAD = 130, VMEXIT_VMSAVE = 131, VMEXIT_STGI = 132, VMEXIT_CLGI = 133, VMEXIT_SKINIT = 134, VMEXIT_RDTSCP = 135, VMEXIT_ICEBP = 136, VMEXIT_WBINVD = 137, VMEXIT_MONITOR = 138, VMEXIT_MWAIT = 139, VMEXIT_MWAIT_CONDITIONAL= 140, VMEXIT_NPF = 1024, /* nested paging fault */ VMEXIT_INVALID = -1 }; #define HVM_SVM_EXIT_REASON_MAX 1025 char * hvm_svm_exit_reason_name[HVM_SVM_EXIT_REASON_MAX] = { /* 0-15 */ "VMEXIT_CR0_READ", "VMEXIT_CR1_READ", "VMEXIT_CR2_READ", "VMEXIT_CR3_READ", "VMEXIT_CR4_READ", "VMEXIT_CR5_READ", "VMEXIT_CR6_READ", "VMEXIT_CR7_READ", "VMEXIT_CR8_READ", "VMEXIT_CR9_READ", "VMEXIT_CR10_READ", "VMEXIT_CR11_READ", "VMEXIT_CR12_READ", "VMEXIT_CR13_READ", "VMEXIT_CR14_READ", "VMEXIT_CR15_READ", /* 16-31 */ "VMEXIT_CR0_WRITE", "VMEXIT_CR1_WRITE", "VMEXIT_CR2_WRITE", "VMEXIT_CR3_WRITE", "VMEXIT_CR4_WRITE", "VMEXIT_CR5_WRITE", "VMEXIT_CR6_WRITE", "VMEXIT_CR7_WRITE", "VMEXIT_CR8_WRITE", "VMEXIT_CR9_WRITE", "VMEXIT_CR10_WRITE", "VMEXIT_CR11_WRITE", "VMEXIT_CR12_WRITE", "VMEXIT_CR13_WRITE", "VMEXIT_CR14_WRITE", "VMEXIT_CR15_WRITE", /* 32-47 */ "VMEXIT_DR0_READ", "VMEXIT_DR1_READ", "VMEXIT_DR2_READ", "VMEXIT_DR3_READ", "VMEXIT_DR4_READ", "VMEXIT_DR5_READ", "VMEXIT_DR6_READ", "VMEXIT_DR7_READ", "VMEXIT_DR8_READ", "VMEXIT_DR9_READ", "VMEXIT_DR10_READ", "VMEXIT_DR11_READ", "VMEXIT_DR12_READ", "VMEXIT_DR13_READ", "VMEXIT_DR14_READ", "VMEXIT_DR15_READ", /* 48-63 */ "VMEXIT_DR0_WRITE", "VMEXIT_DR1_WRITE", "VMEXIT_DR2_WRITE", "VMEXIT_DR3_WRITE", "VMEXIT_DR4_WRITE", "VMEXIT_DR5_WRITE", "VMEXIT_DR6_WRITE", "VMEXIT_DR7_WRITE", "VMEXIT_DR8_WRITE", "VMEXIT_DR9_WRITE", "VMEXIT_DR10_WRITE", "VMEXIT_DR11_WRITE", "VMEXIT_DR12_WRITE", "VMEXIT_DR13_WRITE", "VMEXIT_DR14_WRITE", "VMEXIT_DR15_WRITE", /* 64-83 */ "VMEXIT_EXCEPTION_DE", "VMEXIT_EXCEPTION_DB", "VMEXIT_EXCEPTION_NMI", "VMEXIT_EXCEPTION_BP", "VMEXIT_EXCEPTION_OF", "VMEXIT_EXCEPTION_BR", "VMEXIT_EXCEPTION_UD", "VMEXIT_EXCEPTION_NM", "VMEXIT_EXCEPTION_DF", "VMEXIT_EXCEPTION_09", "VMEXIT_EXCEPTION_TS", "VMEXIT_EXCEPTION_NP", "VMEXIT_EXCEPTION_SS", "VMEXIT_EXCEPTION_GP", "VMEXIT_EXCEPTION_PF", "VMEXIT_EXCEPTION_15", "VMEXIT_EXCEPTION_MF", "VMEXIT_EXCEPTION_AC", "VMEXIT_EXCEPTION_MC", "VMEXIT_EXCEPTION_XF", /* 84-95 */ "VMEXIT_EXCEPTION_20", "VMEXIT_EXCEPTION_21", "VMEXIT_EXCEPTION_22", "VMEXIT_EXCEPTION_23", "VMEXIT_EXCEPTION_24", "VMEXIT_EXCEPTION_25", "VMEXIT_EXCEPTION_26", "VMEXIT_EXCEPTION_27", "VMEXIT_EXCEPTION_28", "VMEXIT_EXCEPTION_29", "VMEXIT_EXCEPTION_30", "VMEXIT_EXCEPTION_31", /* 96-99 */ "VMEXIT_INTR", "VMEXIT_NMI", "VMEXIT_SMI", "VMEXIT_INIT", /* 100-109 */ "VMEXIT_VINTR", "VMEXIT_CR0_SEL_WRITE", "VMEXIT_IDTR_READ", "VMEXIT_GDTR_READ", "VMEXIT_LDTR_READ", "VMEXIT_TR_READ", "VMEXIT_IDTR_WRITE", "VMEXIT_GDTR_WRITE", "VMEXIT_LDTR_WRITE", "VMEXIT_TR_WRITE", /* 110-119 */ "VMEXIT_RDTSC", "VMEXIT_RDPMC", "VMEXIT_PUSHF", "VMEXIT_POPF", "VMEXIT_CPUID", "VMEXIT_RSM", "VMEXIT_IRET", "VMEXIT_SWINT", "VMEXIT_INVD", "VMEXIT_PAUSE", /* 120-129 */ "VMEXIT_HLT", "VMEXIT_INVLPG", "VMEXIT_INVLPGA", "VMEXIT_IOIO", "VMEXIT_MSR", "VMEXIT_TASK_SWITCH", "VMEXIT_FERR_FREEZE", "VMEXIT_SHUTDOWN", "VMEXIT_VMRUN", "VMEXIT_VMMCALL", /* 130-139 */ "VMEXIT_VMLOAD", "VMEXIT_VMSAVE", "VMEXIT_STGI", "VMEXIT_CLGI", "VMEXIT_SKINIT", "VMEXIT_RDTSCP", "VMEXIT_ICEBP", "VMEXIT_WBINVD", "VMEXIT_MONITOR", "VMEXIT_MWAIT", /* 140 */ "VMEXIT_MWAIT_CONDITIONAL", [VMEXIT_NPF] = "VMEXIT_NPF", /* nested paging fault */ }; #if ( HVM_VMX_EXIT_REASON_MAX > HVM_SVM_EXIT_REASON_MAX ) # define HVM_EXIT_REASON_MAX HVM_VMX_EXIT_REASON_MAX # error - Strange! #else # define HVM_EXIT_REASON_MAX HVM_SVM_EXIT_REASON_MAX #endif /* General hvm information */ #define SPURIOUS_APIC_VECTOR 0xff #define ERROR_APIC_VECTOR 0xfe #define INVALIDATE_TLB_VECTOR 0xfd #define EVENT_CHECK_VECTOR 0xfc #define CALL_FUNCTION_VECTOR 0xfb #define THERMAL_APIC_VECTOR 0xfa #define LOCAL_TIMER_VECTOR 0xf9 #define EXTERNAL_INTERRUPT_MAX 256 /* Stringify numbers */ char * hvm_extint_vector_name[EXTERNAL_INTERRUPT_MAX] = { [SPURIOUS_APIC_VECTOR] = "SPURIOS_APIC", [ERROR_APIC_VECTOR] = "ERROR_APIC", [INVALIDATE_TLB_VECTOR]= "INVALIDATE_TLB", [EVENT_CHECK_VECTOR]= "EVENT_CHECK", [CALL_FUNCTION_VECTOR]= "CALL_FUNCTION", [THERMAL_APIC_VECTOR]= "THERMAL_APIC", [LOCAL_TIMER_VECTOR] = "LOCAL_TIMER", }; #define HVM_TRAP_MAX 20 char * hvm_trap_name[HVM_TRAP_MAX] = { [0] = "Divide", [1] = "RESERVED", [2] = "NMI", [3] = "Breakpoint", [4] = "Overflow", [5] = "BOUND", [6] = "Invalid Op", [7] = "Coprocessor not present", [8] = "Double Fault", [9] = "Coprocessor segment overrun", [10] = "TSS", [11] = "Segment not present", [12] = "Stack-segment fault", [13] = "GP", [14] = "Page fault", [15] = "RESERVED", [16] = "FPU", [17] = "Alignment check", [18] = "Machine check", [19] = "SIMD", }; enum { HVM_EVENT_HANDLER_NONE = 0, HVM_EVENT_HANDLER_PF_XEN = 1, HVM_EVENT_HANDLER_PF_INJECT, HVM_EVENT_HANDLER_INJ_EXC, HVM_EVENT_HANDLER_INJ_VIRQ, HVM_EVENT_HANDLER_REINJ_VIRQ, HVM_EVENT_HANDLER_IO_READ, HVM_EVENT_HANDLER_IO_WRITE, HVM_EVENT_HANDLER_CR_READ, /* 8 */ HVM_EVENT_HANDLER_CR_WRITE, HVM_EVENT_HANDLER_DR_READ, HVM_EVENT_HANDLER_DR_WRITE, HVM_EVENT_HANDLER_MSR_READ, HVM_EVENT_HANDLER_MSR_WRITE, HVM_EVENT_HANDLER_CPUID, HVM_EVENT_HANDLER_INTR, HVM_EVENT_HANDLER_NMI, /* 16 */ HVM_EVENT_HANDLER_SMI, HVM_EVENT_HANDLER_VMCALL, HVM_EVENT_HANDLER_HLT, HVM_EVENT_HANDLER_INVLPG, HVM_EVENT_HANDLER_MCE, HVM_EVENT_HANDLER_IO_ASSIST, HVM_EVENT_HANDLER_MMIO_ASSIST, HVM_EVENT_HANDLER_CLTS, HVM_EVENT_HANDLER_LMSW, HVM_EVENT_RDTSC, HVM_EVENT_INTR_WINDOW=0x20, /* Oops... skipped 0x1b-1f */ HVM_EVENT_NPF, HVM_EVENT_REALMODE_EMULATE, HVM_EVENT_TRAP, HVM_EVENT_TRAP_DEBUG, HVM_EVENT_VLAPIC, HVM_EVENT_HANDLER_MAX }; char * hvm_event_handler_name[HVM_EVENT_HANDLER_MAX] = { "(no handler)", "pf_xen", "pf_inject", "inj_exc", "inj_virq", "reinj_virq", "io_read", "io_write", "cr_read", /* 8 */ "cr_write", "dr_read", "dr_write", "msr_read", "msr_write", "cpuid", "intr", "nmi", /* 16 */ "smi", "vmcall", "hlt", "invlpg", "mce", "io_assist", "mmio_assist", "clts", /* 24 */ "lmsw", "rdtsc", [HVM_EVENT_INTR_WINDOW]="intr_window", "npf", "realmode_emulate", "trap", "trap_debug", "vlapic" }; enum { HVM_VOL_VMENTRY, HVM_VOL_VMEXIT, HVM_VOL_HANDLER, HVM_VOL_MAX }; enum { GUEST_INTERRUPT_CASE_NONE, /* This interrupt woke, no other interrupts until halt */ GUEST_INTERRUPT_CASE_WAKE_TO_HALT_ALONE, /* This interrupt woke, maybe another interrupt before halt */ GUEST_INTERRUPT_CASE_WAKE_TO_HALT_ANY, /* Time from interrupt (running) to halt */ GUEST_INTERRUPT_CASE_INTERRUPT_TO_HALT, GUEST_INTERRUPT_CASE_MAX, }; char *guest_interrupt_case_name[] = { [GUEST_INTERRUPT_CASE_WAKE_TO_HALT_ALONE]="wake to halt alone", /* This interrupt woke, maybe another interrupt before halt */ [GUEST_INTERRUPT_CASE_WAKE_TO_HALT_ANY] ="wake to halt any ", /* Time from interrupt (running) to halt */ [GUEST_INTERRUPT_CASE_INTERRUPT_TO_HALT] ="intr to halt ", }; char *hvm_vol_name[HVM_VOL_MAX] = { [HVM_VOL_VMENTRY]="vmentry", [HVM_VOL_VMEXIT] ="vmexit", [HVM_VOL_HANDLER]="handler", }; enum { HYPERCALL_set_trap_table = 0, HYPERCALL_mmu_update, HYPERCALL_set_gdt, HYPERCALL_stack_switch, HYPERCALL_set_callbacks, HYPERCALL_fpu_taskswitch, HYPERCALL_sched_op_compat, HYPERCALL_platform_op, HYPERCALL_set_debugreg, HYPERCALL_get_debugreg, HYPERCALL_update_descriptor, HYPERCALL_memory_op=12, HYPERCALL_multicall, HYPERCALL_update_va_mapping, HYPERCALL_set_timer_op, HYPERCALL_event_channel_op_compat, HYPERCALL_xen_version, HYPERCALL_console_io, HYPERCALL_physdev_op_compat, HYPERCALL_grant_table_op, HYPERCALL_vm_assist, HYPERCALL_update_va_mapping_otherdomain, HYPERCALL_iret, HYPERCALL_vcpu_op, HYPERCALL_set_segment_base, HYPERCALL_mmuext_op, HYPERCALL_acm_op, HYPERCALL_nmi_op, HYPERCALL_sched_op, HYPERCALL_callback_op, HYPERCALL_xenoprof_op, HYPERCALL_event_channel_op, HYPERCALL_physdev_op, HYPERCALL_hvm_op, HYPERCALL_sysctl, HYPERCALL_domctl, HYPERCALL_kexec_op, HYPERCALL_MAX }; char *hypercall_name[HYPERCALL_MAX] = { [HYPERCALL_set_trap_table]="set_trap_table", [HYPERCALL_mmu_update]="mmu_update", [HYPERCALL_set_gdt]="set_gdt", [HYPERCALL_stack_switch]="stack_switch", [HYPERCALL_set_callbacks]="set_callbacks", [HYPERCALL_fpu_taskswitch]="fpu_taskswitch", [HYPERCALL_sched_op_compat]="sched_op(compat)", [HYPERCALL_platform_op]="platform_op", [HYPERCALL_set_debugreg]="set_debugreg", [HYPERCALL_get_debugreg]="get_debugreg", [HYPERCALL_update_descriptor]="update_descriptor", [HYPERCALL_memory_op]="memory_op", [HYPERCALL_multicall]="multicall", [HYPERCALL_update_va_mapping]="update_va_mapping", [HYPERCALL_set_timer_op]="set_timer_op", [HYPERCALL_event_channel_op_compat]="evtchn_op(compat)", [HYPERCALL_xen_version]="xen_version", [HYPERCALL_console_io]="console_io", [HYPERCALL_physdev_op_compat]="physdev_op(compat)", [HYPERCALL_grant_table_op]="grant_table_op", [HYPERCALL_vm_assist]="vm_assist", [HYPERCALL_update_va_mapping_otherdomain]="update_va_mapping_otherdomain", [HYPERCALL_iret]="iret", [HYPERCALL_vcpu_op]="vcpu_op", [HYPERCALL_set_segment_base]="set_segment_base", [HYPERCALL_mmuext_op]="mmuext_op", [HYPERCALL_acm_op]="acm_op", [HYPERCALL_nmi_op]="nmi_op", [HYPERCALL_sched_op]="sched_op", [HYPERCALL_callback_op]="callback_op", [HYPERCALL_xenoprof_op]="xenoprof_op", [HYPERCALL_event_channel_op]="evtchn_op", [HYPERCALL_physdev_op]="physdev_op", [HYPERCALL_hvm_op]="hvm_op", [HYPERCALL_sysctl]="sysctl", [HYPERCALL_domctl]="domctl", [HYPERCALL_kexec_op]="kexec_op" }; enum { PF_XEN_EMUL_LVL_0, PF_XEN_EMUL_LVL_1, PF_XEN_EMUL_LVL_2, PF_XEN_EMUL_LVL_3, PF_XEN_EMUL_LVL_4, PF_XEN_EMUL_EARLY_UNSHADOW, PF_XEN_EMUL_SET_CHANGED, PF_XEN_EMUL_SET_UNCHANGED, PF_XEN_EMUL_SET_FLUSH, PF_XEN_EMUL_SET_ERROR, PF_XEN_EMUL_PROMOTE, PF_XEN_EMUL_DEMOTE, PF_XEN_EMUL_PREALLOC_UNPIN, PF_XEN_EMUL_PREALLOC_UNHOOK, PF_XEN_EMUL_MAX, }; char * pf_xen_emul_name[PF_XEN_EMUL_MAX] = { [PF_XEN_EMUL_LVL_0]="non-linmap", [PF_XEN_EMUL_LVL_1]="linmap l1", [PF_XEN_EMUL_LVL_2]="linmap l2", [PF_XEN_EMUL_LVL_3]="linmap l3", [PF_XEN_EMUL_LVL_4]="linmap l4", [PF_XEN_EMUL_EARLY_UNSHADOW]="early unshadow", [PF_XEN_EMUL_SET_UNCHANGED]="set unchanged", [PF_XEN_EMUL_SET_CHANGED]="set changed", [PF_XEN_EMUL_SET_FLUSH]="set changed", [PF_XEN_EMUL_SET_ERROR]="set changed", [PF_XEN_EMUL_PROMOTE]="promote", [PF_XEN_EMUL_DEMOTE]="demote", [PF_XEN_EMUL_PREALLOC_UNPIN]="unpin", [PF_XEN_EMUL_PREALLOC_UNHOOK]="unhook", }; /* Rio only */ enum { PF_XEN_NON_EMUL_VA_USER, PF_XEN_NON_EMUL_VA_KERNEL, PF_XEN_NON_EMUL_EIP_USER, PF_XEN_NON_EMUL_EIP_KERNEL, PF_XEN_NON_EMUL_MAX, }; char * pf_xen_non_emul_name[PF_XEN_NON_EMUL_MAX] = { [PF_XEN_NON_EMUL_VA_USER]="va user", [PF_XEN_NON_EMUL_VA_KERNEL]="va kernel", [PF_XEN_NON_EMUL_EIP_USER]="eip user", [PF_XEN_NON_EMUL_EIP_KERNEL]="eip kernel", }; enum { PF_XEN_FIXUP_PREALLOC_UNPIN, PF_XEN_FIXUP_PREALLOC_UNHOOK, PF_XEN_FIXUP_UNSYNC, PF_XEN_FIXUP_OOS_ADD, PF_XEN_FIXUP_OOS_EVICT, PF_XEN_FIXUP_PROMOTE, PF_XEN_FIXUP_UPDATE_ONLY, PF_XEN_FIXUP_WRMAP, PF_XEN_FIXUP_BRUTE_FORCE, PF_XEN_FIXUP_MAX, }; char * pf_xen_fixup_name[PF_XEN_FIXUP_MAX] = { [PF_XEN_FIXUP_PREALLOC_UNPIN] = "unpin", [PF_XEN_FIXUP_PREALLOC_UNHOOK] = "unhook", [PF_XEN_FIXUP_UNSYNC] = "unsync", [PF_XEN_FIXUP_OOS_ADD] = "oos-add", [PF_XEN_FIXUP_OOS_EVICT] = "oos-evict", [PF_XEN_FIXUP_PROMOTE] = "promote", [PF_XEN_FIXUP_UPDATE_ONLY] = "update", [PF_XEN_FIXUP_WRMAP] = "wrmap", [PF_XEN_FIXUP_BRUTE_FORCE] = "wrmap-bf", }; enum { PF_XEN_NOT_SHADOW = 1, PF_XEN_FAST_PROPAGATE, PF_XEN_FAST_MMIO, PF_XEN_FALSE_FAST_PATH, PF_XEN_MMIO, PF_XEN_FIXUP, PF_XEN_DOMF_DYING, PF_XEN_EMULATE, PF_XEN_EMULATE_UNSHADOW_USER, PF_XEN_EMULATE_UNSHADOW_EVTINJ, PF_XEN_EMULATE_UNSHADOW_UNHANDLED, PF_XEN_LAST_FAULT=PF_XEN_EMULATE_UNSHADOW_UNHANDLED, PF_XEN_NON_EMULATE, PF_XEN_NO_HANDLER, PF_XEN_MAX, }; #define SHADOW_WRMAP_BF 12 #define SHADOW_PREALLOC_UNPIN 13 #define SHADOW_RESYNC_FULL 14 #define SHADOW_RESYNC_ONLY 15 char * pf_xen_name[PF_XEN_MAX] = { [PF_XEN_NOT_SHADOW]="propagate", [PF_XEN_FAST_PROPAGATE]="fast propagate", [PF_XEN_FAST_MMIO]="fast mmio", [PF_XEN_FALSE_FAST_PATH]="false fast path", [PF_XEN_MMIO]="mmio", [PF_XEN_FIXUP]="fixup", [PF_XEN_DOMF_DYING]="dom dying", [PF_XEN_EMULATE]="emulate", [PF_XEN_EMULATE_UNSHADOW_USER]="unshadow:user-mode", [PF_XEN_EMULATE_UNSHADOW_EVTINJ]="unshadow:evt inj", [PF_XEN_EMULATE_UNSHADOW_UNHANDLED]="unshadow:unhandled instr", [PF_XEN_NON_EMULATE]="fixup|mmio", [PF_XEN_NO_HANDLER]="(no handler)", }; #define CORR_VA_INVALID (0ULL-1) enum { NONPF_MMIO_APIC, NONPF_MMIO_NPF, NONPF_MMIO_UNKNOWN, NONPF_MMIO_MAX }; struct mmio_info { unsigned long long gpa; unsigned long long va; /* Filled only by shadow */ unsigned data; unsigned data_valid:1, is_write:1; }; struct pf_xen_extra { unsigned long long va; union { unsigned flags; struct { unsigned flag_set_ad:1, flag_set_a:1, flag_shadow_l1_get_ref:1, flag_shadow_l1_put_ref:1, flag_l2_propagate:1, flag_set_changed:1, flag_set_flush:1, flag_set_error:1, flag_demote:1, flag_promote:1, flag_wrmap:1, flag_wrmap_guess_found:1, flag_wrmap_brute_force:1, flag_early_unshadow:1, flag_emulation_2nd_pt_written:1, flag_emulation_last_failed:1, flag_emulate_full_pt:1, flag_prealloc_unhook:1, flag_unsync:1, flag_oos_fixup_add:1, flag_oos_fixup_evict:1; }; }; /* Miami + ; fixup & emulate */ unsigned int error_code; /* Rio only */ /* Calculated */ int pf_case; /* Rio */ /* MMIO only */ unsigned long long gpa; unsigned int data; /* Emulate only */ unsigned long long gl1e; /* Miami + */ unsigned long long wval; /* Miami */ unsigned long long corresponding_va; unsigned int pt_index[5], pt_is_lo; int pt_level; /* Other */ unsigned long long gfn; /* Flags */ unsigned corr_valid:1, corr_is_kernel:1, va_is_kernel:1; }; struct pcpu_info; #define GUEST_INTERRUPT_MAX 350 #define FAKE_VECTOR 349 #define CR_MAX 9 #define RESYNCS_MAX 17 #define PF_XEN_FIXUP_UNSYNC_RESYNC_MAX 2 struct hvm_data; struct hvm_summary_handler_node { void (*handler)(struct hvm_data *, void* data); void *data; struct hvm_summary_handler_node *next; }; struct hvm_data { /* Summary information */ int init; int vmexit_valid; int summary_info; struct vcpu_data *v; /* up-pointer */ /* SVM / VMX compatibility. FIXME - should be global */ char ** exit_reason_name; int exit_reason_max; struct hvm_summary_handler_node *exit_reason_summary_handler_list[HVM_EXIT_REASON_MAX]; /* Information about particular exit reasons */ struct { struct cycle_summary exit_reason[HVM_EXIT_REASON_MAX]; int extint[EXTERNAL_INTERRUPT_MAX+1]; int *extint_histogram; struct cycle_summary trap[HVM_TRAP_MAX]; struct cycle_summary pf_xen[PF_XEN_MAX]; struct cycle_summary pf_xen_emul[PF_XEN_EMUL_MAX]; struct cycle_summary pf_xen_emul_early_unshadow[5]; struct cycle_summary pf_xen_non_emul[PF_XEN_NON_EMUL_MAX]; struct cycle_summary pf_xen_fixup[PF_XEN_FIXUP_MAX]; struct cycle_summary pf_xen_fixup_unsync_resync[PF_XEN_FIXUP_UNSYNC_RESYNC_MAX+1]; struct cycle_summary cr_write[CR_MAX]; struct cycle_summary cr3_write_resyncs[RESYNCS_MAX+1]; struct cycle_summary vmcall[HYPERCALL_MAX+1]; struct cycle_summary generic[HVM_EVENT_HANDLER_MAX]; struct cycle_summary mmio[NONPF_MMIO_MAX]; struct hvm_gi_struct { int count; struct cycle_summary runtime[GUEST_INTERRUPT_CASE_MAX]; /* OK, not summary info, but still... */ int is_wake; tsc_t start_tsc; } guest_interrupt[GUEST_INTERRUPT_MAX + 1]; /* IPI Latency */ struct cycle_summary ipi_latency; int ipi_count[256]; struct { struct io_address *mmio, *pio; } io; } summary; /* In-flight accumulation information */ struct { union { struct { unsigned port:31, is_write:1; unsigned int val; } io; struct pf_xen_extra pf_xen; struct { unsigned cr; unsigned long long val; int repromote; } cr_write; struct { unsigned addr; unsigned long long val; } msr; struct { unsigned int event; uint32_t d[4]; } generic; struct { unsigned eax; } vmcall; struct { unsigned vec; } intr; }; /* MMIO gets its separate area, since many exits may use it */ struct mmio_info mmio; }inflight; int resyncs; void (*post_process)(struct hvm_data *); tsc_t exit_tsc, arc_cycles, entry_tsc; unsigned long long rip; unsigned exit_reason, event_handler; int short_summary_done:1, prealloc_unpin:1, wrmap_bf:1; /* Immediate processing */ void *d; /* Wake-to-halt detection. See comment above. */ struct { unsigned waking:1; /* Wake vector: keep track of time from vmentry until: next halt, or next interrupt */ int vector, interrupts, interrupts_wanting_tsc; } w2h; /* Historical info */ tsc_t last_rdtsc; }; enum { HVM_SHORT_SUMMARY_EMULATE, HVM_SHORT_SUMMARY_UNSYNC, HVM_SHORT_SUMMARY_FIXUP, HVM_SHORT_SUMMARY_MMIO, HVM_SHORT_SUMMARY_PROPAGATE, HVM_SHORT_SUMMARY_CR3, HVM_SHORT_SUMMARY_VMCALL, HVM_SHORT_SUMMARY_INTERRUPT, HVM_SHORT_SUMMARY_HLT, HVM_SHORT_SUMMARY_OTHER, HVM_SHORT_SUMMARY_MAX, }; char *hvm_short_summary_name[HVM_SHORT_SUMMARY_MAX] = { [HVM_SHORT_SUMMARY_EMULATE] ="emulate", [HVM_SHORT_SUMMARY_UNSYNC] ="unsync", [HVM_SHORT_SUMMARY_FIXUP] ="fixup", [HVM_SHORT_SUMMARY_MMIO] ="mmio", [HVM_SHORT_SUMMARY_PROPAGATE]="propagate", [HVM_SHORT_SUMMARY_CR3] ="cr3", [HVM_SHORT_SUMMARY_VMCALL] ="vmcall", [HVM_SHORT_SUMMARY_INTERRUPT]="intr", [HVM_SHORT_SUMMARY_HLT] ="hlt", [HVM_SHORT_SUMMARY_OTHER] ="other", }; struct hvm_short_summary_struct { struct cycle_summary s[HVM_SHORT_SUMMARY_MAX]; }; void init_hvm_data(struct hvm_data *h, struct vcpu_data *v) { int i; if(h->init) return; h->v = v; h->init = 1; if(opt.svm_mode) { h->exit_reason_max = HVM_SVM_EXIT_REASON_MAX; h->exit_reason_name = hvm_svm_exit_reason_name; } else { h->exit_reason_max = HVM_VMX_EXIT_REASON_MAX; h->exit_reason_name = hvm_vmx_exit_reason_name; } if(opt.histogram_interrupt_eip) { int count = ((1ULL<summary.extint_histogram = malloc(size); if(h->summary.extint_histogram) bzero(h->summary.extint_histogram, size); else { fprintf(stderr, "FATAL: Could not allocate %zd bytes for interrupt histogram!\n", size); error(ERR_SYSTEM, NULL); } } for(i=0; isummary.guest_interrupt[i].count=0; } /* PV data */ enum { PV_HYPERCALL=1, PV_TRAP=3, PV_PAGE_FAULT, PV_FORCED_INVALID_OP, PV_EMULATE_PRIVOP, PV_EMULATE_4GB, PV_MATH_STATE_RESTORE, PV_PAGING_FIXUP, PV_GDT_LDT_MAPPING_FAULT, PV_PTWR_EMULATION, PV_PTWR_EMULATION_PAE, PV_HYPERCALL_V2 = 13, PV_HYPERCALL_SUBCALL = 14, PV_MAX }; char *pv_name[PV_MAX] = { [PV_HYPERCALL]="hypercall", [PV_TRAP]="trap", [PV_PAGE_FAULT]="page_fault", [PV_FORCED_INVALID_OP]="forced_invalid_op", [PV_EMULATE_PRIVOP]="emulate privop", [PV_EMULATE_4GB]="emulate 4g", [PV_MATH_STATE_RESTORE]="math state restore", [PV_PAGING_FIXUP]="paging fixup", [PV_GDT_LDT_MAPPING_FAULT]="gdt/ldt mapping fault", [PV_PTWR_EMULATION]="ptwr", [PV_PTWR_EMULATION_PAE]="ptwr(pae)", [PV_HYPERCALL_V2]="hypercall", [PV_HYPERCALL_SUBCALL]="hypercall (subcall)", }; #define PV_HYPERCALL_MAX 56 #define PV_TRAP_MAX 20 struct pv_data { unsigned summary_info:1; int count[PV_MAX]; int hypercall_count[PV_HYPERCALL_MAX]; int trap_count[PV_TRAP_MAX]; }; /* Sched data */ enum { RUNSTATE_RUNNING=0, RUNSTATE_RUNNABLE, RUNSTATE_BLOCKED, RUNSTATE_OFFLINE, RUNSTATE_LOST, RUNSTATE_QUEUED, RUNSTATE_INIT, RUNSTATE_MAX }; int runstate_graph[RUNSTATE_MAX] = { [RUNSTATE_BLOCKED]=0, [RUNSTATE_OFFLINE]=1, [RUNSTATE_RUNNABLE]=2, [RUNSTATE_RUNNING]=3, [RUNSTATE_LOST]=-1, [RUNSTATE_QUEUED]=-2, [RUNSTATE_INIT]=-2, }; char * runstate_name[RUNSTATE_MAX]={ [RUNSTATE_RUNNING]= "running", [RUNSTATE_RUNNABLE]="runnable", [RUNSTATE_BLOCKED]= "blocked", /* to be blocked */ [RUNSTATE_OFFLINE]= "offline", [RUNSTATE_QUEUED]= "queued", [RUNSTATE_INIT]= "init", [RUNSTATE_LOST]= "lost", }; enum { RUNNABLE_STATE_INVALID, RUNNABLE_STATE_WAKE, RUNNABLE_STATE_PREEMPT, RUNNABLE_STATE_OTHER, RUNNABLE_STATE_MAX }; char * runnable_state_name[RUNNABLE_STATE_MAX]={ [RUNNABLE_STATE_INVALID]="invalid", /* Should never show up */ [RUNNABLE_STATE_WAKE]="wake", [RUNNABLE_STATE_PREEMPT]="preempt", [RUNNABLE_STATE_OTHER]="other", }; /* Memory data */ enum { MEM_PAGE_GRANT_MAP = 1, MEM_PAGE_GRANT_UNMAP, MEM_PAGE_GRANT_TRANSFER, MEM_SET_P2M_ENTRY, MEM_DECREASE_RESERVATION, MEM_POD_POPULATE = 16, MEM_POD_ZERO_RECLAIM, MEM_POD_SUPERPAGE_SPLINTER, MEM_MAX }; char *mem_name[MEM_MAX] = { [MEM_PAGE_GRANT_MAP] = "grant-map", [MEM_PAGE_GRANT_UNMAP] = "grant-unmap", [MEM_PAGE_GRANT_TRANSFER] = "grant-transfer", [MEM_SET_P2M_ENTRY] = "set-p2m", [MEM_DECREASE_RESERVATION] = "decrease-reservation", [MEM_POD_POPULATE] = "pod-populate", [MEM_POD_ZERO_RECLAIM] = "pod-zero-reclaim", [MEM_POD_SUPERPAGE_SPLINTER] = "pod-superpage-splinter", }; /* Per-unit information. */ struct cr3_value_struct { struct cr3_value_struct * next; struct cr3_value_struct * gnext; unsigned long long gmfn; int cr3_id; unsigned long long first_time, last_time, run_time; struct cycle_summary total_time, guest_time, hv_time; int switch_count, flush_count; struct hvm_short_summary_struct hvm; struct { int now; int count; } prealloc_unpin; struct { unsigned callback:1; unsigned flush_count, switch_count; unsigned fixup_user, emulate_corr_user; } destroy; }; #ifndef MAX_CPUS #define MAX_CPUS 256 #endif typedef uint32_t cpu_mask_t; #define IDLE_DOMAIN 32767 #define DEFAULT_DOMAIN 32768 #define MAX_VLAPIC_LIST 8 struct vlapic_struct { struct { struct outstanding_ipi { tsc_t first_tsc; int vec, count; int injected, valid; } list[MAX_VLAPIC_LIST]; } outstanding; }; struct vcpu_data { int vid; struct domain_data *d; /* up-pointer */ unsigned activated:1; int guest_paging_levels; /* Schedule info */ struct { int state; int runnable_state; /* Only valid when state==RUNSTATE_RUNNABLE */ tsc_t tsc; /* TSC skew detection/correction */ struct last_oldstate_struct { int wrong, actual, pid; tsc_t tsc; } last_oldstate; /* Performance counters */ unsigned long long p1_start, p2_start; } runstate; struct pcpu_info *p; tsc_t pcpu_tsc; /* Hardware tracking */ struct { long long val; tsc_t start_time; struct cr3_value_struct *data; } cr3; /* IPI latency tracking */ struct vlapic_struct vlapic; /* Summary info */ struct cycle_framework f; struct cycle_summary runstates[RUNSTATE_MAX]; struct cycle_summary runnable_states[RUNNABLE_STATE_MAX]; struct cycle_summary cpu_affinity_all, cpu_affinity_pcpu[MAX_CPUS]; enum { VCPU_DATA_NONE=0, VCPU_DATA_HVM, VCPU_DATA_PV } data_type; union { struct hvm_data hvm; struct pv_data pv; }; }; enum { DOMAIN_RUNSTATE_BLOCKED=0, DOMAIN_RUNSTATE_PARTIAL_RUN, DOMAIN_RUNSTATE_FULL_RUN, DOMAIN_RUNSTATE_PARTIAL_CONTENTION, DOMAIN_RUNSTATE_CONCURRENCY_HAZARD, DOMAIN_RUNSTATE_FULL_CONTENTION, DOMAIN_RUNSTATE_LOST, DOMAIN_RUNSTATE_MAX }; char * domain_runstate_name[] = { [DOMAIN_RUNSTATE_BLOCKED]="blocked", [DOMAIN_RUNSTATE_PARTIAL_RUN]="partial run", [DOMAIN_RUNSTATE_FULL_RUN]="full run", [DOMAIN_RUNSTATE_PARTIAL_CONTENTION]="partial contention", [DOMAIN_RUNSTATE_CONCURRENCY_HAZARD]="concurrency_hazard", [DOMAIN_RUNSTATE_FULL_CONTENTION]="full_contention", [DOMAIN_RUNSTATE_LOST]="lost", }; enum { POD_RECLAIM_CONTEXT_UNKNOWN=0, POD_RECLAIM_CONTEXT_FAULT, POD_RECLAIM_CONTEXT_BALLOON, POD_RECLAIM_CONTEXT_MAX }; char * pod_reclaim_context_name[] = { [POD_RECLAIM_CONTEXT_UNKNOWN]="unknown", [POD_RECLAIM_CONTEXT_FAULT]="fault", [POD_RECLAIM_CONTEXT_BALLOON]="balloon", }; #define POD_ORDER_MAX 4 struct domain_data { struct domain_data *next; int did; struct vcpu_data *vcpu[MAX_CPUS]; int max_vid; int runstate; tsc_t runstate_tsc; struct cycle_summary total_time; struct cycle_summary runstates[DOMAIN_RUNSTATE_MAX]; struct cr3_value_struct *cr3_value_head; struct eip_list_struct *emulate_eip_list; struct eip_list_struct *interrupt_eip_list; int guest_interrupt[GUEST_INTERRUPT_MAX+1]; struct hvm_short_summary_struct hvm_short; struct { int done[MEM_MAX]; int done_interval[MEM_MAX]; int done_for[MEM_MAX]; int done_for_interval[MEM_MAX]; } memops; struct { int reclaim_order[POD_ORDER_MAX]; int reclaim_context[POD_RECLAIM_CONTEXT_MAX]; int reclaim_context_order[POD_RECLAIM_CONTEXT_MAX][POD_ORDER_MAX]; /* FIXME: Do a full cycle summary */ int populate_order[POD_ORDER_MAX]; } pod; }; struct domain_data * domain_list=NULL; struct domain_data default_domain; enum { TOPLEVEL_GEN=0, TOPLEVEL_SCHED, TOPLEVEL_DOM0OP, TOPLEVEL_HVM, TOPLEVEL_MEM, TOPLEVEL_PV, TOPLEVEL_SHADOW, TOPLEVEL_HW, TOPLEVEL_MAX=TOPLEVEL_HW+1, }; char * toplevel_name[TOPLEVEL_MAX] = { [TOPLEVEL_GEN]="gen", [TOPLEVEL_SCHED]="sched", [TOPLEVEL_DOM0OP]="dom0op", [TOPLEVEL_HVM]="hvm", [TOPLEVEL_MEM]="mem", [TOPLEVEL_PV]="pv", [TOPLEVEL_SHADOW]="shadow", [TOPLEVEL_HW]="hw", }; struct trace_volume { unsigned long long toplevel[TOPLEVEL_MAX]; unsigned long long sched_verbose; unsigned long long hvm[HVM_VOL_MAX]; } volume; #define UPDATE_VOLUME(_p,_x,_s) \ do { \ (_p)->volume.total._x += _s; \ (_p)->volume.last_buffer._x += _s; \ } while(0) void volume_clear(struct trace_volume *vol) { bzero(vol, sizeof(*vol)); } void volume_summary(struct trace_volume *vol) { int j, k; for(j=0; jtoplevel[j]) { printf(" %-6s: %10lld\n", toplevel_name[j], vol->toplevel[j]); switch(j) { case TOPLEVEL_SCHED: if(vol->sched_verbose) printf(" +-verbose: %10lld\n", vol->sched_verbose); break; case TOPLEVEL_HVM: for(k=0; khvm[k]) printf(" +-%-7s: %10lld\n", hvm_vol_name[k], vol->hvm[k]); } break; } } } struct pcpu_info { /* Information about this pcpu */ unsigned active:1, summary:1; int pid; /* Information related to scanning thru the file */ tsc_t first_tsc, last_tsc, order_tsc; off_t file_offset; off_t next_cpu_change_offset; struct record_info ri; int last_cpu_change_pid; int power_state; /* Information related to tsc skew detection / correction */ struct { tsc_t offset; cpu_mask_t downstream; /* To detect cycles in dependencies */ } tsc_skew; /* Information related to domain tracking */ struct vcpu_data * current; struct { unsigned active:1, domain_valid:1, seen_valid_schedule:1; /* Seen an actual schedule since lost records */ unsigned did:16,vid:16; tsc_t tsc; } lost_record; /* Record volume */ struct { tsc_t buffer_first_tsc, buffer_dom0_runstate_tsc, buffer_dom0_runstate_cycles[RUNSTATE_MAX]; int buffer_dom0_runstate; unsigned buffer_size; struct trace_volume total, last_buffer; } volume; /* Time report */ struct { tsc_t tsc; struct cycle_summary idle, running, lost; } time; }; void __fill_in_record_info(struct pcpu_info *p); #define INTERVAL_DOMAIN_GUEST_INTERRUPT_MAX 10 struct { int max_active_pcpu; off_t last_epoch_offset; int early_eof; int lost_cpus; tsc_t now; struct cycle_framework f; tsc_t buffer_trace_virq_tsc; struct pcpu_info pcpu[MAX_CPUS]; struct { int id; /* Invariant: head null => tail null; head !null => tail valid */ struct cr3_value_struct *head, **tail; } cr3; struct { tsc_t start_tsc; /* Information about specific interval output types */ union { struct { struct interval_element ** values; int count; } array; struct { struct interval_list *head, **tail; } list; struct cr3_value_struct *cr3; struct { struct domain_data *d; int guest_vector[INTERVAL_DOMAIN_GUEST_INTERRUPT_MAX]; } domain; }; } interval; } P = { 0 }; /* Function prototypes */ char * pcpu_string(int pcpu); void pcpu_string_draw(struct pcpu_info *p); void process_generic(struct record_info *ri); void dump_generic(FILE *f, struct record_info *ri); ssize_t __read_record(struct trace_record *rec, off_t offset); void error(enum error_level l, struct record_info *ri); void update_io_address(struct io_address ** list, unsigned int pa, int dir, tsc_t arc_cycles, unsigned int va); int check_extra_words(struct record_info *ri, int expected_size, const char *record); int vcpu_set_data_type(struct vcpu_data *v, int type); void cpumask_init(cpu_mask_t *c) { *c = 0UL; } void cpumask_clear(cpu_mask_t *c, int cpu) { *c &= ~(1UL << cpu); } void cpumask_set(cpu_mask_t *c, int cpu) { *c |= (1UL << cpu); } int cpumask_isset(const cpu_mask_t *c, int cpu) { if(*c & (1UL<time = ((c - P.f.first_tsc) << 10) / opt.cpu_qhz; t->s = t->time / 1000000000; t->ns = t->time - (t->s * 1000000000); } void abs_cycles_to_time(unsigned long long ac, struct time_struct *t) { if(ac > P.f.first_tsc) { /* t->time = ((ac - P.f.first_tsc) * 1000) / (opt.cpu_hz / 1000000 ); */ /* t->s = t->time / 1000000000; */ /* t->ns = t->time % 1000000000; */ t->time = ((ac - P.f.first_tsc) << 10) / opt.cpu_qhz; t->s = t->time / 1000000000; t->ns = t->time - (t->s * 1000000000); } else { t->time = t->s = t->ns = 0; } } tsc_t abs_cycles_to_global(unsigned long long ac) { if(ac > P.f.first_tsc) return ac - P.f.first_tsc; else return 0; } void scatterplot_vs_time(tsc_t atsc, long long y) { struct time_struct t; abs_cycles_to_time(atsc, &t); printf("%u.%09u %lld\n", t.s, t.ns, y); } /* -- Summary Code -- */ /* With compliments to "Numerical Recipes in C", which provided the algorithm * and basic template for this function. */ long long percentile(long long * A, int N, int ple) { int I, J, L, R, K; long long X, W; /* No samples! */ if ( N == 0 ) return 0; /* Find K, the element # we want */ K=N*ple/100; /* Set the left and right boundaries of the current search space */ L=0; R=N-1; while(L < R) { /* X: The value to order everything higher / lower than */ X=A[K]; /* Starting at the left and the right... */ I=L; J=R; do { /* Find the first element on the left that is out-of-order w/ X */ while(A[I]> 1; /* X: The value to order everything higher / lower than */ X=A[K]; /* Starting at the left and the right... */ I=L; I_weight = L_weight; J=R; J_weight = R_weight; do { /* Find the first element on the left that is out-of-order w/ X */ while(A[I]> 1; /* X: The value to order everything higher / lower than */ X=A[K]; /* Starting at the left and the right... */ I=L; I_weight = L_weight; J=R; J_weight = R_weight; do { /* Find the first element on the left that is out-of-order w/ X */ while(A[I]cycles, f->total_cycles); } static inline double summary_percent_global(struct cycle_summary *s) { return __summary_percent(s, &P.f); } static inline void update_cycles(struct cycle_summary *s, long long c) { s->event_count++; if (!c) return; if(opt.sample_size) { if (s->count >= s->sample_size && (s->count == 0 || opt.sample_max == 0 || s->sample_size < opt.sample_max)) { int new_size; void * new_sample = NULL; new_size = s->sample_size << 1; if (new_size == 0) new_size = opt.sample_size; if (opt.sample_max != 0 && new_size > opt.sample_max) new_size = opt.sample_max; new_sample = realloc(s->sample, sizeof(*s->sample) * new_size); if (new_sample) { s->sample = new_sample; s->sample_size = new_size; } } if (s->count < s->sample_size) { s->sample[s->count]=c; } else { /* * If we run out of space for samples, start taking only a * subset of samples. */ int lap, index; lap = (s->count/s->sample_size)+1; index =s->count % s->sample_size; if((index - (lap/3))%lap == 0) { s->sample[index]=c; } } } s->count++; s->cycles += c; s->interval.count++; s->interval.cycles += c; } static inline void clear_interval_cycles(struct interval_element *e) { e->cycles = 0; e->count = 0; e->instructions = 0; } static inline void print_cpu_affinity(struct cycle_summary *s, char *p) { if(s->count) { long long avg; avg = s->cycles / s->count; if ( opt.sample_size ) { long long p5, p50, p95; int data_size = s->count; if(data_size > s->sample_size) data_size = s->sample_size; p50 = percentile(s->sample, data_size, 50); p5 = percentile(s->sample, data_size, 5); p95 = percentile(s->sample, data_size, 95); printf("%s: %7d %6lld {%6lld|%6lld|%6lld}\n", p, s->count, avg, p5, p50, p95); } else { printf("%s: %7d %6lld\n", p, s->count, avg); } } } static inline void print_cycle_percent_summary(struct cycle_summary *s, tsc_t total, char *p) { if(s->count) { long long avg; double percent, seconds; avg = s->cycles / s->count; seconds = ((double)s->cycles) / opt.cpu_hz; percent = ((double)(s->cycles * 100)) / total; if ( opt.sample_size ) { long long p5, p50, p95; int data_size = s->count; if(data_size > s->sample_size) data_size = s->sample_size; p50 = self_weighted_percentile(s->sample, data_size, 50); p5 = self_weighted_percentile(s->sample, data_size, 5); p95 = self_weighted_percentile(s->sample, data_size, 95); printf("%s: %7d %5.2lfs %5.2lf%% %6lld {%6lld|%6lld|%6lld}\n", p, s->count, seconds, percent, avg, p5, p50, p95); } else { printf("%s: %7d %5.2lfs %5.2lf%% %6lld\n", p, s->count, seconds, percent, avg); } } } static inline void print_cycle_summary(struct cycle_summary *s, char *p) { if(s->count) { long long avg; avg = s->cycles / s->count; if ( opt.sample_size ) { long long p5, p50, p95; int data_size = s->count; if(data_size > s->sample_size) data_size = s->sample_size; p50 = self_weighted_percentile(s->sample, data_size, 50); p5 = self_weighted_percentile(s->sample, data_size, 5); p95 = self_weighted_percentile(s->sample, data_size, 95); printf("%s: %7d %5.2lfs %6lld {%6lld|%6lld|%6lld}\n", p, s->count, ((double)s->cycles)/opt.cpu_hz, avg, p5, p50, p95); } else { printf("%s: %7d %5.2lfs %6lld\n", p, s->count, ((double)s->cycles)/opt.cpu_hz, avg); } } } #define PRINT_SUMMARY(_s, _p...) \ do { \ if((_s).event_count) { \ if ( opt.sample_size ) { \ unsigned long long p5, p50, p95; \ int data_size=(_s).count; \ if(data_size > (_s).sample_size) \ data_size=(_s).sample_size; \ p50=percentile((_s).sample, data_size, 50); \ p5=percentile((_s).sample, data_size, 5); \ p95=percentile((_s).sample, data_size, 95); \ printf(_p); \ printf(" %7d %5.2lfs %5.2lf%% %5lld cyc {%5lld|%5lld|%5lld}\n", \ (_s).event_count, \ ((double)(_s).cycles)/opt.cpu_hz, \ summary_percent_global(&(_s)), \ (_s).count ? (_s).cycles / (_s).count:0, \ p5, p50, p95); \ } else { \ printf(_p); \ printf(" %7d %5.2lfs %5.2lf%% %5lld cyc\n", \ (_s).event_count, \ ((double)(_s).cycles)/opt.cpu_hz, \ summary_percent_global(&(_s)), \ (_s).count ? (_s).cycles / (_s).count:0); \ } \ } \ } while(0) #define INTERVAL_DESC_MAX 31 struct interval_list { struct interval_element *elem; struct interval_list *next; char desc[INTERVAL_DESC_MAX+1]; /* +1 for the null terminator */ }; void __interval_cycle_percent_output(struct interval_element *e, tsc_t cycles) { printf(" %.02lf", __cycles_percent(e->cycles, cycles)); clear_interval_cycles(e); } void interval_cycle_percent_output(struct interval_element *e) { __interval_cycle_percent_output(e, opt.interval.cycles); } void interval_time_output(void) { struct time_struct t; abs_cycles_to_time(P.interval.start_tsc, &t); printf("%u.%09u", t.s, t.ns); } void interval_table_output(void) { int i; interval_time_output(); if(opt.interval.mode == INTERVAL_MODE_ARRAY) { for(i=0; inext) interval_cycle_percent_output(p->elem); } printf("\n"); } void interval_table_tail(void) { struct interval_list *p; printf("time"); for(p=P.interval.list.head; p; p = p->next) printf(" %s", p->desc); printf("\n"); } void interval_table_alloc(int count) { P.interval.array.count = count; P.interval.array.values = malloc(count * sizeof(struct interval_list *)); if(!P.interval.array.values) { fprintf(stderr, "Malloc failed!\n"); error(ERR_SYSTEM, NULL); } bzero(P.interval.array.values, count*sizeof(struct interval_list *)); } void interval_list_add(struct interval_element *e, char *desc) { struct interval_list *p; fprintf(warn, "%s: Adding element '%s'\n", __func__, desc); if((p=malloc(sizeof(*p)))==NULL) { fprintf(stderr, "malloc() failed.\n"); error(ERR_SYSTEM, NULL); } bzero(p, sizeof(*p)); p->elem = e; strncpy(p->desc, desc, INTERVAL_DESC_MAX); p->next=NULL; if(P.interval.list.head) *P.interval.list.tail = p; else P.interval.list.head = p; P.interval.list.tail = &p->next; } void interval_cr3_schedule_time_header(void) { if( opt.interval.mode == INTERVAL_MODE_ARRAY ) { int i; printf("time"); for(i=0; igmfn == opt.interval.array.values[i]) { if(P.interval.array.values[i]) { fprintf(stderr, "Fatal: duplicate cr3 value %llx!\n", cr3->gmfn); error(ERR_ASSERT, NULL); } fprintf(stderr, "%s: found gmfn %llx\n", __func__, cr3->gmfn); P.interval.array.values[i] = &cr3->total_time.interval; } } } else if(opt.interval.mode == INTERVAL_MODE_LIST) { char desc[32]; snprintf(desc, 32, "%llx", cr3->gmfn); interval_list_add(&cr3->total_time.interval, desc); } else { /* Custom */ if(cr3->gmfn == opt.interval.array.values[0]) P.interval.cr3 = cr3; } } int cr3_time_compare(const void *_a, const void *_b) { struct cr3_value_struct *a=*(typeof(&a))_a; struct cr3_value_struct *b=*(typeof(&a))_b; if(a->total_time.interval.cycles < b->total_time.interval.cycles) return 1; else if(b->total_time.interval.cycles == a->total_time.interval.cycles) { if(a->total_time.interval.count < b->total_time.interval.count) return 1; else if(a->total_time.interval.count == b->total_time.interval.count) return 0; else return -1; } else return -1; } void interval_cr3_schedule_ordered_output(void) { struct cr3_value_struct *p; int i; struct cr3_value_struct **qsort_array; int N=0; for(p=P.cr3.head; p; p=p->gnext) N++; if(!N) return; qsort_array = malloc(N * sizeof(struct eip_list_struct *)); for(i=0, p=P.cr3.head; p; p=p->gnext, i++) qsort_array[i]=p; qsort(qsort_array, N, sizeof(struct eip_list_struct *), cr3_time_compare); interval_time_output(); for(i=0; itotal_time.interval.cycles > 0) { printf(" %8llx: %.02lf %c\n", p->gmfn, __cycles_percent(p->total_time.interval.cycles, opt.interval.cycles), (p->first_time > P.interval.start_tsc)?'*':' '); } clear_interval_cycles(&p->total_time.interval); } free(qsort_array); } void interval_cr3_short_summary_header(void) { int i; printf("time guest"); for(i=0; ihvm.s; printf(" %.02lf", __cycles_percent(p->total_time.interval.cycles, opt.interval.cycles)); for(i=0; itotal_time.interval.cycles); clear_interval_cycles(&p->total_time.interval); printf("\n"); } } void interval_domain_value_check(struct domain_data *d) { if( opt.interval.mode == INTERVAL_MODE_ARRAY ) { int i; for(i=0; idid == opt.interval.array.values[i]) { if(P.interval.array.values[i]) { fprintf(stderr, "Fatal: duplicate domain value %d!\n", d->did); error(ERR_ASSERT, NULL); } P.interval.array.values[i] = &d->total_time.interval; } } } else if(opt.interval.mode == INTERVAL_MODE_LIST) { char desc[32]; snprintf(desc, 32, "%d", d->did); interval_list_add(&d->total_time.interval, desc); } else { if(d->did == opt.interval.array.values[0]) P.interval.domain.d = d; } } void interval_domain_short_summary_header(void) { int i; printf("time running"); for(i=0; itotal_time.interval); for(i=0; ihvm_short.s[i].interval); printf("\n"); } } void interval_domain_guest_interrupt(struct hvm_data *h, int vector) { struct domain_data *d = h->v->d; int i; /* Check to see if this vector is in the "print list" */ for(i=0; iguest_interrupt[vector]++; } } void interval_domain_guest_interrupt_tail(void) { int i; printf("time running"); for(i=0; iguest_interrupt[v]); d->guest_interrupt[v]=0; } printf("\n"); } } void interval_domain_grant_maps_output(void) { if(P.interval.domain.d) { struct domain_data *d; d=P.interval.domain.d; interval_time_output(); printf(" %d", d->memops.done_for_interval[MEM_PAGE_GRANT_MAP]); d->memops.done_for_interval[MEM_PAGE_GRANT_MAP] = 0; printf("\n"); } } /* General interval gateways */ void interval_callback(void) { /* First, see if we're in generic mode. */ switch(opt.interval.mode) { case INTERVAL_MODE_LIST: case INTERVAL_MODE_ARRAY: interval_table_output(); return; default: break; } switch(opt.interval.output) { case INTERVAL_CR3_SCHEDULE_ORDERED: interval_cr3_schedule_ordered_output(); break; case INTERVAL_CR3_SHORT_SUMMARY: interval_cr3_short_summary_output(); break; case INTERVAL_DOMAIN_SHORT_SUMMARY: interval_domain_short_summary_output(); break; case INTERVAL_DOMAIN_GUEST_INTERRUPT: interval_domain_guest_interrupt_output(); break; case INTERVAL_DOMAIN_GRANT_MAPS: interval_domain_grant_maps_output(); break; default: break; } } void interval_header(void) { switch(opt.interval.output) { case INTERVAL_CR3_SHORT_SUMMARY: interval_cr3_short_summary_header(); break; case INTERVAL_DOMAIN_SHORT_SUMMARY: interval_domain_short_summary_header(); break; default: break; } } void interval_tail(void) { if(opt.interval.mode == INTERVAL_MODE_LIST) { interval_table_tail(); return; } switch(opt.interval.output) { case INTERVAL_DOMAIN_GUEST_INTERRUPT: interval_domain_guest_interrupt_tail(); break; default: break; } } /* -- Eip list data -- */ void update_eip(struct eip_list_struct **head, unsigned long long eip, unsigned long long cycles, int type, void * extra) { struct eip_list_struct *p, **last=head; for(p=*head; p; last = (&p->next), p=p->next) if(p->eip >= eip) break; if(!p || p->eip != eip) { p=malloc(sizeof(*p)); if(!p) { perror("malloc failed"); error(ERR_SYSTEM, NULL); } bzero(p, sizeof(*p)); p->eip=eip; p->type = type; if(eip_list_type[type].new) { eip_list_type[type].new(p, extra); } p->next = *last; *last=p; } else if(p->type != type) { fprintf(stderr, "WARNING, mixed types! %d %d\n", p->type, type); } else if(eip_list_type[type].update) { eip_list_type[type].update(p, extra); } update_cycles(&p->summary, cycles); } int eip_compare(const void *_a, const void *_b) { struct eip_list_struct *a=*(typeof(&a))_a; struct eip_list_struct *b=*(typeof(&a))_b; if(a->summary.cycles < b->summary.cycles) return 1; else if(b->summary.cycles == a->summary.cycles) { if(a->summary.count < b->summary.count) return 1; else if(a->summary.count == b->summary.count) return 0; else return -1; } else return -1; } void dump_eip(struct eip_list_struct *head) { struct eip_list_struct *p; int i; int total = 0; struct eip_list_struct **qsort_array; int N=0; for(p=head; p; p=p->next) { total += p->summary.count; N++; } if(!N) return; qsort_array = malloc(N * sizeof(struct eip_list_struct *)); for(i=0, p=head; p; p=p->next, i++) qsort_array[i]=p; qsort(qsort_array, N, sizeof(struct eip_list_struct *), eip_compare); /* WARNING: don't use N after this point unless you copy this variable */ #if 0 if(opt.summary_eip_limit && opt.summary_eip_limit < N) N=opt.summary_eip_limit; #endif printf(" Total samples: %d\n", total); for(i=0; isummary.cycles ) PRINT_SUMMARY(p->summary, " %12llx%-45s: ", p->eip, find_symbol(p->eip)); else { printf(" %12llx%-45s: ", p->eip, find_symbol(p->eip)); printf(" %7d %5.2lf%%\n", p->summary.count, ((double)p->summary.count*100)/total); } if(eip_list_type[p->type].dump) { eip_list_type[p->type].dump(p); } } free(qsort_array); } /* -- HVM code -- */ struct hvm_pf_xen_record { //unsigned vcpu:16, domain:16; union { struct { unsigned long long va; unsigned int error_code; } x64; struct { unsigned int va; unsigned int error_code; } x32; }; }; void hvm_update_short_summary(struct hvm_data *h, int element) { struct vcpu_data *v = h->v; if(v->cr3.data) update_cycles(&v->cr3.data->hvm.s[element], h->arc_cycles); update_cycles(&v->d->hvm_short.s[element], h->arc_cycles); h->short_summary_done=1; } void hvm_short_summary(struct hvm_short_summary_struct *hss, tsc_t total, char *prefix) { char desc[80]; int i; for(i=0; is + i, total, desc); } } /* Wrapper to try to make sure this is only called once per * call site, rather than walking through the list each time */ #define hvm_set_summary_handler(_h, _s, _d) \ do { \ static int done=0; \ int ret; \ if(!done) { \ if ((ret=__hvm_set_summary_handler(_h, _s, _d))) \ fprintf(stderr, "%s: hvm_set_summary_handler returned %d\n", \ __func__, ret); \ done=1; \ } \ } while(0) int __hvm_set_summary_handler(struct hvm_data *h, void (*s)(struct hvm_data *h, void*d), void*d) { /* Set summary handler */ if(h->exit_reason < h->exit_reason_max) { struct hvm_summary_handler_node *p, **q; /* Find the end of the list, checking to make sure there are no * duplicates along the way */ q=&h->exit_reason_summary_handler_list[h->exit_reason]; p = *q; while(p) { if(p->handler == s && p->data == d) { fprintf(stderr, "%s: Unexpected duplicate handler %p,%p\n", __func__, s, d); error(ERR_STRICT, NULL); return -EBUSY; } q=&p->next; p=*q; } assert(p==NULL); /* Insert the new handler */ p=malloc(sizeof(*p)); if (!p) { fprintf(stderr, "%s: Malloc failed!\n", __func__); error(ERR_SYSTEM, NULL); } p->handler=s; p->data = d; p->next=*q; *q=p; return 0; } return -EINVAL; } void hvm_generic_postprocess(struct hvm_data *h); static int hvm_set_postprocess(struct hvm_data *h, void (*s)(struct hvm_data *h)) { if ( h->post_process == NULL || h->post_process == hvm_generic_postprocess ) { h->post_process = s; return 0; } else return 1; } #define SIGN_EXTENDED_BITS (~((1ULL<<48)-1)) #define HIGH_BIT(_v) ((_v) & (1ULL<<47)) static inline int is_valid_addr64(unsigned long long va) { if(HIGH_BIT(va)) return ((va & SIGN_EXTENDED_BITS) == SIGN_EXTENDED_BITS); else return ((va & SIGN_EXTENDED_BITS) == 0); } void hvm_pf_xen_summary(struct hvm_data *h, void *d) { int i,j, k; printf(" page_fault\n"); for(i=0; isummary.pf_xen[i], " %-25s ", pf_xen_name[i]); } else { PRINT_SUMMARY(h->summary.pf_xen[i], " [%23d] ", i); } switch(i){ case PF_XEN_NON_EMULATE: for(j=0; jsummary.pf_xen_non_emul[j], " *%-13s ", pf_xen_non_emul_name[j]); break; case PF_XEN_EMULATE: for(j=0; jsummary.pf_xen_emul[j], " *%-13s ", pf_xen_emul_name[j]); if(j == PF_XEN_EMUL_EARLY_UNSHADOW) { int k; for(k=0; k<5; k++) { PRINT_SUMMARY(h->summary.pf_xen_emul_early_unshadow[k], " +[%d] ", k); } } } break; case PF_XEN_FIXUP: for(j=0; jsummary.pf_xen_fixup[j], " *%-13s ", pf_xen_fixup_name[j]); if(j == PF_XEN_FIXUP_UNSYNC ) { for(k=0; ksummary.pf_xen_fixup_unsync_resync[k], " +[%3d] ", k); } PRINT_SUMMARY(h->summary.pf_xen_fixup_unsync_resync[k], " +[max] "); } } break; } } } void pf_preprocess(struct pf_xen_extra *e, int guest_paging_levels) { switch(guest_paging_levels) { /* Select a subfield of _bits bits starting at bit _shift from _x */ #define _SUBFIELD(_bits, _shift, _x) \ (((_x)>>(_shift)) & ((1ULL<<(_bits))-1)) case 4: /* Verify sign-extension */ if((HIGH_BIT(e->va) &&((e->va & SIGN_EXTENDED_BITS) != SIGN_EXTENDED_BITS)) || (!HIGH_BIT(e->va) && ((e->va & SIGN_EXTENDED_BITS) != 0))) { fprintf(warn, "Strange, va %llx not properly sign extended for 4-level pagetables\n", e->va); } e->pt_index[4]=_SUBFIELD(9,39,e->va); e->pt_index[3]=_SUBFIELD(9,30,e->va); e->pt_index[2]=_SUBFIELD(9,21,e->va); e->pt_index[1]=_SUBFIELD(9,12,e->va); /* These are only useful for the linear-pagetable code */ e->pt_index[0]=_SUBFIELD(9,3,e->va); if(e->va & 0x4) e->pt_is_lo=0; break; case 3: e->pt_index[3]=_SUBFIELD(2,30,e->va); e->pt_index[2]=_SUBFIELD(9,21,e->va); e->pt_index[1]=_SUBFIELD(9,12,e->va); /* These are only useful for the linear-pagetable code */ e->pt_index[0]=_SUBFIELD(9,3,e->va); if(e->va & 0x4) e->pt_is_lo=0; break; case 2: e->pt_index[2]=_SUBFIELD(10,22,e->va); e->pt_index[1]=_SUBFIELD(10,12,e->va); /* This is only useful for the linear pagetable code */ e->pt_index[0]=_SUBFIELD(10,2,e->va); break; case 0: break; default: fprintf(warn, "Don't know how to handle %d-level pagetables\n", guest_paging_levels); } e->corresponding_va = CORR_VA_INVALID; e->pt_level = 0; /* Detect accesses to Windows linear pagetables */ switch(guest_paging_levels) { case 2: if(e->pt_index[2] == 768) { if(e->pt_index[1] == 768) { e->pt_level = 2; e->corresponding_va=((1UL<<22)-1) | e->pt_index[0]<<22; } else { e->pt_level = 1; e->corresponding_va = ((1UL<<12)-1) | e->pt_index[1]<<22 | e->pt_index[0]<<12; } } break; case 3: if(e->pt_index[3]==3 && (e->pt_index[2]>>2==0)) { if(e->pt_index[2]==3 && e->pt_index[1]>>2==0) { if(e->pt_index[1] == 3 && e->pt_index[0]>>2==0) { e->pt_level = 3; e->corresponding_va=((1UL<<30)-1) | e->pt_index[0]<<30; } else { e->pt_level = 2; e->corresponding_va=((1UL<<21)-1) | e->pt_index[1]<<30 | e->pt_index[2]<<21; } } else { e->pt_level = 1; e->corresponding_va = ((1UL<<12)-1) | e->pt_index[0]<<12 | e->pt_index[1]<<21 | e->pt_index[2]<<30; } } break; case 4: if(e->pt_index[4] == 0x1ed) { if(e->pt_index[3] == 0x1ed) { if(e->pt_index[2] == 0x1ed) { if(e->pt_index[1] == 0x1ed) { e->pt_level = 4; e->corresponding_va = ((1ULL<<39)-1) | (unsigned long long)e->pt_index[0]<<39; } else { e->pt_level = 3; e->corresponding_va = ((1ULL<<30)-1) | (unsigned long long)e->pt_index[0]<<30 | (unsigned long long)e->pt_index[1]<<39; } } else { e->pt_level = 2; e->corresponding_va = ((1ULL<<21)-1) | (unsigned long long)e->pt_index[0]<<21 | (unsigned long long)e->pt_index[1]<<30 | (unsigned long long)e->pt_index[2]<<39; } } else { e->pt_level = 1; e->corresponding_va = ((1ULL<<12)-1) | (unsigned long long)e->pt_index[0]<<12 | (unsigned long long)e->pt_index[1]<<21 | (unsigned long long)e->pt_index[2]<<30 | (unsigned long long)e->pt_index[3]<<39; } if(HIGH_BIT(e->corresponding_va)) e->corresponding_va |= SIGN_EXTENDED_BITS; } break; default: break; } } void hvm_pf_xen_preprocess(unsigned event, struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; struct mmio_info *m = &h->inflight.mmio; struct hvm_pf_xen_record *r = (typeof(r))h->d; if(event == TRC_HVM_PF_XEN64) { if(!is_valid_addr64(r->x64.va)) fprintf(warn, "%s: invalid va %llx", __func__, r->x64.va); e->va = r->x64.va; e->error_code = r->x64.error_code; } else { e->va = r->x32.va; e->error_code = r->x32.error_code; } if(m->data_valid) e->pf_case = PF_XEN_MMIO; else { pf_preprocess(e, h->v->guest_paging_levels); /* On rio traces, we try to infer emulation by looking for accesses in the linear pagetable */ if(e->pt_level > 0) e->pf_case = PF_XEN_EMULATE; else e->pf_case = PF_XEN_NON_EMULATE; } } static inline int is_kernel(int paging_levels, unsigned long long va) { switch(paging_levels) { case 2: case 3: if(va & 0x80000000) return 1; else return 0; break; case 4: if(HIGH_BIT(va)) return 1; else return 0; default: return 0; } } void hvm_pf_xen_postprocess(struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; if(opt.summary_info) { if(e->pf_case) update_cycles(&h->summary.pf_xen[e->pf_case], h->arc_cycles); else fprintf(warn, "Strange, pf_case 0!\n"); switch(e->pf_case) { case PF_XEN_EMULATE: update_eip(&h->v->d->emulate_eip_list, h->rip, h->arc_cycles, 0, NULL); break; case PF_XEN_NON_EMULATE: if(is_kernel(h->v->guest_paging_levels, h->rip)) update_cycles(&h->summary.pf_xen_non_emul[PF_XEN_NON_EMUL_EIP_KERNEL], h->arc_cycles); else update_cycles(&h->summary.pf_xen_non_emul[PF_XEN_NON_EMUL_EIP_USER], h->arc_cycles); if(is_kernel(h->v->guest_paging_levels, e->va)) update_cycles(&h->summary.pf_xen_non_emul[PF_XEN_NON_EMUL_VA_KERNEL], h->arc_cycles); else update_cycles(&h->summary.pf_xen_non_emul[PF_XEN_NON_EMUL_VA_USER], h->arc_cycles); } /* Set summary handler */ hvm_set_summary_handler(h, hvm_pf_xen_summary, NULL); } } void hvm_pf_xen_process(struct record_info *ri, struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; if(ri->event == TRC_HVM_PF_XEN64 && h->v->guest_paging_levels != 4) fprintf(warn, "Strange, PF_XEN64 but guest_paging_levels %d!\n", h->v->guest_paging_levels); else if(ri->event == TRC_HVM_PF_XEN && h->v->guest_paging_levels == 4) fprintf(warn, "Strange, PF_XEN but guest_paging_levels %d!\n", h->v->guest_paging_levels); hvm_pf_xen_preprocess(ri->event, h); if(opt.dump_all) { if(e->pf_case == PF_XEN_EMULATE) printf("]%s pf_xen:emulate va %llx ec %x level %d corr %llx e->pt_index[%d %d %d %d %d]\n", ri->dump_header, e->va, e->error_code, e->pt_level, e->corresponding_va, e->pt_index[0], e->pt_index[1], e->pt_index[2], e->pt_index[3], e->pt_index[4]); else printf("]%s pf_xen va %llx ec %x e->pt_index[%d %d %d %d %d]\n", ri->dump_header, e->va, e->error_code, e->pt_index[0], e->pt_index[1], e->pt_index[2], e->pt_index[3], e->pt_index[4]); } if ( hvm_set_postprocess(h, hvm_pf_xen_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } char * hvm_vlapic_icr_dest_shorthand_name[4] = { "dest_field", "self", "all-inc", "all-exc" }; void hvm_vlapic_vmentry_cleanup(struct vcpu_data *v, tsc_t tsc) { int i; struct vlapic_struct *vla = &v->vlapic; for(i=0; ioutstanding.list + i; if(!(o->valid && o->injected)) continue; if(tsc >= o->first_tsc) lat = tsc - o->first_tsc; else fprintf(warn, "Strange, vec %d first_tsc %lld > ri->tsc %lld!\n", o->vec, o->first_tsc, tsc); if(opt.dump_ipi_latency || (opt.dump_all && o->count > 1)) { struct time_struct t; cycles_to_time(lat, &t); printf(" [vla] d%dv%d vec %d ipis %d, latency %lld (%u.%09u s)\n", v->d->did, v->vid, o->vec, o->count, lat, t.s, t.ns); } #if 0 /* FIXME: make general somehow */ if(opt.summary_info) { update_cycles(&h->summary.ipi_latency, lat); h->summary.ipi_count[vla->outstanding_ipis]++; } #endif o->vec = o->count = o->injected = o->valid = o->first_tsc = 0; } } void hvm_vlapic_clear(struct vlapic_struct *vla) { bzero(vla, sizeof(*vla)); } struct outstanding_ipi *find_vec(struct vlapic_struct *vla, int vec) { struct outstanding_ipi *o = NULL; int i; /* Find the entry for this vector, or the first empty one. */ for(i=0; ioutstanding.list[i].valid && vla->outstanding.list[i].vec == vec) { o = vla->outstanding.list + i; break; } else if(!vla->outstanding.list[i].valid && !o) o = vla->outstanding.list + i; } if(o && !o->valid) { o->vec = vec; o->valid = 1; } return o; } void ipi_send(struct vcpu_data *ov, int vec) { struct vlapic_struct *vla; struct outstanding_ipi *o = NULL; if(ov->runstate.state == RUNSTATE_LOST) { if(opt.dump_all) fprintf(warn, "%s: v%d in state RUNSTATE_LOST, not counting ipi\n", __func__, ov->vid); return; } vla = &ov->vlapic; o = find_vec(vla, vec); if(!o) { fprintf(warn, "%s: Couldn't find an open slot!\n", __func__); return; } if(!o->first_tsc) o->first_tsc = P.now; if(opt.dump_all && o->count == 0 && o->injected) printf(" [vla] Pre-injection\n"); o->count++; if((opt.dump_all) #if 0 && (ov->runstate.state != RUNSTATE_RUNNING || ov->hvm.vmexit_valid) #endif ) printf(" [vla] d%dv%d vec %d state %s (outstanding ipis %d)\n", ov->d->did, ov->vid, o->vec, runstate_name[ov->runstate.state], o->count); } void hvm_vlapic_icr_handler(struct hvm_data *h) { struct mmio_info *m = &h->inflight.mmio; union { unsigned int val; struct { unsigned vec:8, delivery_mode:3, dest_mode:1, delivery_status:1, _res1:1, level:1, trigger:1, _res2:2, dest_shorthand:2; }; } icr = { .val = m->data }; if(m->is_write) { if(opt.dump_all) { printf(" [vla] d%dv%d icr vec %d %s\n", h->v->d->did, h->v->vid, icr.vec, hvm_vlapic_icr_dest_shorthand_name[icr.dest_shorthand]); } if(icr.dest_shorthand == 3) { struct vcpu_data *ov, *v = h->v; struct domain_data *d = v->d; int i; for(i=0; ivcpu[i]; if(!ov || ov == v) continue; ipi_send(ov, icr.vec); } } else if(icr.dest_shorthand != 1) { #if 0 fprintf(warn, "Strange, vlapic icr %s vec %d!\n", hvm_vlapic_icr_dest_shorthand_name[icr.dest_shorthand], icr.vec); #endif } } else { /* Read */ if(opt.dump_all) { printf(" [vla] d%dv%d icr status %s\n", h->v->d->did, h->v->vid, icr.delivery_status?"pending":"idle"); } } } void hvm_vlapic_inject(struct vcpu_data *v, int vec) { struct vlapic_struct *vla = &v->vlapic; struct outstanding_ipi *o = NULL; o = find_vec(vla, vec); if(o) { if(opt.dump_all) printf(" [vla] d%dv%d vec %d injecting\n", v->d->did, v->vid, vec); o->injected=1; } else { fprintf(stderr, "%s: Couldn't find an open ipi slot!\n", __func__); } } void hvm_vlapic_eoi_handler(struct hvm_data *h) { if(opt.dump_all) printf(" [vla] d%dv%d eoi\n", h->v->d->did, h->v->vid); } void hvm_vlapic_handler(struct hvm_data *h) { struct mmio_info *m = &h->inflight.mmio; switch(m->gpa) { case 0xfee00300: hvm_vlapic_icr_handler(h); break; case 0xfee000b0: hvm_vlapic_eoi_handler(h); break; } } /* Also called by shadow_mmio_postprocess */ #define MMIO_VGA_START (0xa0000) #define MMIO_VGA_END (0xbffff) void enumerate_mmio(struct hvm_data *h) { struct mmio_info *m = &h->inflight.mmio; /* Skip vga area */ if ( opt.mmio_enumeration_skip_vga && m->gpa >= MMIO_VGA_START && m->gpa < MMIO_VGA_END) { warn_once("WARNING: Not enumerationg MMIO in VGA range. Use --mmio-enumeration-skip-vga=0 to override.\n"); return; } if ( m->data_valid ) update_io_address(&h->summary.io.mmio, m->gpa, m->is_write, h->arc_cycles, m->va); } void hvm_mmio_summary(struct hvm_data *h, void *data) { long reason=(long)data; PRINT_SUMMARY(h->summary.mmio[reason], " mmio "); } void hvm_mmio_assist_postprocess(struct hvm_data *h) { long reason; switch(h->exit_reason) { case VMEXIT_NPF: case EXIT_REASON_EPT_VIOLATION: reason=NONPF_MMIO_NPF; hvm_set_summary_handler(h, hvm_mmio_summary, (void *)reason); break; case EXIT_REASON_APIC_ACCESS: reason=NONPF_MMIO_APIC; hvm_set_summary_handler(h, hvm_mmio_summary, (void *)reason); break; default: { static int warned = 0; if (!warned) { fprintf(warn, "%s: Strange, MMIO with unexpected exit reason %d\n", __func__, h->exit_reason); warned=1; } reason=NONPF_MMIO_UNKNOWN; hvm_set_summary_handler(h, hvm_mmio_summary, (void *)reason); break; } } if(opt.summary_info) { update_cycles(&h->summary.mmio[reason], h->arc_cycles); } if ( opt.with_mmio_enumeration ) enumerate_mmio(h); } #define HVM_IO_ASSIST_WRITE 0x200 void hvm_mmio_assist_process(struct record_info *ri, struct hvm_data *h) { struct mmio_info *m = &h->inflight.mmio; union { struct { unsigned int gpa; unsigned int data; } x32; struct { unsigned long long gpa; unsigned int data; } x64; } *r = (typeof(r))h->d; union { unsigned event; struct { unsigned minor:8, x64:1, write:2; }; } mevt = { .event = ri->event }; if(mevt.x64) { m->gpa = r->x64.gpa; m->data = r->x64.data; if(ri->extra_words*(sizeof(unsigned int))==sizeof(r->x64)) m->data_valid=1; } else { m->gpa = r->x32.gpa; m->data = r->x32.data; if(ri->extra_words*(sizeof(unsigned int))==sizeof(r->x32)) m->data_valid=1; } m->is_write = mevt.write; if(opt.dump_all) { if(m->data_valid) printf("]%s mmio_assist %c gpa %llx data %x\n", ri->dump_header, mevt.write?'w':'r', m->gpa, m->data); else printf("]%s mmio_assist %c gpa %llx (no data)\n", ri->dump_header, mevt.write?'w':'r', m->gpa); } if((m->gpa & 0xfffff000) == 0xfee00000) hvm_vlapic_handler(h); /* Catch MMIOs that don't go through the shadow code; tolerate * failures to set (probably shadow_mmio) */ hvm_set_postprocess(h, hvm_mmio_assist_postprocess); } void hvm_inj_virq_process(struct record_info *ri, struct hvm_data *h) { struct { int vector, fake; } *r = (typeof(r))h->d; if(opt.dump_all) { printf(" %s inj_virq vec %u %s\n", ri->dump_header, r->vector, r->fake?"fake":"real"); } if(opt.summary_info) { int vector = r->vector; if(vector >= GUEST_INTERRUPT_MAX) vector = GUEST_INTERRUPT_MAX; h->summary.guest_interrupt[vector].count++; if(opt.interval.output == INTERVAL_DOMAIN_GUEST_INTERRUPT) interval_domain_guest_interrupt(h, vector); } /* If we're waking, make this the wake vector */ if(r->vector < GUEST_INTERRUPT_MAX ) { int vector = r->vector; if ( h->w2h.waking && h->w2h.vector == 0 ) { if(h->summary.guest_interrupt[vector].start_tsc) { fprintf(warn, "Strange, d%dv%d waking && wake_vector 0 but vec %d start_tsc %lld!\n", h->v->d->did, h->v->vid, vector, h->summary.guest_interrupt[vector].start_tsc); error(ERR_WARN, NULL); } if(h->w2h.interrupts) fprintf(warn, "Strange, waking && wake_vector 0 but interrupts_this_wait_to_halt %d!\n", h->w2h.interrupts); if(opt.dump_all) printf(" [w2h] d%dv%d Setting wake_vector %d\n", h->v->d->did, h->v->vid, vector); /* In svm mode, vector information is invalid */ if ( opt.svm_mode && r->fake ) h->w2h.vector = FAKE_VECTOR; else h->w2h.vector = vector; h->summary.guest_interrupt[vector].is_wake = 1; } if( h->summary.guest_interrupt[vector].start_tsc == 0 ) { /* Note that we want start_tsc set at the next vmentry */ h->summary.guest_interrupt[vector].start_tsc = 1; h->w2h.interrupts_wanting_tsc++; h->w2h.interrupts++; if(opt.dump_all) printf(" [w2h] d%dv%d Starting vec %d\n", h->v->d->did, h->v->vid, vector); } } hvm_vlapic_inject(h->v, r->vector); } /* I/O Handling */ struct io_address { struct io_address *next; unsigned int pa; unsigned int va; struct cycle_summary summary[2]; }; void update_io_address(struct io_address ** list, unsigned int pa, int dir, tsc_t arc_cycles, unsigned int va) { struct io_address *p, *q=NULL; /* Keep list in order */ for(p=*list; p && (p->pa != pa) && (p->pa < pa); q=p, p=p->next); /* If we didn't find it, make a new element. */ if(!p || (p->pa != pa)) { if((p=malloc(sizeof(*p)))==NULL) { fprintf(stderr, "malloc() failed.\n"); error(ERR_SYSTEM, NULL); } bzero(p, sizeof(*p)); p->pa=pa; p->va=va; /* If we stopped in the middle or at the end, add it in */ if(q) { p->next=q->next; q->next=p; } else { /* Otherwise, we stopped after the first element; put it at the beginning */ p->next = *list; *list = p; } } update_cycles(&p->summary[dir], arc_cycles); } void hvm_io_address_summary(struct io_address *list, char * s) { if(!list) return; printf("%s\n", s); for(; list; list=list->next) { if ( list->va ) { PRINT_SUMMARY(list->summary[0], "%8x@%8x:[r] ", list->pa, list->va); PRINT_SUMMARY(list->summary[1], "%8x@%8x:[w] ", list->pa, list->va); } else { PRINT_SUMMARY(list->summary[0], "%8x:[r] ", list->pa); PRINT_SUMMARY(list->summary[1], "%8x:[w] ", list->pa); } } } void hvm_io_write_postprocess(struct hvm_data *h) { if(opt.with_pio_enumeration) update_io_address(&h->summary.io.pio, h->inflight.io.port, 1, h->arc_cycles, 0); } void hvm_io_read_postprocess(struct hvm_data *h) { if(opt.with_pio_enumeration) update_io_address(&h->summary.io.pio, h->inflight.io.port, 0, h->arc_cycles, 0); if(opt.scatterplot_io && h->inflight.io.port == opt.scatterplot_io_port) scatterplot_vs_time(h->exit_tsc, P.now - h->exit_tsc); } void hvm_io_assist_process(struct record_info *ri, struct hvm_data *h) { union { struct { unsigned int port; unsigned int data; } x32; } *r = (typeof(r))h->d; union { unsigned event; struct { unsigned minor:8, x64:1, write:2; }; } mevt = { .event = ri->event }; if(mevt.x64) { fprintf(stderr, "FATAL: Unexpected 64-bit PIO\n"); error(ERR_RECORD, ri); return; } h->inflight.io.port = r->x32.port; h->inflight.io.val = r->x32.data; if(mevt.write) { h->inflight.io.is_write = 1; if ( hvm_set_postprocess(h, hvm_io_write_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } else { h->inflight.io.is_write = 0; if ( hvm_set_postprocess(h, hvm_io_read_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } if(opt.dump_all) { printf(" %s io %s port %x val %x\n", ri->dump_header, mevt.write?"write":"read", r->x32.port, r->x32.data); } } /* cr_write */ /* CR3 list */ void cr3_switch(unsigned long long val, struct hvm_data *h) { struct vcpu_data *v = h->v; /* Really only need absolute tsc here. Later change to global time. */ unsigned long long now = P.now; unsigned long long gmfn = val >> 12; if ( !h->init ) return; if(opt.with_cr3_enumeration) { if(v->cr3.data) { struct cr3_value_struct *cur = v->cr3.data; unsigned long long cycles = now - v->cr3.start_time; if(opt.summary_info) update_cycles(&cur->total_time, cycles); cur->last_time = now; } if(gmfn) { struct cr3_value_struct *p, **last=&v->d->cr3_value_head; /* Always add to tail, so that we get consistent interval ouptut as the number of cr3s grow */ for(p=*last; p; last = (&p->next), p=p->next) if(p->gmfn == gmfn) break; if(!p) { if((p=malloc(sizeof(*p)))==NULL) { fprintf(stderr, "malloc() failed.\n"); error(ERR_SYSTEM, NULL); } bzero(p, sizeof(*p)); p->gmfn = gmfn; p->cr3_id = P.cr3.id; p->first_time = now; p->next=*last; *last=p; p->gnext = NULL; if(P.cr3.head) *P.cr3.tail = p; else P.cr3.head = p; P.cr3.tail = &p->gnext; P.cr3.id++; /* Add to the interval list if appropriate */ if(opt.interval.check == INTERVAL_CHECK_CR3 && v->d->did != DEFAULT_DOMAIN) interval_cr3_value_check(p); } if(p->prealloc_unpin.now) { fprintf(warn, "Re-promoting previously unpinned cr3 %llx!\n", p->gmfn); p->prealloc_unpin.now = 0; h->inflight.cr_write.repromote = 1; } /* Accounting for new toplevel */ v->cr3.start_time = now; p->switch_count++; if(p->destroy.callback) p->destroy.switch_count++; v->cr3.data = p; } else { v->cr3.data = NULL; } if (opt.scatterplot_cr3_switch) { scatterplot_vs_time(h->exit_tsc, v->cr3.data ? (v->cr3.data->cr3_id) : 0); } } else { if (opt.scatterplot_cr3_switch) scatterplot_vs_time(h->exit_tsc, gmfn); } v->cr3.val = val; }; void cr3_prealloc_unpin(struct vcpu_data *v, unsigned long long gmfn) { struct cr3_value_struct *cr3; /* Look for it in the list */ for(cr3 = v->d->cr3_value_head; cr3; cr3=cr3->next) if(cr3->gmfn == gmfn) break; if(!cr3) return; if(cr3->prealloc_unpin.now) fprintf(warn, "Strange, gmfn %llx multiple unpins w/o access!\n", gmfn); cr3->prealloc_unpin.now = 1; cr3->prealloc_unpin.count++; if(opt.dump_all) printf(" cr3 %llx unpinned %d times\n", gmfn, cr3->prealloc_unpin.count); } int cr3_compare_start(const void *_a, const void *_b) { struct cr3_value_struct *a=*(typeof(&a))_a; struct cr3_value_struct *b=*(typeof(&a))_b; if(a->first_time > b->first_time) return 1; else if(b->first_time == a->first_time) return 0; else return -1; } void cr3_dump_list(struct cr3_value_struct *head){ struct cr3_value_struct *p; struct cr3_value_struct **qsort_array; int i, N=0; if(!head) return; /* Count the number of elements */ for(p=head; p; p=p->next) N++; /* Alloc a struct of the right size */ qsort_array = malloc(N * sizeof(struct eip_list_struct *)); /* Point the array into it */ for(i=0, p=head; p; p=p->next, i++) qsort_array[i]=p; /* Sort the array by time */ qsort(qsort_array, N, sizeof(struct eip_list_struct *), cr3_compare_start); /* WARNING: don't use N after this point unless you copy this variable */ #if 0 if(opt.summary_eip_limit && opt.summary_eip_limit < N) N=opt.summary_eip_limit; #endif /* Now print the results */ printf(" cr3 values:\n"); for(i=0; ifirst_time, &first); abs_cycles_to_time(p->last_time, &last); snprintf(desc, 30, " %8llx (id %d)", p->gmfn, p->cr3_id); print_cycle_summary(&p->total_time, desc); snprintf(desc, 30, " guest"); print_cycle_percent_summary(&p->guest_time, p->run_time, desc); snprintf(desc, 30, " hv "); print_cycle_percent_summary(&p->hv_time, p->run_time, desc); hvm_short_summary(&p->hvm, p->run_time, " + "); printf(" Seen: %4u.%09u-%4u.%09u switch %d flush %d\n", first.s, first.ns, last.s, last.ns, p->switch_count, p->flush_count); if(p->destroy.callback) printf(" destroy: flush %u switch %u fixup %u emulate %u\n", p->destroy.flush_count, p->destroy.switch_count, p->destroy.fixup_user, p->destroy.emulate_corr_user); } free(qsort_array); } void hvm_cr3_write_summary(struct hvm_data *h) { int j; for(j=0; jsummary.cr3_write_resyncs[j], " *[%3d] ", j); PRINT_SUMMARY(h->summary.cr3_write_resyncs[j], " *[MAX] "); } void hvm_cr_write_summary(struct hvm_data *h, void *data) { long cr=(long)data; PRINT_SUMMARY(h->summary.cr_write[cr], " cr%ld ", cr); if ( cr==3 ) hvm_cr3_write_summary(h); } void hvm_cr_write_postprocess(struct hvm_data *h) { if(h->inflight.cr_write.cr == 3) { struct vcpu_data *v = h->v; unsigned long long new_val = h->inflight.cr_write.val; unsigned long long oval; int flush=0; if(v->cr3.val) { oval = v->cr3.val; if(new_val == oval) { if(v->cr3.data) { v->cr3.data->flush_count++; if(v->cr3.data->destroy.callback) v->cr3.data->destroy.flush_count++; } flush=1; } } if(opt.summary_info) { int resyncs = h->resyncs; if(resyncs > RESYNCS_MAX) resyncs = RESYNCS_MAX; update_cycles(&h->summary.cr3_write_resyncs[resyncs], h->arc_cycles); update_cycles(&h->summary.cr_write[3], h->arc_cycles); hvm_update_short_summary(h, HVM_SHORT_SUMMARY_CR3); } if(!flush) cr3_switch(new_val, h); } else { if(opt.summary_info) { if(h->inflight.cr_write.cr < CR_MAX) update_cycles(&h->summary.cr_write[h->inflight.cr_write.cr], h->arc_cycles); } } /* Set summary handler */ /* FIXME - deal with cr_read_summary */ if(h->exit_reason < h->exit_reason_max) { /* Want a different "set" for each cr */ switch(h->inflight.cr_write.cr) { #define case_cr(_x) \ case (_x): \ hvm_set_summary_handler(h, hvm_cr_write_summary, (void *)(_x)); \ break case_cr(0); case_cr(1); case_cr(2); case_cr(3); case_cr(4); case_cr(5); case_cr(6); case_cr(7); case_cr(8); case_cr(9); case_cr(10); case_cr(11); case_cr(12); case_cr(13); case_cr(14); case_cr(15); #undef case_cr default: fprintf(stderr, "Unexpected cr: %d\n", h->inflight.cr_write.cr); error(ERR_SANITY, NULL); break; } } } void hvm_cr_write_process(struct record_info *ri, struct hvm_data *h) { union { struct { unsigned cr; unsigned int val; } x32; struct { unsigned cr; unsigned long long val; } __attribute__((packed)) x64; } *r = (typeof(r))h->d; unsigned cr; unsigned long long val; if(ri->event & TRC_64_FLAG) { h->inflight.cr_write.cr = cr = r->x64.cr; h->inflight.cr_write.val = val = r->x64.val; } else { h->inflight.cr_write.cr = cr = r->x32.cr; h->inflight.cr_write.val = val = r->x32.val; } /* In vmx, in real mode, cr accesses may cause EXNMI vmexits. * Account them under that heading; otherwise, complain */ if ( hvm_set_postprocess(h, hvm_cr_write_postprocess) ) fprintf(warn, "%s: Strange, h->postprocess already set!\n", __func__); if(opt.dump_all) { if(cr == 3 && h->v->cr3.val) { printf("]%s cr_write cr3 val %llx oval %llx %s\n", ri->dump_header, val, h->v->cr3.val, (h->v->cr3.val == val)?"flush":"switch"); } else { printf(" %s cr_write cr%d val %llx\n", ri->dump_header, cr, val); } } } /* msr_write */ void hvm_msr_write_summary(struct hvm_data *h, void *d) { } void hvm_msr_write_postprocess(struct hvm_data *h) { if(opt.summary_info) { } /* Set summary handler */ hvm_set_summary_handler(h, hvm_msr_write_summary, NULL); } void hvm_msr_write_process(struct record_info *ri, struct hvm_data *h) { struct { unsigned int addr; unsigned long long val; } __attribute__((packed)) *r = (typeof(r))h->d; if(check_extra_words(ri, sizeof(*r), "msr_write")) return; h->inflight.msr.addr = r->addr; h->inflight.msr.val = r->val; if(opt.dump_all) { printf(" %s msr_write addr %x val %llx\n", ri->dump_header, r->addr, r->val); } if ( hvm_set_postprocess(h, hvm_msr_write_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } /* msr_read */ void hvm_msr_read_summary(struct hvm_data *h, void *d) { } void hvm_msr_read_postprocess(struct hvm_data *h) { if(opt.summary_info) { } /* Set summary handler */ hvm_set_summary_handler(h, hvm_msr_read_summary, NULL); } void hvm_msr_read_process(struct record_info *ri, struct hvm_data *h) { struct { unsigned int addr; unsigned long long val; } __attribute__((packed)) *r = (typeof(r))h->d; if(check_extra_words(ri, sizeof(*r), "msr_read")) return; h->inflight.msr.addr = r->addr; h->inflight.msr.val = r->val; if(opt.dump_all) { printf(" %s msr_read addr %x val %llx\n", ri->dump_header, r->addr, r->val); } if ( hvm_set_postprocess(h, hvm_msr_read_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } void hvm_vmcall_summary(struct hvm_data *h, void *d) { int i; for ( i=0; isummary.vmcall[i], " [%10s] ", hypercall_name[i]); } PRINT_SUMMARY(h->summary.vmcall[HYPERCALL_MAX], " [%10s] ", "max"); } void hvm_vmcall_postprocess(struct hvm_data *h) { unsigned eax = h->inflight.vmcall.eax ; if(opt.summary) { if ( eax < HYPERCALL_MAX ) update_cycles(&h->summary.vmcall[eax], h->arc_cycles); else update_cycles(&h->summary.vmcall[HYPERCALL_MAX], h->arc_cycles); hvm_set_summary_handler(h, hvm_vmcall_summary, NULL); } } void hvm_vmcall_process(struct record_info *ri, struct hvm_data *h) { struct { unsigned int eax; } *r = (typeof(r))h->d; if(opt.dump_all) { if(r->eax < HYPERCALL_MAX) printf(" %s vmcall %2x (%s)\n", ri->dump_header, r->eax, hypercall_name[r->eax]); else printf(" %s vmcall %2x\n", ri->dump_header, r->eax); } h->inflight.vmcall.eax = r->eax; if ( hvm_set_postprocess(h, hvm_vmcall_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } void hvm_inj_exc_process(struct record_info *ri, struct hvm_data *h) { struct { unsigned vec, ec; } *r = (typeof(r))h->d; if ( opt.dump_all ) { if(r->vec < HVM_TRAP_MAX) printf(" %3u.%09u %s inj_exc trap %s ec %x\n", ri->t.s, ri->t.ns, pcpu_string(ri->cpu), hvm_trap_name[r->vec], r->ec); else printf(" %3u.%09u %s inj_exc trap %u ec %x\n", ri->t.s, ri->t.ns, pcpu_string(ri->cpu), r->vec, r->ec); } } void hvm_intr_summary(struct hvm_data *h, void *d) { int i; for(i=0; isummary.extint[i]) { if(hvm_extint_vector_name[i]) printf(" %10s(%3d): %d\n", hvm_extint_vector_name[i], i, h->summary.extint[i]); else printf(" [%3d]: %d\n", i, h->summary.extint[i]); } if(h->summary.extint[EXTERNAL_INTERRUPT_MAX]) printf(" Other: : %d\n", h->summary.extint[EXTERNAL_INTERRUPT_MAX]); } void hvm_intr_process(struct record_info *ri, struct hvm_data *h) { unsigned vec = *(unsigned *)h->d; /* Vector is difficult to get in SVM mode */ if ( opt.svm_mode ) vec = 0; if( (h->rip >> ADDR_SPACE_BITS) != 00 && (h->rip >> ADDR_SPACE_BITS) != ((0ULL-1)>> ADDR_SPACE_BITS) ) { fprintf(stderr, "Unexpected rip %llx (shift %llx)\n", h->rip, h->rip >> ADDR_SPACE_BITS); error(ERR_RECORD, NULL); /* Can process with strange rip */ } h->inflight.intr.vec = vec; if ( opt.dump_all ) { if ( vec < EXTERNAL_INTERRUPT_MAX && hvm_extint_vector_name[vec] ) printf(" %s intr vec %s(%x)\n", ri->dump_header, hvm_extint_vector_name[vec], vec); else printf(" %s intr vec %x\n", ri->dump_header, vec); } if(opt.scatterplot_interrupt_eip && vec == opt.scatterplot_interrupt_vector) { struct time_struct t; /* Truncate to 40 bits */ unsigned long long rip = h->rip & ((1ULL << ADDR_SPACE_BITS)-1); /* Want absolute tsc to global tsc */ abs_cycles_to_time(h->exit_tsc, &t); printf("d%dv%d %u.%09u %lld\n", h->v->d->did, h->v->vid, t.s, t.ns, rip); } if(opt.histogram_interrupt_eip && vec == opt.histogram_interrupt_vector) { /* Truncate to 40 bits */ unsigned long long rip = h->rip & ((1ULL << ADDR_SPACE_BITS)-1); unsigned index = rip / opt.histogram_interrupt_increment; h->summary.extint_histogram[index]++; } if(opt.with_interrupt_eip_enumeration && vec == opt.interrupt_eip_enumeration_vector) { /* Truncate to 40 bits */ unsigned long long rip = h->rip & ((1ULL << ADDR_SPACE_BITS)-1); /* Want absolute tsc to global tsc */ update_eip(&h->v->d->interrupt_eip_list, rip, 0, 0, NULL); } /* Disable generic postprocessing */ /* FIXME: Do the summary stuff in a post-processor */ h->post_process = NULL; if(opt.summary_info) { if(opt.summary) hvm_set_summary_handler(h, hvm_intr_summary, NULL); if(vec < EXTERNAL_INTERRUPT_MAX) h->summary.extint[vec]++; else h->summary.extint[EXTERNAL_INTERRUPT_MAX]++; } } void hvm_intr_window_process(struct record_info *ri, struct hvm_data *h) { struct { uint32_t vector; uint32_t source; int32_t intr; } *r = (typeof(r))h->d; char *intsrc_name[] = { "none", "pic", "lapic", "nmi", "mce", "vector" }; if ( opt.dump_all ) { printf(" %s intr_window vec %u src %u(%s) ", ri->dump_header, (unsigned)r->vector, (unsigned)r->source, r->source < 6 ? intsrc_name[r->source]: "?"); if ( r->intr > 0 ) printf("intr %x\n", (unsigned)r->intr); else printf("intr #\n"); } } void hvm_pf_inject_process(struct record_info *ri, struct hvm_data *h) { union { struct { unsigned ec; unsigned int cr2; } x32; struct { unsigned ec; unsigned long long cr2; } __attribute__((packed)) x64; } *r = (typeof(r))h->d; unsigned int ec; unsigned long long cr2; int is_64 = 0; if(ri->event & TRC_64_FLAG) { is_64 = 1; cr2 = r->x64.cr2; ec = r->x64.ec; } else { cr2 = r->x32.cr2; ec = r->x32.ec; } if ( opt.dump_all ) { printf(" %3u.%09u %s pf_inject%s guest_cr2 %llx guest_ec %x\n", ri->t.s, ri->t.ns, pcpu_string(ri->cpu), is_64?"64":"", cr2, ec); } } void hvm_generic_postprocess_init(struct record_info *ri, struct hvm_data *h); void hvm_npf_process(struct record_info *ri, struct hvm_data *h) { struct { uint64_t gpa; uint64_t mfn; uint32_t qualification; uint32_t p2mt; } *r = (typeof(r))h->d; if ( opt.dump_all ) printf(" %s npf gpa %llx q %x mfn %llx t %d\n", ri->dump_header, (unsigned long long)r->gpa, r->qualification, (unsigned long long)r->mfn, r->p2mt); if ( opt.summary_info ) hvm_generic_postprocess_init(ri, h); } void hvm_rdtsc_process(struct record_info *ri, struct hvm_data *h) { struct { unsigned long long tsc; } *r = (typeof(r))h->d; if ( opt.dump_all ) printf(" %s rdtsc %llx %lld %s\n", ri->dump_header, (unsigned long long)r->tsc, (unsigned long long)r->tsc, h->last_rdtsc > r->tsc ? "BACKWARDS" : ""); if ( opt.scatterplot_rdtsc ) { struct time_struct t; abs_cycles_to_time(ri->tsc, &t); printf("%dv%d %u.%09u %llu\n", h->v->d->did, h->v->vid, t.s, t.ns, r->tsc); } h->last_rdtsc = r->tsc; } void hvm_generic_summary(struct hvm_data *h, void *data) { long evt = (long)data; assert(evt < HVM_EVENT_HANDLER_MAX); PRINT_SUMMARY(h->summary.generic[evt], " %s ", hvm_event_handler_name[evt]); } void hvm_generic_postprocess_init(struct record_info *ri, struct hvm_data *h) { if ( h->post_process != hvm_generic_postprocess ) fprintf(warn, "%s: Strange, h->postprocess set!\n", __func__); h->inflight.generic.event = ri->event; bcopy(h->d, h->inflight.generic.d, sizeof(unsigned int) * 4); } void hvm_generic_postprocess(struct hvm_data *h) { long evt = 0; static unsigned registered[HVM_EVENT_HANDLER_MAX] = { 0 }; if ( h->inflight.generic.event ) evt = (h->inflight.generic.event - TRC_HVM_HANDLER) & ~(TRC_64_FLAG|HVM_IO_ASSIST_WRITE); else { static unsigned warned[HVM_EXIT_REASON_MAX] = { 0 }; /* Some exits we don't expect a handler; just return */ if(opt.svm_mode) { } else { switch(h->exit_reason) { /* These just need us to go through the return path */ case EXIT_REASON_PENDING_INTERRUPT: case EXIT_REASON_TPR_BELOW_THRESHOLD: /* Not much to log now; may need later */ case EXIT_REASON_WBINVD: return; default: break; } } if ( !warned[h->exit_reason] ) { /* If we aren't a known exception, warn and log results */ fprintf(warn, "%s: Strange, exit %x(%s) missing a handler\n", __func__, h->exit_reason, (h->exit_reason > h->exit_reason_max) ? "[clipped]" : h->exit_reason_name[h->exit_reason]); warned[h->exit_reason]=1; } } if ( evt >= HVM_EVENT_HANDLER_MAX || evt < 0) { fprintf(warn, "%s: invalid hvm event %lx(%x)\n", __func__, evt, h->inflight.generic.event); error(ERR_RECORD, NULL); return; } if(opt.summary_info) { update_cycles(&h->summary.generic[evt], h->arc_cycles); /* NB that h->exit_reason may be 0, so we offset by 1 */ if ( registered[evt] ) { static unsigned warned[HVM_EXIT_REASON_MAX] = { 0 }; if ( registered[evt] != h->exit_reason+1 && !warned[h->exit_reason]) { fprintf(warn, "%s: HVM evt %lx in %x and %x!\n", __func__, evt, registered[evt]-1, h->exit_reason); warned[h->exit_reason]=1; } } else { int ret; if((ret=__hvm_set_summary_handler(h, hvm_generic_summary, (void *)evt))) fprintf(stderr, "%s: hvm_set_summary_handler returned %d\n", __func__, ret); registered[evt]=h->exit_reason+1; } /* HLT checked at hvm_vmexit_close() */ } } void hvm_generic_dump(struct record_info *ri, char * prefix) { struct { unsigned vcpu:16, domain:16; unsigned d[4]; } *cr = (typeof(cr))ri->d; char *evt_string, evt_number[256]; int i, evt, is_64 = 0; evt = ri->event - TRC_HVM_HANDLER; if(evt & TRC_64_FLAG) { evt &= ~(TRC_64_FLAG); is_64=1; } if(evt < HVM_EVENT_HANDLER_MAX) { evt_string = hvm_event_handler_name[evt]; } else { snprintf(evt_number, 256, "hvm_handler %d", evt); evt_string = evt_number; } printf("%s%s %s%s [", prefix, ri->dump_header, evt_string, is_64?"64":""); for(i=0; iextra_words; i++) { printf(" %x", ri->d[i]); } printf(" ]\n"); } void hvm_handler_process(struct record_info *ri, struct hvm_data *h) { /* Wait for first vmexit to initialize */ if(!h->init) { if(opt.dump_all) hvm_generic_dump(ri,"!"); return; } h->d = ri->d; /* Handle things that don't need a vmexit */ switch(ri->event) { default: goto needs_vmexit; /* Records about changing guest state */ case TRC_HVM_PF_INJECT: case TRC_HVM_PF_INJECT64: hvm_pf_inject_process(ri, h); break; case TRC_HVM_REINJ_VIRQ: if ( opt.dump_all ) { printf(" %3u.%09u %s inj_virq vec %u\n", ri->t.s, ri->t.ns, pcpu_string(ri->cpu), *(unsigned*)h->d); } break; case TRC_HVM_INJ_EXC: hvm_inj_exc_process(ri, h); break; case TRC_HVM_INJ_VIRQ: hvm_inj_virq_process(ri, h); break; case TRC_HVM_INTR_WINDOW: hvm_intr_window_process(ri, h); break; case TRC_HVM_OP_DESTROY_PROC: if(h->v->cr3.data) { struct cr3_value_struct *cur = h->v->cr3.data; if(cur->destroy.callback) fprintf(warn, "Strange, double callback for cr3 gmfn %llx!\n", cur->gmfn); cur->destroy.callback = 1; } else if(opt.with_cr3_enumeration) { fprintf(warn, "Warning: destroy_proc: don't know current cr3\n"); } if ( opt.dump_all ) { printf(" %3u.%09u %s destroy_proc cur_cr3 %llx\n", ri->t.s, ri->t.ns, pcpu_string(ri->cpu), h->v->cr3.val); } break; } return; needs_vmexit: /* Wait for the next vmexit */ if(!h->vmexit_valid) { if(opt.dump_all) hvm_generic_dump(ri,"!"); return; } /* Keep generic "event handler" info */ h->event_handler = ri->event - TRC_HVM_HANDLER; switch(ri->event) { /* Records adding to the vmexit reason */ case TRC_HVM_INTR: hvm_intr_process(ri, h); break; case TRC_HVM_PF_XEN: case TRC_HVM_PF_XEN64: hvm_pf_xen_process(ri, h); break; case TRC_HVM_IOPORT_READ: case TRC_HVM_IOPORT_WRITE: hvm_io_assist_process(ri, h); break; case TRC_HVM_IOMEM_READ: case TRC_HVM_IOMEM_WRITE: case TRC_HVM_IOMEM_READ|TRC_64_FLAG: case TRC_HVM_IOMEM_WRITE|TRC_64_FLAG: hvm_mmio_assist_process(ri, h); break; case TRC_HVM_CR_WRITE: case TRC_HVM_CR_WRITE64: hvm_cr_write_process(ri, h); break; case TRC_HVM_MSR_WRITE: hvm_msr_write_process(ri, h); break; case TRC_HVM_MSR_READ: hvm_msr_read_process(ri, h); break; case TRC_HVM_VMMCALL: hvm_vmcall_process(ri, h); break; case TRC_HVM_NPF: hvm_npf_process(ri, h); break; case TRC_HVM_RDTSC: hvm_rdtsc_process(ri, h); break; case TRC_HVM_DR_READ: case TRC_HVM_DR_WRITE: case TRC_HVM_CPUID: case TRC_HVM_SMI: case TRC_HVM_HLT: case TRC_HVM_INVLPG: case TRC_HVM_INVLPG64: case TRC_HVM_MCE: case TRC_HVM_CLTS: case TRC_HVM_LMSW: case TRC_HVM_LMSW64: case TRC_HVM_NMI: case TRC_HVM_REALMODE_EMULATE: case TRC_HVM_TRAP: case TRC_HVM_TRAP_DEBUG: case TRC_HVM_CR_READ: case TRC_HVM_CR_READ64: default: if(opt.dump_all) hvm_generic_dump(ri, "]"); if(opt.summary_info) hvm_generic_postprocess_init(ri, h); break; } } void vcpu_next_update(struct pcpu_info *p, struct vcpu_data *next, tsc_t tsc); void vcpu_prev_update(struct pcpu_info *p, struct vcpu_data *prev, tsc_t tsc, int new_runstate); struct vcpu_data * vcpu_find(int did, int vid); void lose_vcpu(struct vcpu_data *v, tsc_t tsc); int domain_runstate(struct domain_data *d) { int i; int runstates[RUNSTATE_MAX]; int ret=-1; int max_vcpus = 0; if(d->did == DEFAULT_DOMAIN) return 0; for(i=0; imax_vid; i++) if(d->vcpu[i] && d->vcpu[i]->runstate.state != RUNSTATE_INIT) { max_vcpus++; runstates[d->vcpu[i]->runstate.state]++; } if(runstates[RUNSTATE_LOST] == max_vcpus) ret=DOMAIN_RUNSTATE_LOST; else if(runstates[RUNSTATE_RUNNING]) { if(runstates[RUNSTATE_RUNNABLE]) ret=DOMAIN_RUNSTATE_CONCURRENCY_HAZARD; else if(runstates[RUNSTATE_BLOCKED]||runstates[RUNSTATE_OFFLINE]) ret= DOMAIN_RUNSTATE_PARTIAL_RUN; else ret= DOMAIN_RUNSTATE_FULL_RUN; } else if(runstates[RUNSTATE_RUNNABLE]) { if(runstates[RUNSTATE_BLOCKED]||runstates[RUNSTATE_OFFLINE]) ret= DOMAIN_RUNSTATE_PARTIAL_CONTENTION; else ret= DOMAIN_RUNSTATE_FULL_CONTENTION; } else if(runstates[RUNSTATE_BLOCKED]||runstates[RUNSTATE_OFFLINE]) { ret= DOMAIN_RUNSTATE_BLOCKED; } else { fprintf(warn, "Strange, no meaningful runstates for d%d!\n", d->did); } if ( ret < 0 ) { printf(" Max vid: %d (max_vcpus %d)\n", d->max_vid, max_vcpus); for(i=0; i<=d->max_vid; i++) if(d->vcpu[i]) fprintf(warn, " v%d: %s\n", i, runstate_name[d->vcpu[i]->runstate.state]); for(i=0; i= 0) return ret; error(ERR_ASSERT, NULL); return -1; /* Never happens */ } static inline void runstate_update(struct vcpu_data *v, int new_runstate, tsc_t tsc) { struct domain_data *d = v->d; if ( opt.scatterplot_runstate ) { struct time_struct t; abs_cycles_to_time(tsc, &t); printf("%dv%d %u.%09u %d\n", d->did, v->vid, t.s, t.ns, runstate_graph[v->runstate.state]); printf("%dv%d %u.%09u %d\n", d->did, v->vid, t.s, t.ns, runstate_graph[new_runstate]); } if(v->runstate.tsc > 0 && v->runstate.tsc < tsc) { update_cycles(v->runstates + v->runstate.state, tsc - v->runstate.tsc); if ( opt.scatterplot_runstate_time ) { struct time_struct t, dt; abs_cycles_to_time(tsc, &t); cycles_to_time(tsc - v->runstate.tsc, &dt); printf("%dv%d %u.%09u %u.%09u\n", d->did, v->vid, t.s, t.ns, dt.s, dt.ns); } if(v->runstate.state == RUNSTATE_RUNNING) update_cycles(&v->d->total_time, tsc - v->runstate.tsc); if(v->runstate.state == RUNSTATE_RUNNABLE) update_cycles(v->runnable_states + v->runstate.runnable_state, tsc - v->runstate.tsc); /* How much did dom0 run this buffer? */ if(v->d->did == 0) { int i; for(i=0; iactive) continue; start_tsc = (p->volume.buffer_first_tsc > v->runstate.tsc) ? p->volume.buffer_first_tsc : v->runstate.tsc; p->volume.buffer_dom0_runstate_cycles[v->runstate.state] += tsc - start_tsc; #if 0 printf(" - updated p%d dom0_runstate %s to %lld cycles (+%lld)\n", p->pid, runstate_name[v->runstate.state], p->volume.buffer_dom0_runstate_cycles[v->runstate.state], tsc - start_tsc); #endif p->volume.buffer_dom0_runstate = new_runstate; p->volume.buffer_dom0_runstate_tsc = tsc; } } } /* Detect "runnable" states */ if ( new_runstate == RUNSTATE_RUNNABLE ) { switch(v->runstate.state) { case RUNSTATE_RUNNING: v->runstate.runnable_state=RUNNABLE_STATE_PREEMPT; break; case RUNSTATE_BLOCKED: case RUNSTATE_OFFLINE: v->runstate.runnable_state=RUNNABLE_STATE_WAKE; break; default: v->runstate.runnable_state=RUNNABLE_STATE_OTHER; break; } } else v->runstate.runnable_state=RUNNABLE_STATE_INVALID; v->runstate.state = new_runstate; v->runstate.tsc = tsc; /* Determine the domain runstate */ if(d->runstate_tsc > 0 && d->runstate_tsc < tsc) update_cycles(d->runstates + d->runstate, tsc - d->runstate_tsc); d->runstate = domain_runstate(d); d->runstate_tsc = tsc; } void hvm_vmexit_process(struct record_info *ri, struct hvm_data *h, struct vcpu_data *v) { struct { union { struct { unsigned int exit_reason; unsigned long long rip; } __attribute__((packed)) x64; struct { unsigned int exit_reason; unsigned int eip; } x32; }; } *r; if ( ri->event & TRC_64_FLAG ) { if (check_extra_words(ri, sizeof(r->x64), "vmexit")) return; } else { if (check_extra_words(ri, sizeof(r->x32), "vmexit")) return; } r = (typeof(r))ri->d; if(!h->init) init_hvm_data(h, v); h->vmexit_valid=1; bzero(&h->inflight, sizeof(h->inflight)); if(ri->event == TRC_HVM_VMEXIT64) { if(v->guest_paging_levels != 4) { if ( verbosity >= 6 ) fprintf(warn, "%s: VMEXIT64, but guest_paging_levels %d. Switching to 4.\n", __func__, v->guest_paging_levels); v->guest_paging_levels = 4; } if(!is_valid_addr64(r->x64.rip)) fprintf(warn, "%s: invalid va %llx\n", __func__, r->x64.rip); h->rip = r->x64.rip; h->exit_reason = r->x64.exit_reason; } else { if(v->guest_paging_levels == 4) { int new_paging_levels = opt.default_guest_paging_levels; if(new_paging_levels == 4) new_paging_levels = 2; /* Wild guess */ if ( verbosity >= 6 ) fprintf(warn, "%s: VMEXIT, but guest_paging_levels %d. Switching to %d(default).\n", __func__, v->guest_paging_levels, new_paging_levels); v->guest_paging_levels = new_paging_levels; } h->rip = r->x32.eip; h->exit_reason = r->x32.exit_reason; } if(opt.scatterplot_vmexit_eip) scatterplot_vs_time(ri->tsc, h->rip); if(h->exit_reason > h->exit_reason_max) { fprintf(warn, "h->exit_reason %x > exit_reason_max %x!\n", (unsigned int)h->exit_reason, (unsigned int)h->exit_reason_max); error(ERR_RECORD, ri); return; } if(opt.dump_all) { if ( h->exit_reason < h->exit_reason_max && h->exit_reason_name[h->exit_reason] != NULL) printf("]%s vmexit exit_reason %s eip %llx%s\n", ri->dump_header, h->exit_reason_name[h->exit_reason], h->rip, find_symbol(h->rip)); else printf("]%s vmexit exit_reason %x eip %llx%s\n", ri->dump_header, h->exit_reason, h->rip, find_symbol(h->rip)); } if(h->v->cr3.data && h->entry_tsc) { update_cycles(&h->v->cr3.data->guest_time, ri->tsc - h->entry_tsc); h->v->cr3.data->run_time += (ri->tsc - h->entry_tsc); } h->exit_tsc = ri->tsc; h->entry_tsc = 0; h->resyncs = 0; h->prealloc_unpin = 0; h->wrmap_bf = 0; h->short_summary_done = 0; h->post_process = hvm_generic_postprocess; h->inflight.generic.event = 0; } void hvm_close_vmexit(struct hvm_data *h, tsc_t tsc) { if(h->exit_tsc) { if(h->exit_tsc > tsc) h->arc_cycles = 0; else { h->arc_cycles = tsc - h->exit_tsc; if(opt.summary_info) { update_cycles(&h->summary.exit_reason[h->exit_reason], h->arc_cycles); h->summary_info = 1; } if ( opt.scatterplot_extint_cycles && h->exit_reason == EXIT_REASON_EXTERNAL_INTERRUPT && h->inflight.intr.vec == opt.scatterplot_extint_cycles_vector ) { struct time_struct t; abs_cycles_to_time(tsc, &t); printf("d%dv%d %u.%09u %lld\n", h->v->d->did, h->v->vid, t.s, t.ns, h->arc_cycles); } } } if(h->post_process) (h->post_process)(h); if(h->arc_cycles) { if(opt.summary_info && !h->short_summary_done) { switch(h->event_handler) { case HVM_EVENT_HANDLER_VMCALL: hvm_update_short_summary(h, HVM_SHORT_SUMMARY_VMCALL); break; case HVM_EVENT_HANDLER_INTR: hvm_update_short_summary(h, HVM_SHORT_SUMMARY_INTERRUPT); break; case HVM_EVENT_HANDLER_HLT: hvm_update_short_summary(h, HVM_SHORT_SUMMARY_HLT); break; default: hvm_update_short_summary(h, HVM_SHORT_SUMMARY_OTHER); break; } } if(h->v->cr3.data) { h->v->cr3.data->run_time += h->arc_cycles; if(opt.summary_info) update_cycles(&h->v->cr3.data->hv_time, h->arc_cycles); } } h->exit_tsc = 0; h->vmexit_valid = 0; h->post_process = NULL; } void hvm_vmentry_process(struct record_info *ri, struct hvm_data *h) { if(!h->init) { if(opt.dump_all) printf("!%s vmentry\n", ri->dump_header); return; } /* Vista bug * This has to be done here because irqs are injected on the path out * to vmexit. */ hvm_vlapic_vmentry_cleanup(h->v, ri->tsc); if(h->w2h.waking && opt.dump_all) printf(" [w2h] d%dv%d Finishing waking\n", h->v->d->did, h->v->vid); h->w2h.waking = 0; if ( h->w2h.interrupts_wanting_tsc ) { int i; for(i=0; isummary.guest_interrupt[i].start_tsc == 1 ) { if(opt.dump_all) printf(" [w2h] d%dv%d Setting vec %d tsc to %lld\n", h->v->d->did, h->v->vid, i, ri->tsc); h->summary.guest_interrupt[i].start_tsc = ri->tsc; h->w2h.interrupts_wanting_tsc--; if ( h->w2h.interrupts_wanting_tsc == 0 ) break; } } } if(!h->vmexit_valid) { if(opt.dump_all) printf("!%s vmentry\n", ri->dump_header); return; } if(opt.dump_all) { unsigned long long arc_cycles = ri->tsc - h->exit_tsc; printf("]%s vmentry cycles %lld %s\n", ri->dump_header, arc_cycles, (arc_cycles>10000)?"!":""); } hvm_close_vmexit(h, ri->tsc); h->entry_tsc = ri->tsc; } void hvm_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct vcpu_data *v = p->current; struct hvm_data *h = &v->hvm; assert(p->current); if(vcpu_set_data_type(p->current, VCPU_DATA_HVM)) return; if(ri->evt.sub == 2) { UPDATE_VOLUME(p, hvm[HVM_VOL_HANDLER], ri->size); hvm_handler_process(ri, h); } else { switch(ri->event) { /* HVM */ case TRC_HVM_VMEXIT: case TRC_HVM_VMEXIT64: UPDATE_VOLUME(p, hvm[HVM_VOL_VMEXIT], ri->size); hvm_vmexit_process(ri, h, v); break; case TRC_HVM_VMENTRY: UPDATE_VOLUME(p, hvm[HVM_VOL_VMENTRY], ri->size); hvm_vmentry_process(ri, &p->current->hvm); break; default: fprintf(warn, "Unknown hvm event: %x\n", ri->event); } } } void hvm_summary(struct hvm_data *h) { int i; if(!h->summary_info) return; printf("Exit reasons:\n"); for(i=0; iexit_reason_max; i++) { struct hvm_summary_handler_node *p; if ( h->exit_reason_name[i] ) PRINT_SUMMARY(h->summary.exit_reason[i], " %-20s ", h->exit_reason_name[i]); else PRINT_SUMMARY(h->summary.exit_reason[i], " %20d ", i); p=h->exit_reason_summary_handler_list[i]; while(p) { p->handler(h, p->data); p=p->next; } } printf("Guest interrupt counts:\n"); for(i=0; isummary.guest_interrupt[i].count) { int j; printf(" [%3d] %d\n", i, h->summary.guest_interrupt[i].count); for(j=1; jsummary.guest_interrupt[i].runtime+j, desc); } } if(h->summary.guest_interrupt[i].count) printf(" [%d+] %d\n", i, h->summary.guest_interrupt[i].count); if(opt.histogram_interrupt_eip) { unsigned max = ((1ULL<summary.extint_histogram[i]) { printf("[%llx-%llx]: %d\n", opt.histogram_interrupt_increment * i, (opt.histogram_interrupt_increment * (i+1)) - 1, h->summary.extint_histogram[i]); } } PRINT_SUMMARY(h->summary.ipi_latency, "IPI latency \n"); for(i=0; i<256; i++) if(h->summary.ipi_count[i]) printf(" [%3d] %10d\n", i, h->summary.ipi_count[i]); hvm_io_address_summary(h->summary.io.pio, "IO address summary:"); hvm_io_address_summary(h->summary.io.mmio, "MMIO address summary:"); } /* ---- Shadow records ---- */ union shadow_event { unsigned event; struct { unsigned minor:8, paging_levels:4; }; }; /* WARNING - not thread safe */ #define FLAGSTRING(_name, _char) \ if(e->flag_ ## _name) \ flagstring[i] = _char; \ i++; char * flag_string(struct pf_xen_extra *e) { static char flagstring[32]; int i=0; for(i=0; i<32; i++) flagstring[i]='-'; i=0; if(e->flag_set_ad) flagstring[i]='d'; else if(e->flag_set_a) flagstring[i]='a'; i++; FLAGSTRING(shadow_l1_get_ref, 'g'); FLAGSTRING(shadow_l1_put_ref, 'p'); //FLAGSTRING(l2_propagate, '2'); FLAGSTRING(demote, 'D'); FLAGSTRING(promote, 'P'); FLAGSTRING(wrmap, 'w'); FLAGSTRING(wrmap_guess_found, 'G'); //FLAGSTRING(wrmap_brute_force, 'b'); FLAGSTRING(early_unshadow, 'e'); FLAGSTRING(prealloc_unhook, 'H'); FLAGSTRING(unsync, 'u'); FLAGSTRING(oos_fixup_add, 'a'); FLAGSTRING(oos_fixup_evict, 'x'); flagstring[i]=0; return flagstring; } void shadow_emulate_postprocess(struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; if ( opt.summary_info ) { update_eip(&h->v->d->emulate_eip_list, h->rip, h->arc_cycles, 0, NULL); update_cycles(&h->summary.pf_xen[PF_XEN_EMULATE], h->arc_cycles); update_cycles(&h->summary.pf_xen_emul[e->pt_level], h->arc_cycles); if(h->prealloc_unpin) update_cycles(&h->summary.pf_xen_emul[PF_XEN_EMUL_PREALLOC_UNPIN], h->arc_cycles); if(e->flag_prealloc_unhook) update_cycles(&h->summary.pf_xen_emul[PF_XEN_EMUL_PREALLOC_UNHOOK], h->arc_cycles); if(e->flag_early_unshadow) update_cycles(&h->summary.pf_xen_emul[PF_XEN_EMUL_EARLY_UNSHADOW], h->arc_cycles); if(e->flag_set_changed) update_cycles(&h->summary.pf_xen_emul[PF_XEN_EMUL_SET_CHANGED], h->arc_cycles); else update_cycles(&h->summary.pf_xen_emul[PF_XEN_EMUL_SET_UNCHANGED], h->arc_cycles); if(e->flag_set_flush) update_cycles(&h->summary.pf_xen_emul[PF_XEN_EMUL_SET_FLUSH], h->arc_cycles); if(e->flag_set_error) update_cycles(&h->summary.pf_xen_emul[PF_XEN_EMUL_SET_ERROR], h->arc_cycles); if(e->flag_promote) update_cycles(&h->summary.pf_xen_emul[PF_XEN_EMUL_PROMOTE], h->arc_cycles); if(e->flag_demote) update_cycles(&h->summary.pf_xen_emul[PF_XEN_EMUL_DEMOTE], h->arc_cycles); /* more summary info */ hvm_update_short_summary(h, HVM_SHORT_SUMMARY_EMULATE); } if(opt.scatterplot_unpin_promote) { if(e->flag_early_unshadow) scatterplot_vs_time(h->exit_tsc, -10); if(h->prealloc_unpin) scatterplot_vs_time(h->exit_tsc, 0); if(e->flag_promote) { if(opt.with_cr3_enumeration) { if(h->v->cr3.data) scatterplot_vs_time(h->exit_tsc, h->v->cr3.data->cr3_id); } else scatterplot_vs_time(h->exit_tsc, 2); } } } void shadow_emulate_process(struct record_info *ri, struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; union { /* for PAE, guest_l1e may be 64 while guest_va may be 32; so put it first for alignment sake. */ struct { unsigned gl1e, write_val; unsigned va; unsigned flags:29, emulation_count:3; } gpl2; struct { unsigned long long gl1e, write_val; unsigned va; unsigned flags:29, emulation_count:3; } gpl3; struct { unsigned long long gl1e, write_val; unsigned long long va; unsigned flags:29, emulation_count:3; } gpl4; } *r = (typeof(r))ri->d; union shadow_event sevt = { .event = ri->event }; int rec_gpl = sevt.paging_levels + 2; if ( rec_gpl != h->v->guest_paging_levels ) { fprintf(warn, "%s: record paging levels %d, guest paging levels %d. Switching.\n", __func__, rec_gpl, h->v->guest_paging_levels); h->v->guest_paging_levels = rec_gpl; } /* Fill in extended information */ switch(rec_gpl) { case 2: if(sizeof(r->gpl2) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl2), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = r->gpl2.va; e->flags = r->gpl2.flags; e->gl1e = r->gpl2.gl1e; e->wval = r->gpl2.write_val; break; case 3: if(sizeof(r->gpl3) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl3), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = r->gpl3.va; e->flags = r->gpl3.flags; e->gl1e = r->gpl3.gl1e; e->wval = r->gpl3.write_val; break; case 4: if(sizeof(r->gpl4) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl4), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = r->gpl4.va; e->flags = r->gpl4.flags; e->gl1e = r->gpl4.gl1e; e->wval = r->gpl4.write_val; break; } pf_preprocess(e,rec_gpl); if(opt.dump_all) printf("]%s emulate va %llx gl1e %8llx wval %8llx flags %s(%x) pt_level %d corr %8llx\n", ri->dump_header, e->va, e->gl1e, e->wval, flag_string(e), e->flags, e->pt_level, e->corresponding_va); if ( hvm_set_postprocess(h, shadow_emulate_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } struct shadow_emulate_other { unsigned long long gfn, va; }; #define SHADOW_OTHER_LOGS_GFN_NOT_GMFN 1 void shadow_parse_other(struct record_info *ri, struct shadow_emulate_other *o, struct hvm_data *h) { union { /* for PAE, guest_l1e may be 64 while guest_va may be 32; so put it first for alignment sake. */ #if SHADOW_OTHER_LOGS_GFN_NOT_GMFN /* D'OH! Accidentally used mfn_t in the struct, so gmfns are always 64-bit... :-/ */ struct { unsigned int gfn, va; } gpl2; #endif struct { unsigned long long gfn; unsigned int va; } gpl3; struct { unsigned long long gfn, va; } gpl4; } *r = (typeof(r))ri->d; union shadow_event sevt = { .event = ri->event }; int rec_gpl = sevt.paging_levels + 2; if ( rec_gpl != h->v->guest_paging_levels ) { fprintf(warn, "%s: record paging levels %d, guest paging levels %d. Switching.\n", __func__, rec_gpl, h->v->guest_paging_levels); h->v->guest_paging_levels = rec_gpl; } switch(rec_gpl) { #if SHADOW_OTHER_LOGS_GFN_NOT_GMFN case 2: if(sizeof(r->gpl2) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl2), rec_gpl, ri->extra_words * 4); error(ERR_RECORD, ri); return; } o->va = r->gpl2.va; o->gfn = r->gpl2.gfn; break; #else case 2: /* FALLTHRU */ #endif case 3: if(sizeof(r->gpl3) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl3), rec_gpl, ri->extra_words * 4); error(ERR_RECORD, ri); return; } o->va = r->gpl3.va; o->gfn = r->gpl3.gfn; break; case 4: if(sizeof(r->gpl4) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl4), rec_gpl, ri->extra_words * 4); error(ERR_RECORD, ri); return; } o->va = r->gpl4.va; o->gfn = r->gpl4.gfn; break; } } #if 0 void shadow_unsync_postprocess(struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; if(h->resyncs > 1) fprintf(warn, "Strange, %d resyncs for an unsync!\n", h->resyncs); if(opt.summary_info) { update_cycles(&h->summary.pf_xen[PF_XEN_EMULATE_UNSYNC], h->arc_cycles); if(h->resyncs <= 1) update_cycles(&h->summary.pf_xen_unsync[h->resyncs], h->arc_cycles); } } void shadow_unsync_process(struct record_info *ri, struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; struct shadow_emulate_other r; shadow_parse_other(ri, &r, h); e->gmfn = r.gmfn; e->va = r.va; pf_preprocess(e, h->v->guest_paging_levels); if(opt.dump_all) printf("]%s shadow unsync gmfn %llx va %llx pt_level %d corr %llx\n", ri->dump_header, e->gmfn, e->va, e->pt_level, e->corresponding_va); if ( hvm_set_postprocess(h, shadow_unsync_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } #endif void shadow_fault_generic_postprocess(struct hvm_data *h); void shadow_emulate_other_process(struct record_info *ri, struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; struct shadow_emulate_other r; union shadow_event sevt = { .event = ri->event }; shadow_parse_other(ri, &r, h); e->gfn = r.gfn; e->va = r.va; e->pf_case = sevt.minor; pf_preprocess(e, h->v->guest_paging_levels); if(opt.dump_all) printf("]%s shadow %s gfn %llx va %llx\n", ri->dump_header, pf_xen_name[sevt.minor], e->gfn, e->va); if ( hvm_set_postprocess(h, shadow_fault_generic_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } void shadow_fixup_postprocess(struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; if ( opt.summary_info ) { update_cycles(&h->summary.pf_xen[PF_XEN_FIXUP], h->arc_cycles); if(h->prealloc_unpin) { update_cycles(&h->summary.pf_xen_fixup[PF_XEN_FIXUP_PREALLOC_UNPIN], h->arc_cycles); } if(e->flag_unsync) { update_cycles(&h->summary.pf_xen_fixup[PF_XEN_FIXUP_UNSYNC], h->arc_cycles); if(h->resyncs < PF_XEN_FIXUP_UNSYNC_RESYNC_MAX) update_cycles(&h->summary.pf_xen_fixup_unsync_resync[h->resyncs], h->arc_cycles); else update_cycles(&h->summary.pf_xen_fixup_unsync_resync[PF_XEN_FIXUP_UNSYNC_RESYNC_MAX], h->arc_cycles); } if(e->flag_oos_fixup_add) update_cycles(&h->summary.pf_xen_fixup[PF_XEN_FIXUP_OOS_ADD], h->arc_cycles); if(e->flag_oos_fixup_evict) update_cycles(&h->summary.pf_xen_fixup[PF_XEN_FIXUP_OOS_EVICT], h->arc_cycles); if(e->flag_promote) update_cycles(&h->summary.pf_xen_fixup[PF_XEN_FIXUP_PROMOTE], h->arc_cycles); if(e->flag_wrmap) { update_cycles(&h->summary.pf_xen_fixup[PF_XEN_FIXUP_WRMAP], h->arc_cycles); if(e->flag_wrmap_brute_force || h->wrmap_bf) update_cycles(&h->summary.pf_xen_fixup[PF_XEN_FIXUP_BRUTE_FORCE], h->arc_cycles); } else if(e->flag_wrmap_brute_force || h->wrmap_bf) { fprintf(warn, "Strange: wrmap_bf but not wrmap!\n"); } if(!(e->flag_promote || h->prealloc_unpin || e->flag_unsync)) update_cycles(&h->summary.pf_xen_fixup[PF_XEN_FIXUP_UPDATE_ONLY], h->arc_cycles); /* more summary info */ if(e->flag_unsync) hvm_update_short_summary(h, HVM_SHORT_SUMMARY_UNSYNC); else hvm_update_short_summary(h, HVM_SHORT_SUMMARY_FIXUP); } if(opt.scatterplot_unpin_promote) { if(h->prealloc_unpin) scatterplot_vs_time(h->exit_tsc, 0); if(e->flag_promote) { if(opt.with_cr3_enumeration) { if(h->v->cr3.data) scatterplot_vs_time(h->exit_tsc, h->v->cr3.data->cr3_id); } else scatterplot_vs_time(h->exit_tsc, 2); } } } void shadow_fixup_process(struct record_info *ri, struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; union { /* for PAE, guest_l1e may be 64 while guest_va may be 32; so put it first for alignment sake. */ struct { unsigned int gl1e, va, flags; } gpl2; struct { unsigned long long gl1e; unsigned int va, flags; } gpl3; struct { unsigned long long gl1e, va; unsigned int flags; } gpl4; } *r = (typeof(r))ri->d; union shadow_event sevt = { .event = ri->event }; int rec_gpl = sevt.paging_levels + 2; if ( rec_gpl != h->v->guest_paging_levels ) { fprintf(warn, "%s: record paging levels %d, guest paging levels %d. Switching.\n", __func__, rec_gpl, h->v->guest_paging_levels); h->v->guest_paging_levels = rec_gpl; } switch(rec_gpl) { case 2: if(sizeof(r->gpl2) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl2), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = r->gpl2.va; e->flags = r->gpl2.flags; e->gl1e = r->gpl2.gl1e; break; case 3: if(sizeof(r->gpl3) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl3), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = r->gpl3.va; e->flags = r->gpl3.flags; e->gl1e = r->gpl3.gl1e; break; case 4: if(sizeof(r->gpl4) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl4), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = r->gpl4.va; e->flags = r->gpl4.flags; e->gl1e = r->gpl4.gl1e; break; } pf_preprocess(e,rec_gpl); if(opt.dump_all) { if ( e->flag_unsync ) printf("]%s fixup:unsync va %llx gl1e %llx corr %llx flags (%x)%s\n", ri->dump_header, e->va, e->gl1e, e->corresponding_va, e->flags, flag_string(e)); else printf("]%s fixup va %llx gl1e %llx flags (%x)%s\n", ri->dump_header, e->va, e->gl1e, e->flags, flag_string(e)); } if ( hvm_set_postprocess(h, shadow_fixup_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } void shadow_mmio_postprocess(struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; if ( opt.summary_info ) { if(e->pf_case) update_cycles(&h->summary.pf_xen[e->pf_case], h->arc_cycles); else fprintf(warn, "Strange, pf_case 0!\n"); hvm_update_short_summary(h, HVM_SHORT_SUMMARY_MMIO); } if(opt.with_mmio_enumeration) enumerate_mmio(h); } void shadow_mmio_process(struct record_info *ri, struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; struct mmio_info *m = &h->inflight.mmio; union { /* for PAE, guest_l1e may be 64 while guest_va may be 32; so put it first for alignment sake. */ struct { unsigned int va; } gpl2; struct { unsigned long long va; } gpl4; } *r = (typeof(r))ri->d; union shadow_event sevt = { .event = ri->event }; int rec_gpl = sevt.paging_levels + 2; if ( rec_gpl != h->v->guest_paging_levels ) { fprintf(warn, "%s: record paging levels %d, guest paging levels %d. Switching.\n", __func__, rec_gpl, h->v->guest_paging_levels); h->v->guest_paging_levels = rec_gpl; } switch(rec_gpl) { case 2: case 3: if(sizeof(r->gpl2) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl2), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = m->va = r->gpl2.va; break; case 4: if(sizeof(r->gpl4) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl4), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = m->va = r->gpl4.va; break; } if(opt.dump_all) printf("]%s %smmio va %llx\n", ri->dump_header, (e->pf_case==PF_XEN_FAST_MMIO)?"fast ":"", e->va); if ( hvm_set_postprocess(h, shadow_mmio_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } void shadow_propagate_postprocess(struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; if ( opt.summary_info ) { if(e->pf_case) update_cycles(&h->summary.pf_xen[e->pf_case], h->arc_cycles); else fprintf(warn, "Strange, pf_case 0!\n"); hvm_update_short_summary(h, HVM_SHORT_SUMMARY_PROPAGATE); } } void shadow_propagate_process(struct record_info *ri, struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; union { /* for PAE, guest_l1e may be 64 while guest_va may be 32; so put it first for alignment sake. */ struct { unsigned int gl1e, va, flags; } gpl2; struct { unsigned long long gl1e; unsigned int va, flags; } gpl3; struct { unsigned long long gl1e, va; unsigned int flags; } gpl4; } *r = (typeof(r))ri->d; union shadow_event sevt = { .event = ri->event }; int rec_gpl = sevt.paging_levels + 2; if ( rec_gpl != h->v->guest_paging_levels ) { fprintf(warn, "%s: record paging levels %d, guest paging levels %d. Switching.\n", __func__, rec_gpl, h->v->guest_paging_levels); h->v->guest_paging_levels = rec_gpl; } switch(rec_gpl) { case 2: if(sizeof(r->gpl2) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl2), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = r->gpl2.va; e->flags = r->gpl2.flags; e->gl1e = r->gpl2.gl1e; break; case 3: if(sizeof(r->gpl3) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl3), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = r->gpl3.va; e->flags = r->gpl3.flags; e->gl1e = r->gpl3.gl1e; break; case 4: if(sizeof(r->gpl4) != ri->extra_words * 4) { fprintf(warn, "%s: expected %zd bytes for %d-level guest, got %d!\n", __func__, sizeof(r->gpl4), h->v->guest_paging_levels, ri->extra_words * 4); error(ERR_RECORD, ri); return; } e->va = r->gpl4.va; e->flags = r->gpl4.flags; e->gl1e = r->gpl4.gl1e; break; } if(opt.dump_all) printf("]%s propagate va %llx gl1e %llx flags (%x)%s\n", ri->dump_header, e->va, e->gl1e, e->flags, flag_string(e)); if ( hvm_set_postprocess(h, shadow_propagate_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } void shadow_fault_generic_dump(unsigned int event, uint32_t *d, char *prefix, char * dump_header) { char *evt_string, evt_number[10]; union shadow_event sevt = { .event = event }; int i; if(sevt.minor < PF_XEN_MAX && pf_xen_name[sevt.minor]) { evt_string = pf_xen_name[sevt.minor]; } else { snprintf(evt_number, 10, "%d", sevt.minor); evt_string = evt_number; } printf("%s%s shadow %s gl %d [", prefix, dump_header, evt_string, sevt.paging_levels); for(i=0; i<4; i++) { printf(" %x", d[i]); } printf(" ]\n"); } void shadow_fault_generic_postprocess(struct hvm_data *h) { struct pf_xen_extra *e = &h->inflight.pf_xen; if ( e->pf_case < PF_XEN_NOT_SHADOW || e->pf_case > PF_XEN_LAST_FAULT ) { fprintf(warn, "%s: Strange, unexpected case %d\n", __func__, e->pf_case); return; } if(opt.summary_info) { update_cycles(&h->summary.pf_xen[e->pf_case], h->arc_cycles); hvm_update_short_summary(h, HVM_SHORT_SUMMARY_PROPAGATE); } } void shadow_fault_generic_process(struct record_info *ri, struct hvm_data *h) { union shadow_event sevt = { .event = ri->event }; /* pf-case traces, vs others */ h->inflight.generic.event = ri->event; bcopy(ri->d, h->inflight.generic.d, sizeof(unsigned int) * 4); if(opt.dump_all) shadow_fault_generic_dump(h->inflight.generic.event, h->inflight.generic.d, "]", ri->dump_header); h->inflight.pf_xen.pf_case = sevt.minor; if ( hvm_set_postprocess(h, shadow_fault_generic_postprocess) ) fprintf(warn, "%s: Strange, postprocess already set\n", __func__); } void shadow_resync_process(struct record_info *ri, struct hvm_data *h) { struct { unsigned long long gfn; } *r = (typeof(r))ri->d; if(opt.dump_all) printf(" %s oos resync %s gfn %llx\n", ri->dump_header, (ri->event == TRC_SHADOW_RESYNC_FULL)?"full":"only", r->gfn); h->resyncs++; } void shadow_prealloc_unpin_process(struct record_info *ri, struct hvm_data *h) { struct { unsigned long long gfn; } *r = (typeof(r))ri->d; if(opt.dump_all) printf(" %s prealloc-unpin gfn %llx\n", ri->dump_header, r->gfn); if(h->prealloc_unpin) fprintf(warn, "Strange, more than one prealloc_unpin per arc!\n"); h->prealloc_unpin = 1; if(opt.with_cr3_enumeration) cr3_prealloc_unpin(h->v, r->gfn); } void shadow_wrmap_bf_process(struct record_info *ri, struct hvm_data *h) { struct { unsigned long long gfn; } *r = (typeof(r))ri->d; if(opt.dump_all) printf(" %s wrmap-bf gfn %llx\n", ri->dump_header, r->gfn); h->wrmap_bf = 1; } void shadow_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct hvm_data *h; union shadow_event sevt = { .event = ri->event }; int gpl = sevt.paging_levels + 2; assert(p->current); if(vcpu_set_data_type(p->current, VCPU_DATA_HVM)) return; h = &p->current->hvm; if(!h->init || !h->vmexit_valid) { if(opt.dump_all) shadow_fault_generic_dump(ri->event, ri->d, "!", ri->dump_header); return; } if(sevt.minor <= PF_XEN_NOT_SHADOW) { if(p->current->guest_paging_levels != gpl) { fprintf(warn, "%s: Changing guest paging levels to %d\n", __func__, gpl); p->current->guest_paging_levels = gpl; } } if(sevt.minor <= PF_XEN_LAST_FAULT) { h->inflight.pf_xen.pf_case = sevt.minor; if(opt.summary) { hvm_set_summary_handler(h, hvm_pf_xen_summary, NULL); } } /* FIXME - mask out paging levels */ switch(sevt.minor) { case PF_XEN_NOT_SHADOW: shadow_propagate_process(ri, h); break; case PF_XEN_EMULATE: shadow_emulate_process(ri, h); break; case PF_XEN_FIXUP: shadow_fixup_process(ri, h); break; case PF_XEN_MMIO: case PF_XEN_FAST_MMIO: shadow_mmio_process(ri, h); break; case PF_XEN_EMULATE_UNSHADOW_USER: case PF_XEN_EMULATE_UNSHADOW_EVTINJ: case PF_XEN_EMULATE_UNSHADOW_UNHANDLED: shadow_emulate_other_process(ri, h); break; #if 0 case PF_XEN_EMULATE_UNSYNC: shadow_unsync_process(ri, h); break; #endif case SHADOW_RESYNC_FULL: case SHADOW_RESYNC_ONLY: shadow_resync_process(ri, h); break; case SHADOW_PREALLOC_UNPIN: shadow_prealloc_unpin_process(ri, h); break; case SHADOW_WRMAP_BF: shadow_wrmap_bf_process(ri, h); break; default: if(sevt.minor <= PF_XEN_LAST_FAULT) { shadow_fault_generic_process(ri, h); } else { warn_once("Warning: processing shadow as generic\n"); process_generic(ri); } break; } } /* ---- PV guests ---- */ union pv_event { unsigned event; struct { unsigned minor:8, x64:1, unused1:3, sub:4, main:12, unused:4; }; }; void pv_hypercall_process(struct record_info *ri, struct pv_data *pv) { union { struct { uint32_t eip, eax; } x32; struct { uint64_t eip; uint32_t eax; } x64; } * r = (typeof(r)) ri->d; union pv_event pevt = { .event = ri->event }; unsigned long long eip; unsigned int eax; if(pevt.x64) { eip = r->x64.eip; eax = r->x64.eax; } else { eip = r->x32.eip; eax = r->x32.eax; } if(opt.summary_info) { if(eax < PV_HYPERCALL_MAX) pv->hypercall_count[eax]++; } if(opt.dump_all) { if(eax < HYPERCALL_MAX) printf(" %s hypercall %2x (%s) eip %llx\n", ri->dump_header, eax, hypercall_name[eax], eip); else printf(" %s hypercall %x eip %llx\n", ri->dump_header, eax, eip); } } void pv_trap_process(struct record_info *ri, struct pv_data *pv) { union { struct { unsigned int eip; unsigned trapnr:15, use_error_code:1, error_code:16; } x32; struct { unsigned long long eip; unsigned trapnr:15, use_error_code:1, error_code:16; } x64; } * r = (typeof(r)) ri->d; union pv_event pevt = { .event = ri->event }; unsigned long long eip; unsigned trapnr, use_ec, ec; if(pevt.x64) { eip = r->x64.eip; trapnr = r->x64.trapnr; use_ec = r->x64.use_error_code; ec = r->x64.error_code; } else { eip = r->x32.eip; trapnr = r->x32.trapnr; use_ec = r->x32.use_error_code; ec = r->x32.error_code; } if(opt.summary_info) { if(trapnr < PV_TRAP_MAX) pv->trap_count[trapnr]++; } if(opt.dump_all) { printf(" %s trap %x eip %llx", ri->dump_header, trapnr, eip); if(use_ec) printf(" ec %x\n", ec); else printf("\n"); } } void pv_ptwr_emulation_process(struct record_info *ri, struct pv_data *pv) { union pv_event pevt = { .event = ri->event }; union { /* gpl2 is deprecated */ struct { unsigned long long pte; unsigned int addr, eip; } gpl3; struct { unsigned long long pte; unsigned long long addr, eip; } gpl4; } *r = (typeof(r))ri->d; struct { unsigned long long pte, addr, eip; } e; switch ( pevt.minor ) { case PV_PTWR_EMULATION_PAE: if ( pevt.x64 ) { fprintf(warn, "Strange: PV_PTWR_EMULATION, but x64! %x\n", ri->event); error(ERR_RECORD, ri); } e.pte = r->gpl3.pte; e.addr = r->gpl3.addr; e.eip = r->gpl3.eip; break; case PV_PTWR_EMULATION: if ( !pevt.x64 ) { fprintf(warn, "Strange: PV_PTWR_EMULATION, but !x64! %x\n", ri->event); error(ERR_RECORD, ri); } e.pte = r->gpl4.pte; e.addr = r->gpl4.addr; e.eip = r->gpl4.eip; break; default: fprintf(warn, "ERROR: Unknown PV_PTRW minor type %d!\n", pevt.minor); error(ERR_RECORD, ri); return; } if ( opt.dump_all ) { printf(" %s ptwr l1e %llx eip %llx addr %llx\n", ri->dump_header, e.pte, e.eip, e.addr); } } void pv_generic_process(struct record_info *ri, struct pv_data *pv) { union pv_event pevt = { .event = ri->event }; if ( opt.dump_all ) { if(pevt.minor < PV_MAX && pv_name[pevt.minor]) printf(" %s %s", ri->dump_header, pv_name[pevt.minor]); else printf(" %s PV-%d ", ri->dump_header, pevt.minor); if (ri->extra_words) { int i; printf("[ "); for(i=0; iextra_words; i++) { printf("%x ", (unsigned)ri->d[i]); } printf("]"); } printf("\n"); } } void pv_summary(struct pv_data *pv) { int i, j; if(!pv->summary_info) return; printf("PV events:\n"); for(i=0; icount[i]; if (i == PV_HYPERCALL_V2) count += pv->count[PV_HYPERCALL_SUBCALL]; if (count == 0) continue; printf(" %s %d\n", pv_name[i], count); switch(i) { case PV_HYPERCALL: case PV_HYPERCALL_V2: for(j=0; jhypercall_count[j]) printf(" %-29s[%2d]: %6d\n", hypercall_name[j], j, pv->hypercall_count[j]); } break; case PV_TRAP: for(j=0; jtrap_count[j]) printf(" [%d] %d\n", j, pv->trap_count[j]); } break; } } } static const char *grant_table_op_str[] = { "map_grant_ref", "unmap_grant_ref", "setup_table", "dump_table", "transfer", "copy", "query_size", "unmap_and_replace", "set_version", "get_status_frames", "get_version", "swap_grant_ref", }; static const char *vcpu_op_str[] = { "initialise", "up", "down", "is_up", "get_runstate_info", "register_runstate_memory_area", "set_periodic_timer", "stop_periodic_timer", "set_singleshot_timer", "stop_singleshot_timer", "register_vcpu_info", "send_nmi", "get_physid", "register_vcpu_time_memory_area", }; static const char *sched_op_str[] = { "yield", "block", "shutdown", "poll", "remote_shutdown", "shutdown_code", "watchdog", }; static const char *cmd_to_str(const char *strings[], size_t n, uint32_t cmd) { static char buf[32]; if (cmd < n) return strings[cmd]; snprintf(buf, sizeof(buf), "unknown (%d)", cmd); return buf; } #define CMD_TO_STR(op) \ static const char * op ## _to_str(uint32_t cmd) { \ return cmd_to_str(op ## _str, ARRAY_SIZE(op ## _str), cmd); \ } CMD_TO_STR(grant_table_op); CMD_TO_STR(vcpu_op); CMD_TO_STR(sched_op); void pv_hypercall_gather_args(const struct record_info *ri, uint64_t *args) { int i, word; /* Missing arguments are zeroed. */ memset(args, 0, 6 * sizeof(uint64_t)); for (i = 0, word = 1; i < 6 && word < ri->extra_words; i++) { int present = pv_hypercall_arg_present(ri, i); switch (present) { case ARG_32BIT: args[i] = ri->d[word]; break; case ARG_64BIT: args[i] = ((uint64_t)ri->d[word + 1] << 32) | ri->d[word]; break; } /* Skip over any words for this argument. */ word += present; } } static void pv_hypercall_print_args(const struct record_info *ri) { int i, word; for (i = 0, word = 1; i < 6 && word < ri->extra_words; i++) { int present = pv_hypercall_arg_present(ri, i); switch (present) { case ARG_MISSING: printf(" ??"); break; case ARG_32BIT: printf(" %08x", ri->d[word]); break; case ARG_64BIT: printf(" %016"PRIu64"", ((uint64_t)ri->d[word + 1] << 32) | ri->d[word]); break; } word += present; } } void pv_hypercall_v2_process(struct record_info *ri, struct pv_data *pv, const char *indent) { int op = pv_hypercall_op(ri); if(opt.summary_info) { if(op < PV_HYPERCALL_MAX) pv->hypercall_count[op]++; } if(opt.dump_all) { uint64_t args[6]; if(op < HYPERCALL_MAX) printf(" %s%s hypercall %2x (%s)", ri->dump_header, indent, op, hypercall_name[op]); else printf(" %s%s hypercall %2x", ri->dump_header, indent, op); switch(op) { case HYPERCALL_mmu_update: pv_hypercall_gather_args(ri, args); printf(" %d updates%s", (uint32_t)args[1] & ~MMU_UPDATE_PREEMPTED, (args[1] & MMU_UPDATE_PREEMPTED) ? " (preempted)" : ""); break; case HYPERCALL_multicall: pv_hypercall_gather_args(ri, args); printf(" %d calls", (uint32_t)args[1]); break; case HYPERCALL_grant_table_op: pv_hypercall_gather_args(ri, args); printf(" %s %d ops", grant_table_op_to_str(args[0]), (uint32_t)args[2]); break; case HYPERCALL_vcpu_op: pv_hypercall_gather_args(ri, args); printf(" %s vcpu %d", vcpu_op_to_str(args[0]), (uint32_t)args[1]); break; case HYPERCALL_mmuext_op: pv_hypercall_gather_args(ri, args); printf(" %d ops", (uint32_t)args[1]); break; case HYPERCALL_sched_op: pv_hypercall_gather_args(ri, args); printf(" %s", sched_op_to_str(args[0])); break; default: pv_hypercall_print_args(ri); break; } printf("\n"); } } void pv_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct vcpu_data *v = p->current; struct pv_data *pv = &v->pv; union pv_event pevt = { .event = ri->event }; if(vcpu_set_data_type(p->current, VCPU_DATA_PV)) return; if(opt.summary_info) { pv->summary_info=1; if(pevt.minor == PV_PTWR_EMULATION_PAE) pv->count[PV_PTWR_EMULATION]++; else pv->count[pevt.minor]++; } switch(pevt.minor) { case PV_HYPERCALL: pv_hypercall_process(ri, pv); break; case PV_TRAP: pv_trap_process(ri, pv); break; case PV_PTWR_EMULATION: case PV_PTWR_EMULATION_PAE: pv_ptwr_emulation_process(ri, pv); break; case PV_HYPERCALL_V2: pv_hypercall_v2_process(ri, pv, ""); break; case PV_HYPERCALL_SUBCALL: pv_hypercall_v2_process(ri, pv, " "); break; default: pv_generic_process(ri, pv); break; } } /* ---- Schedule ---- */ struct vcpu_data * vcpu_create(struct domain_data *d, int vid) { struct vcpu_data *v; assert(d->vcpu[vid] == NULL); fprintf(warn, "Creating vcpu %d for dom %d\n", vid, d->did); if((v=malloc(sizeof(*v)))==NULL) { fprintf(stderr, "%s: malloc %zd failed!\n", __func__, sizeof(*d)); error(ERR_SYSTEM, NULL); } bzero(v, sizeof(*v)); v->vid = vid; v->d = d; v->p = NULL; v->runstate.state = RUNSTATE_INIT; v->runstate.last_oldstate.wrong = RUNSTATE_INIT; d->vcpu[vid] = v; assert(v == v->d->vcpu[v->vid]); if(vid > d->max_vid) d->max_vid = vid; return v; } /* Called by both domain_create and sched_default_domain_init */ void domain_init(struct domain_data *d, int did) { bzero(d, sizeof(*d)); d->did = did; d->next = NULL; if(opt.interval.check == INTERVAL_CHECK_DOMAIN) interval_domain_value_check(d); } struct domain_data * domain_create(int did) { struct domain_data *d; fprintf(warn, "Creating domain %d\n", did); if((d=malloc(sizeof(*d)))==NULL) { fprintf(stderr, "%s: malloc %zd failed!\n", __func__, sizeof(*d)); error(ERR_SYSTEM, NULL); } /* Initialize domain & vcpus */ domain_init(d, did); return d; } struct domain_data * domain_find(int did) { struct domain_data *d, *n, **q; /* Look for domain, keeping track of the last pointer so we can add a domain if we need to. */ for ( d = domain_list, q=&domain_list ; d && (d->did < did) ; q = &d->next, d=d->next ) ; if(d && d->did == did) return d; /* Make a new domain */ n = domain_create(did); /* Insert it into the list */ n->next = d; *q = n; return n; } struct vcpu_data * vcpu_find(int did, int vid) { struct domain_data *d; struct vcpu_data *v; d = domain_find(did); v = d->vcpu[vid]; if(!v) v = vcpu_create(d, vid); return v; } void pcpu_runstate_update(struct pcpu_info *p, tsc_t tsc) { if ( p->time.tsc ) { if ( p->current->d->did == IDLE_DOMAIN ) update_cycles(&p->time.idle, tsc - p->time.tsc); else update_cycles(&p->time.running, tsc - p->time.tsc); p->time.tsc = 0; } } void vcpu_prev_update(struct pcpu_info *p, struct vcpu_data *prev, tsc_t tsc, int new_runstate) { assert(prev == prev->d->vcpu[prev->vid]); if(prev->p != p) { fprintf(warn, "Strange, sched_switch on pcpu %d, prev->pcpu %d!\n", p->pid, prev->p->pid); prev->runstate.tsc = 0; goto set; } //assert(p->current); if ( !p->current ) { fprintf(warn, "%s: FATAL: p->current NULL!\n", __func__); error(ERR_ASSERT, NULL); } if(p->current != prev) { fprintf(warn, "Strange, sched_switch prev d%dv%d, pcpu %d current d%dv%d!\n", prev->d->did, prev->vid, p->pid, p->current->d->did, p->current->vid); prev->runstate.tsc = 0; goto set; } if(prev->runstate.state != RUNSTATE_RUNNING) { fprintf(warn, "Strange, prev d%dv%d not running!\n", prev->d->did, prev->vid); prev->runstate.tsc = 0; goto set; } set: pcpu_runstate_update(p, tsc); p->current = NULL; pcpu_string_draw(p); runstate_update(prev, new_runstate, tsc); } void vcpu_next_update(struct pcpu_info *p, struct vcpu_data *next, tsc_t tsc) { assert(next == next->d->vcpu[next->vid]); //assert(p->current == NULL); if ( p->current != NULL ) { if ( p->lost_record.seen_valid_schedule == 0 ) { fprintf(warn, "%s: p->current non-NULL, but seen_valid_schedule 0. Ignoring.\n", __func__); runstate_update(p->current, RUNSTATE_LOST, tsc); p->current = NULL; } else { fprintf(warn, "%s: FATAL: p->current not NULL! (d%dv%d, runstate %s)\n", __func__, p->current->d->did, p->current->vid, runstate_name[p->current->runstate.state]); error(ERR_ASSERT, NULL); } } if(next->activated) { /* We may get lost records at start-of-day, so ignore setting runstate of default vcpus */ if(next->runstate.state == RUNSTATE_RUNNING && next->d->did != DEFAULT_DOMAIN) { fprintf(warn, "Strange, next d%dv%d already running on proc %d!\n", next->d->did, next->vid, next->p->pid); next->runstate.tsc = 0; } /* If we're moving from one pcpu to another, record change & update tsc */ if(next->p != p) { if(next->pcpu_tsc) { update_cycles(&next->cpu_affinity_all, tsc - next->pcpu_tsc); update_cycles(&next->cpu_affinity_pcpu[p->pid], tsc - next->pcpu_tsc); } next->pcpu_tsc = tsc; } } else { next->guest_paging_levels = opt.default_guest_paging_levels; next->activated = 1; next->pcpu_tsc = tsc; } runstate_update(next, RUNSTATE_RUNNING, tsc); if ( opt.scatterplot_pcpu && next->d->did != IDLE_DOMAIN && next->d->did != DEFAULT_DOMAIN ) { struct time_struct t; abs_cycles_to_time(tsc, &t); if ( next->p ) printf("%dv%d %u.%09u %d\n", next->d->did, next->vid, t.s, t.ns, next->p->pid); printf("%dv%d %u.%09u %d\n", next->d->did, next->vid, t.s, t.ns, p->pid); } next->p = p; p->current = next; pcpu_string_draw(p); p->time.tsc = tsc; p->lost_record.seen_valid_schedule = 1; } /* If current is the default domain, we're fixing up from something * like start-of-day. Update what we can. */ void vcpu_start(struct pcpu_info *p, struct vcpu_data *v) { /* If vcpus are created, or first show up, in a "dead zone", this will * fail. */ if( !p->current || p->current->d->did != DEFAULT_DOMAIN) { fprintf(stderr, "Strange, p->current not default domain!\n"); error(ERR_FILE, NULL); return; } if(!p->first_tsc) { fprintf(stderr, "Strange, p%d first_tsc 0!\n", p->pid); error(ERR_FILE, NULL); } if(p->first_tsc <= p->current->runstate.tsc) { fprintf(stderr, "Strange, first_tsc %llx < default_domain runstate tsc %llx!\n", p->first_tsc, p->current->runstate.tsc); error(ERR_FILE, NULL); } /* Change default domain to 'queued' */ runstate_update(p->current, RUNSTATE_QUEUED, p->first_tsc); /* FIXME: Copy over data from the default domain this interval */ fprintf(warn, "Using first_tsc for d%dv%d (%lld cycles)\n", v->d->did, v->vid, p->last_tsc - p->first_tsc); /* Simulate the time since the first tsc */ runstate_update(v, RUNSTATE_RUNNING, p->first_tsc); p->time.tsc = p->first_tsc; p->current = v; pcpu_string_draw(p); v->p = p; } void sched_runstate_process(struct pcpu_info *p) { enum { CHANGE=0, CONTINUE } type; struct vcpu_data *v; struct record_info *ri = &p->ri; struct { unsigned vcpu:16, dom:16; unsigned long long p1, p2; } __attribute__((packed)) * r = (typeof(r))ri->d; union { unsigned int event; struct { unsigned lo:4, new_runstate:4, old_runstate:4, sub:4, main:12, unused:4; }; } _sevt = { .event = ri->event }; struct { int new_runstate, old_runstate; } sevt; int perfctrs; struct last_oldstate_struct last_oldstate; switch(_sevt.lo) { case 1: type = CHANGE; sevt.new_runstate = _sevt.new_runstate; sevt.old_runstate = _sevt.old_runstate; break; case 2: type = CONTINUE; sevt.new_runstate = sevt.old_runstate = RUNSTATE_RUNNING; break; default: fprintf(warn, "FATAL: Unexpected runstate change type %d!\n", _sevt.lo); error(ERR_RECORD, NULL); return; } perfctrs = (ri->extra_words == 5); if(opt.dump_all) { if( perfctrs ) { printf(" %s %s {%lld,%lld} d%uv%u %s->%s\n", ri->dump_header, type?"runstate_continue":"runstate_change", r->p1, r->p2, r->dom, r->vcpu, runstate_name[sevt.old_runstate], runstate_name[sevt.new_runstate]); } else { printf(" %s %s d%uv%u %s->%s\n", ri->dump_header, type?"runstate_continue":"runstate_change", r->dom, r->vcpu, runstate_name[sevt.old_runstate], runstate_name[sevt.new_runstate]); } } /* Sanity check: expected transitions */ if ( type == CHANGE ) { if( (sevt.new_runstate == RUNSTATE_RUNNING && sevt.old_runstate != RUNSTATE_RUNNABLE) || (sevt.new_runstate == RUNSTATE_BLOCKED && sevt.old_runstate == RUNSTATE_RUNNABLE ) ) { fprintf(warn, "Strange, d%dv%d unexpected runstate transition %s->%s\n", r->dom, r->vcpu, runstate_name[sevt.old_runstate], runstate_name[sevt.new_runstate]); } } if(r->vcpu > MAX_CPUS) { fprintf(warn, "%s: vcpu %u > MAX_VCPUS %d!\n", __func__, r->vcpu, MAX_CPUS); return; } v = vcpu_find(r->dom, r->vcpu); /* We want last_oldstate reset every time; so copy the last one and use * that locally, clobbering the one in the vcpu struct. If it needs to * be reset, it will be reset below. */ last_oldstate = v->runstate.last_oldstate; v->runstate.last_oldstate.wrong = RUNSTATE_INIT; /* Close vmexits when the putative reason for blocking / &c stops. * This way, we don't account cpu contention to some other overhead. */ if(sevt.new_runstate == RUNSTATE_RUNNABLE && v->data_type == VCPU_DATA_HVM && v->hvm.vmexit_valid) { hvm_close_vmexit(&v->hvm, ri->tsc); } /* Track waking state */ if ( v->data_type == VCPU_DATA_HVM && v->runstate.state != RUNSTATE_LOST ) { if ( sevt.new_runstate == RUNSTATE_RUNNABLE && sevt.old_runstate == RUNSTATE_BLOCKED ) { /* Hmm... want to make sure we're not in some weird vmexit state... have to look later. */ if(opt.dump_all) printf(" [w2h] d%dv%d Setting waking\n", v->d->did, v->vid); v->hvm.w2h.waking = 1; } else if ( sevt.new_runstate != RUNSTATE_RUNNING || sevt.old_runstate != RUNSTATE_RUNNABLE ) { if( v->hvm.w2h.waking && sevt.old_runstate == RUNSTATE_RUNNING && sevt.new_runstate != RUNSTATE_OFFLINE ) { /* NB: This is printed a lot unnecessairly when there is TSC skew */ if ( sevt.old_runstate != v->runstate.state ) fprintf(warn, "Strange, unexpected waking transition for d%dv%d: %s -> %s\n", v->d->did, v->vid, runstate_name[sevt.old_runstate], runstate_name[sevt.new_runstate]); v->hvm.w2h.waking = 0; } /* Close wake-to-halt summary */ /* FIXME: Need to think about handling preemption. */ if (sevt.new_runstate == RUNSTATE_BLOCKED && sevt.old_runstate == RUNSTATE_RUNNING && v->hvm.w2h.interrupts ) { int i; for(i=0; ihvm.summary.guest_interrupt + i; tsc_t start_tsc = g->start_tsc; if(start_tsc) { tsc_t t = (start_tsc == 1) ? 0 : ri->tsc - start_tsc; if(opt.dump_all) printf(" [w2h] Halting vec %d is_wake %d time %lld\n", i, g->is_wake, t); if(opt.scatterplot_wake_to_halt && t && g->is_wake) scatterplot_vs_time(ri->tsc, t); if(opt.summary && t) { if(g->is_wake) { if(v->hvm.w2h.interrupts==1) update_cycles(&g->runtime[GUEST_INTERRUPT_CASE_WAKE_TO_HALT_ALONE], t); update_cycles(&g->runtime[GUEST_INTERRUPT_CASE_WAKE_TO_HALT_ANY], t); } else { update_cycles(&g->runtime[GUEST_INTERRUPT_CASE_INTERRUPT_TO_HALT], t); } } g->start_tsc = 0; g->is_wake = 0; } } v->hvm.w2h.interrupts = 0; v->hvm.w2h.vector = 0; } } } /* Sanity checks / tsc skew detection */ if( v->runstate.state != sevt.old_runstate && v->runstate.state != RUNSTATE_INIT ) { if(v->runstate.state == RUNSTATE_LOST) { if( sevt.new_runstate == RUNSTATE_RUNNING ) goto update; else if(opt.dump_all) fprintf(warn, "%s: d%dv%d in runstate lost, not updating to %s\n", __func__, v->d->did, v->vid, runstate_name[sevt.new_runstate]); goto no_update; } else if (last_oldstate.wrong == sevt.new_runstate && last_oldstate.actual == sevt.old_runstate) { tsc_t lag, old_offset; struct pcpu_info *p2; if(ri->tsc < last_oldstate.tsc) { fprintf(warn, "WARNING: new tsc %lld < detected runstate tsc %lld! Not updating\n", ri->tsc, last_oldstate.tsc); goto no_update; } p2 = P.pcpu + last_oldstate.pid; lag = ri->tsc - last_oldstate.tsc; old_offset = p2->tsc_skew.offset; cpumask_union(&p2->tsc_skew.downstream, &p->tsc_skew.downstream); cpumask_set(&p2->tsc_skew.downstream, p->pid); if(cpumask_isset(&p2->tsc_skew.downstream, p2->pid)) { if ( opt.tsc_loop_fatal ) { fprintf(stderr, "FATAL: tsc skew dependency loop detected!\n"); error(ERR_FILE, NULL); } else { int i; fprintf(warn, "Tsc skew dependency loop detected! Resetting...\n"); for ( i=0; i<=P.max_active_pcpu; i++) { struct pcpu_info *p = P.pcpu + i; p->tsc_skew.offset = 0; cpumask_init(&p->tsc_skew.downstream); } goto no_update; } } p2->tsc_skew.offset += lag * 2; fprintf(warn, "TSC skew detected p%d->p%d, %lld cycles. Changing p%d offset from %lld to %lld\n", p->pid, p2->pid, lag, p2->pid, old_offset, p2->tsc_skew.offset); goto no_update; } else { fprintf(warn, "runstate_change old_runstate %s, d%dv%d runstate %s. Possible tsc skew.\n", runstate_name[sevt.old_runstate], v->d->did, v->vid, runstate_name[v->runstate.state]); v->runstate.last_oldstate.wrong = sevt.old_runstate; v->runstate.last_oldstate.actual = v->runstate.state; v->runstate.last_oldstate.tsc = ri->tsc; v->runstate.last_oldstate.pid = p->pid; if ( v->runstate.state == RUNSTATE_RUNNING ) { fprintf(warn, " Not updating.\n"); goto no_update; } goto update; } fprintf(stderr, "FATAL: Logic hole in %s\n", __func__); error(ERR_ASSERT, NULL); } update: /* Actually update the runstate. Special things to do if we're starting * or stopping actually running on a physical cpu. */ if ( type == CONTINUE ) { if( v->runstate.state == RUNSTATE_INIT ) { /* Start-of-day; account first tsc -> now to v */ vcpu_start(p, v); } else { /* Continue running. First, do some sanity checks */ if ( v->runstate.state == RUNSTATE_LOST ) { fprintf(warn, "WARNING: continue with d%dv%d in RUNSTATE_LOST. Resetting current.\n", v->d->did, v->vid); if ( p->current ) vcpu_prev_update(p, p->current, ri->tsc, RUNSTATE_LOST); vcpu_next_update(p, v, ri->tsc); } else if( v->runstate.state != RUNSTATE_RUNNING ) { /* This should never happen. */ fprintf(warn, "FATAL: sevt.old_runstate running, but d%dv%d runstate %s!\n", v->d->did, v->vid, runstate_name[v->runstate.state]); error(ERR_FILE, NULL); } else if ( v->p != p ) { fprintf(warn, "FATAL: continue on p%d, but d%dv%d p%d!\n", p->pid, v->d->did, v->vid, v->p ? v->p->pid : -1); error(ERR_FILE, NULL); } runstate_update(v, RUNSTATE_RUNNING, ri->tsc); } } else if ( sevt.old_runstate == RUNSTATE_RUNNING || v->runstate.state == RUNSTATE_RUNNING ) { /* * Cases: * old running, v running: * normal (prev update p, lost record check) * v running, old ! running: * tsc skew (prev update v->p, lost record check) * old running, v init: start-of-day (fake update, prev p, lost record) * old running, v !{running,init}: * # (should never happen) */ if( sevt.old_runstate == RUNSTATE_RUNNING ) { if( v->runstate.state == RUNSTATE_INIT ) { /* Start-of-day; account first tsc -> now to v */ vcpu_start(p, v); } else if( v->runstate.state != RUNSTATE_RUNNING && v->runstate.state != RUNSTATE_LOST ) { /* This should never happen. */ fprintf(warn, "FATAL: sevt.old_runstate running, but d%dv%d runstate %s!\n", v->d->did, v->vid, runstate_name[v->runstate.state]); error(ERR_FILE, NULL); } vcpu_prev_update(p, v, ri->tsc, sevt.new_runstate); } else { vcpu_prev_update(v->p, v, ri->tsc, sevt.new_runstate); } if(P.lost_cpus && v->d->did != IDLE_DOMAIN) { if(opt.dump_all) fprintf(warn, "%s: %d lost cpus, setting d%dv%d runstate to RUNSTATE_LOST\n", __func__, P.lost_cpus, v->d->did, v->vid); lose_vcpu(v, ri->tsc); } } else if ( sevt.new_runstate == RUNSTATE_RUNNING ) { if(perfctrs) { v->runstate.p1_start = r->p1; v->runstate.p2_start = r->p2; } vcpu_next_update(p, v, ri->tsc); } else if ( v->runstate.state != RUNSTATE_INIT ) { /* TSC skew at start-of-day is hard to deal with. Don't * bring a vcpu out of INIT until it's seen to be actually * running somewhere. */ runstate_update(v, sevt.new_runstate, ri->tsc); } no_update: return; } void dump_sched_switch(struct record_info *ri) { struct { unsigned int prev_dom, prev_vcpu, next_dom, next_vcpu; } *r = (typeof(r))ri->d; printf(" %s sched_switch prev d%uv%u next d%uv%u\n", ri->dump_header, r->prev_dom, r->prev_vcpu, r->next_dom, r->next_vcpu); } void sched_switch_process(struct pcpu_info *p) { struct vcpu_data *prev, *next; struct record_info *ri = &p->ri; struct { unsigned int prev_dom, prev_vcpu, next_dom, next_vcpu; } * r = (typeof(r))ri->d; if(opt.dump_all) dump_sched_switch(ri); if(r->prev_vcpu > MAX_CPUS) { fprintf(warn, "%s: prev_vcpu %u > MAX_VCPUS %d!\n", __func__, r->prev_vcpu, MAX_CPUS); return; } if(r->next_vcpu > MAX_CPUS) { fprintf(warn, "%s: next_vcpu %u > MAX_VCPUS %d!\n", __func__, r->next_vcpu, MAX_CPUS); return; } prev = vcpu_find(r->prev_dom, r->prev_vcpu); next = vcpu_find(r->next_dom, r->next_vcpu); vcpu_prev_update(p, prev, ri->tsc, RUNSTATE_QUEUED); /* FIXME */ vcpu_next_update(p, next, ri->tsc); } void sched_default_vcpu_activate(struct pcpu_info *p) { struct vcpu_data *v = default_domain.vcpu[p->pid]; if(!v) v = vcpu_create(&default_domain, p->pid); assert(v == v->d->vcpu[v->vid]); v->activated = 1; v->guest_paging_levels = opt.default_guest_paging_levels; v->p = p; v->runstate.state = RUNSTATE_RUNNING; p->current = v; pcpu_string_draw(p); } void sched_default_domain_init(void) { struct domain_data *d = &default_domain; domain_init(d, DEFAULT_DOMAIN); } void runstate_clear(tsc_t * runstate_cycles) { int i; for(i=0; ipcpu_tsc ) { update_cycles(&v->cpu_affinity_all, P.f.last_tsc - v->pcpu_tsc); update_cycles(&v->cpu_affinity_pcpu[v->p->pid], P.f.last_tsc - v->pcpu_tsc); } printf(" Runstates:\n"); for(i=0; irunstates+i, desc); if ( i==RUNSTATE_RUNNABLE ) { int j; for(j=0; jrunnable_states+j, desc); } } } print_cpu_affinity(&v->cpu_affinity_all, " cpu affinity"); for ( i = 0; i < MAX_CPUS ; i++) { snprintf(desc,30, " [%d]", i); print_cpu_affinity(v->cpu_affinity_pcpu+i, desc); } } void sched_summary_domain(struct domain_data *d) { int i; char desc[30]; printf(" Runstates:\n"); for(i=0; irunstates+i, desc); } } void dump_sched_vcpu_action(struct record_info *ri, const char *action) { struct { unsigned int domid, vcpuid; } *r = (typeof(r))ri->d; printf(" %s %s d%uv%u\n", ri->dump_header, action, r->domid, r->vcpuid); } void sched_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; if(ri->evt.sub == 0xf) { switch(ri->event) { case TRC_SCHED_SWITCH: sched_switch_process(p); break; default: process_generic(&p->ri); } return; } if(ri->evt.sub == 1) { /* TRC_SCHED_MIN */ sched_runstate_process(p); } else if (ri->evt.sub == 8) { /* TRC_SCHED_VERBOSE */ switch(ri->event) { case TRC_SCHED_DOM_ADD: if(opt.dump_all) { struct { unsigned int domid; } *r = (typeof(r))ri->d; printf(" %s sched_init_domain d%u\n", ri->dump_header, r->domid); } break; case TRC_SCHED_DOM_REM: if(opt.dump_all) { struct { unsigned int domid, vcpuid; } *r = (typeof(r))ri->d; printf(" %s sched_destroy_domain d%u\n", ri->dump_header, r->domid); } break; case TRC_SCHED_SLEEP: if(opt.dump_all) dump_sched_vcpu_action(ri, "vcpu_sleep"); break; case TRC_SCHED_WAKE: if(opt.dump_all) dump_sched_vcpu_action(ri, "vcpu_wake"); break; case TRC_SCHED_YIELD: if(opt.dump_all) dump_sched_vcpu_action(ri, "vcpu_yield"); break; case TRC_SCHED_BLOCK: if(opt.dump_all) dump_sched_vcpu_action(ri, "vcpu_block"); break; case TRC_SCHED_SHUTDOWN: case TRC_SCHED_SHUTDOWN_CODE: if(opt.dump_all) { struct { unsigned int domid, vcpuid, reason; } *r = (typeof(r))ri->d; printf(" %s %s d%uv%u, reason = %u\n", ri->dump_header, ri->event == TRC_SCHED_SHUTDOWN ? "sched_shutdown" : "sched_shutdown_code", r->domid, r->vcpuid, r->reason); } break; case TRC_SCHED_ADJDOM: if(opt.dump_all) { struct { unsigned int domid; } *r = (typeof(r))ri->d; printf(" %s sched_adjust d%u\n", ri->dump_header, r->domid); } break; case TRC_SCHED_SWITCH: if(opt.dump_all) dump_sched_switch(ri); break; case TRC_SCHED_SWITCH_INFPREV: if(opt.dump_all) { struct { unsigned int domid, vcpuid, runtime; } *r = (typeof(r))ri->d; printf(" %s sched_switch prev d%uv%d, run for %u.%uus\n", ri->dump_header, r->domid, r->vcpuid, r->runtime / 1000, r->runtime % 1000); } break; case TRC_SCHED_SWITCH_INFNEXT: if(opt.dump_all) { struct { unsigned int domid, vcpuid, rsince; int slice; } *r = (typeof(r))ri->d; printf(" %s sched_switch next d%uv%u", ri->dump_header, r->domid, r->vcpuid); if ( r->rsince != 0 ) printf(", was runnable for %u.%uus", r->rsince / 1000, r->rsince % 1000); if ( r->slice > 0 ) printf(", next slice %u.%uus", r->slice / 1000, r->slice % 1000); printf("\n"); } break; case TRC_SCHED_SWITCH_INFCONT: if(opt.dump_all) { struct { unsigned int domid, vcpuid, rsince; int slice; } *r = (typeof(r))ri->d; printf(" %s sched_switch continue d%uv%u, run for %u.%uus", ri->dump_header, r->domid, r->vcpuid, r->rsince / 1000, r->rsince % 1000); if ( r->slice > 0 ) printf(", next slice %u.%uus", r->slice / 1000, r->slice % 1000); printf("\n"); } break; case TRC_SCHED_CTL: case TRC_SCHED_S_TIMER_FN: case TRC_SCHED_T_TIMER_FN: case TRC_SCHED_DOM_TIMER_FN: break; default: process_generic(&p->ri); } } else if(ri->evt.sub == 2) { /* TRC_SCHED_CLASS */ switch(ri->event) { /* CREDIT (TRC_CSCHED_xxx) */ case TRC_SCHED_CLASS_EVT(CSCHED, 1): /* SCHED_TASKLET */ if(opt.dump_all) printf(" %s csched:sched_tasklet\n", ri->dump_header); break; case TRC_SCHED_CLASS_EVT(CSCHED, 2): /* ACCOUNT_START */ case TRC_SCHED_CLASS_EVT(CSCHED, 3): /* ACCOUNT_STOP */ if(opt.dump_all) { struct { unsigned int domid, vcpuid, actv_cnt; } *r = (typeof(r))ri->d; printf(" %s csched:acct_%s d%uv%u, active_vcpus %u\n", ri->dump_header, ri->event == TRC_SCHED_CLASS_EVT(CSCHED, 2) ? "start" : "stop", r->domid, r->vcpuid, r->actv_cnt); } break; case TRC_SCHED_CLASS_EVT(CSCHED, 4): /* STOLEN_VCPU */ if(opt.dump_all) { struct { unsigned int peer_cpu, domid, vcpuid; } *r = (typeof(r))ri->d; printf(" %s csched:stolen_vcpu d%uv%u from cpu %u\n", ri->dump_header, r->domid, r->vcpuid, r->peer_cpu); } break; case TRC_SCHED_CLASS_EVT(CSCHED, 5): /* PICKED_CPU */ if(opt.dump_all) { struct { unsigned int domid, vcpuid, cpu; } *r = (typeof(r))ri->d; printf(" %s csched:pick_cpu %u for d%uv%u\n", ri->dump_header, r->cpu, r->domid, r->vcpuid); } break; case TRC_SCHED_CLASS_EVT(CSCHED, 6): /* TICKLE */ if(opt.dump_all) { struct { unsigned int cpu; } *r = (typeof(r))ri->d; printf(" %s csched:runq_tickle, cpu %u\n", ri->dump_header, r->cpu); } break; case TRC_SCHED_CLASS_EVT(CSCHED, 7): /* BOOST_START */ if(opt.dump_all) { struct { unsigned int domid, vcpuid; } *r = (typeof(r))ri->d; printf(" %s csched: d%uv%u boosted\n", ri->dump_header, r->domid, r->vcpuid); } break; case TRC_SCHED_CLASS_EVT(CSCHED, 8): /* BOOST_END */ if(opt.dump_all) { struct { unsigned int domid, vcpuid; } *r = (typeof(r))ri->d; printf(" %s csched: d%uv%u unboosted\n", ri->dump_header, r->domid, r->vcpuid); } break; case TRC_SCHED_CLASS_EVT(CSCHED, 9): /* SCHEDULE */ if(opt.dump_all) { struct { unsigned int cpu:16, tasklet:8, idle:8; } *r = (typeof(r))ri->d; printf(" %s csched:schedule cpu %u%s%s\n", ri->dump_header, r->cpu, r->tasklet ? ", tasklet scheduled" : "", r->idle ? ", idle" : ", busy"); } break; case TRC_SCHED_CLASS_EVT(CSCHED, 10): /* RATELIMIT */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; unsigned int runtime; } *r = (typeof(r))ri->d; printf(" %s csched:ratelimit, d%uv%u run only %u.%uus\n", ri->dump_header, r->domid, r->vcpuid, r->runtime / 1000, r->runtime % 1000); } break; case TRC_SCHED_CLASS_EVT(CSCHED, 11): /* STEAL_CHECK */ if(opt.dump_all) { struct { unsigned int peer_cpu, check; } *r = (typeof(r))ri->d; printf(" %s csched:load_balance %s %u\n", ri->dump_header, r->check ? "checking" : "skipping", r->peer_cpu); } break; /* CREDIT 2 (TRC_CSCHED2_xxx) */ case TRC_SCHED_CLASS_EVT(CSCHED2, 1): /* TICK */ case TRC_SCHED_CLASS_EVT(CSCHED2, 4): /* CREDIT_ADD */ break; case TRC_SCHED_CLASS_EVT(CSCHED2, 2): /* RUNQ_POS */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16, pos; } *r = (typeof(r))ri->d; printf(" %s csched2:runq_insert d%uv%u, position %u\n", ri->dump_header, r->domid, r->vcpuid, r->pos); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 3): /* CREDIT_BURN */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; int credit, delta; } *r = (typeof(r))ri->d; printf(" %s csched2:burn_credits d%uv%u, credit = %d, delta = %d\n", ri->dump_header, r->domid, r->vcpuid, r->credit, r->delta); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 5): /* TICKLE_CHECK */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; int credit; } *r = (typeof(r))ri->d; printf(" %s csched2:tickle_check d%uv%u, credit = %d\n", ri->dump_header, r->domid, r->vcpuid, r->credit); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 6): /* TICKLE */ if(opt.dump_all) { struct { unsigned int cpu:16; } *r = (typeof(r))ri->d; printf(" %s csched2:runq_tickle cpu %u\n", ri->dump_header, r->cpu); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 7): /* CREDIT_RESET */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; int credit_start, credit_end; unsigned int multiplier; } *r = (typeof(r))ri->d; printf(" %s csched2:reset_credits d%uv%u, " "credit_start = %d, credit_end = %d, mult = %u\n", ri->dump_header, r->domid, r->vcpuid, r->credit_start, r->credit_end, r->multiplier); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 8): /* SCHED_TASKLET */ if(opt.dump_all) printf(" %s csched2:sched_tasklet\n", ri->dump_header); break; case TRC_SCHED_CLASS_EVT(CSCHED2, 9): /* UPDATE_LOAD */ if(opt.dump_all) printf(" %s csched2:update_load\n", ri->dump_header); break; case TRC_SCHED_CLASS_EVT(CSCHED2, 10): /* RUNQ_ASSIGN */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; unsigned int rqi:16; } *r = (typeof(r))ri->d; printf(" %s csched2:runq_assign d%uv%u on rq# %u\n", ri->dump_header, r->domid, r->vcpuid, r->rqi); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 11): /* UPDATE_VCPU_LOAD */ if(opt.dump_all) { struct { uint64_t vcpuload; unsigned int vcpuid:16, domid:16; unsigned int shift; } *r = (typeof(r))ri->d; double vcpuload; vcpuload = (r->vcpuload * 100.0) / (1ULL << r->shift); printf(" %s csched2:update_vcpu_load d%uv%u, " "vcpu_load = %4.3f%% (%"PRIu64")\n", ri->dump_header, r->domid, r->vcpuid, vcpuload, r->vcpuload); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 12): /* UPDATE_RUNQ_LOAD */ if(opt.dump_all) { struct { uint64_t rq_avgload, b_avgload; unsigned int rq_load:16, rq_id:8, shift:8; } *r = (typeof(r))ri->d; double avgload, b_avgload; avgload = (r->rq_avgload * 100.0) / (1ULL << r->shift); b_avgload = (r->b_avgload * 100.0) / (1ULL << r->shift); printf(" %s csched2:update_rq_load rq# %u, load = %u, " "avgload = %4.3f%% (%"PRIu64"), " "b_avgload = %4.3f%% (%"PRIu64")\n", ri->dump_header, r->rq_id, r->rq_load, avgload, r->rq_avgload, b_avgload, r->b_avgload); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 13): /* TICKLE_NEW */ if (opt.dump_all) { struct { unsigned int vcpuid:16, domid:16, processor; int credit; } *r = (typeof(r))ri->d; printf(" %s csched2:runq_tickle_new d%uv%u, " "processor = %u, credit = %d\n", ri->dump_header, r->domid, r->vcpuid, r->processor, r->credit); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 14): /* RUNQ_MAX_WEIGHT */ if (opt.dump_all) { struct { unsigned rqi:16, max_weight:16; } *r = (typeof(r))ri->d; printf(" %s csched2:update_max_weight rq# %u, max_weight = %u\n", ri->dump_header, r->rqi, r->max_weight); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 15): /* MIGRATE */ if (opt.dump_all) { struct { unsigned vcpuid:16, domid:16; unsigned rqi:16, trqi:16; } *r = (typeof(r))ri->d; printf(" %s csched2:migrate d%uv%u rq# %u --> rq# %u\n", ri->dump_header, r->domid, r->vcpuid, r->rqi, r->trqi); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 16): /* LOAD_CHECK */ if (opt.dump_all) { struct { unsigned lrqi:16, orqi:16; unsigned load_delta; } *r = (typeof(r))ri->d; printf(" %s csched2:load_balance_check lrq# %u, orq# %u, " "delta = %u\n", ri->dump_header, r->lrqi, r->orqi, r->load_delta); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 17): /* LOAD_BALANCE */ if (opt.dump_all) { struct { uint64_t lb_avgload, ob_avgload; unsigned lrqi:16, orqi:16; } *r = (typeof(r))ri->d; printf(" %s csched2:load_balance_begin lrq# %u, " "avg_load = %"PRIu64" -- orq# %u, avg_load = %"PRIu64"\n", ri->dump_header, r->lrqi, r->lb_avgload, r->orqi, r->ob_avgload); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 19): /* PICKED_CPU */ if (opt.dump_all) { struct { uint64_t b_avgload; unsigned vcpuid:16, domid:16; unsigned rqi:16, cpu:16; } *r = (typeof(r))ri->d; printf(" %s csched2:picked_cpu d%uv%u, rq# %u, cpu %u\n", ri->dump_header, r->domid, r->vcpuid, r->rqi, r->cpu); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 20): /* RUNQ_CANDIDATE */ if (opt.dump_all) { struct { unsigned vcpuid:16, domid:16; unsigned tickled_cpu, skipped; int credit; } *r = (typeof(r))ri->d; printf(" %s csched2:runq_candidate d%uv%u, credit = %d, " "%u vcpus skipped, ", ri->dump_header, r->domid, r->vcpuid, r->credit, r->skipped); if (r->tickled_cpu == (unsigned)-1) printf("no cpu was tickled\n"); else printf("cpu %u was tickled\n", r->tickled_cpu); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 21): /* SCHEDULE */ if (opt.dump_all) { struct { unsigned cpu:16, rqi:16; unsigned tasklet:8, idle:8, smt_idle:8, tickled:8; } *r = (typeof(r))ri->d; printf(" %s csched2:schedule cpu %u, rq# %u%s%s%s%s\n", ri->dump_header, r->cpu, r->rqi, r->tasklet ? ", tasklet scheduled" : "", r->idle ? ", idle" : ", busy", r->idle ? (r->smt_idle ? ", SMT idle" : ", SMT busy") : "", r->tickled ? ", tickled" : ", not tickled"); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 22): /* RATELIMIT */ if (opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; unsigned int runtime; } *r = (typeof(r))ri->d; printf(" %s csched2:ratelimit, d%uv%u run only %u.%uus\n", ri->dump_header, r->domid, r->vcpuid, r->runtime / 1000, r->runtime % 1000); } break; case TRC_SCHED_CLASS_EVT(CSCHED2, 23): /* RUNQ_CAND_CHECK */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; } *r = (typeof(r))ri->d; printf(" %s csched2:runq_cand_check d%uv%u\n", ri->dump_header, r->domid, r->vcpuid); } break; /* RTDS (TRC_RTDS_xxx) */ case TRC_SCHED_CLASS_EVT(RTDS, 1): /* TICKLE */ if(opt.dump_all) { struct { unsigned int cpu:16; } *r = (typeof(r))ri->d; printf(" %s rtds:runq_tickle cpu %u\n", ri->dump_header, r->cpu); } break; case TRC_SCHED_CLASS_EVT(RTDS, 2): /* RUNQ_PICK */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; uint64_t cur_dl, cur_bg; } __attribute__((packed)) *r = (typeof(r))ri->d; printf(" %s rtds:runq_pick d%uv%u, deadline = %"PRIu64", " "budget = %"PRIu64"\n", ri->dump_header, r->domid, r->vcpuid, r->cur_dl, r->cur_bg); } break; case TRC_SCHED_CLASS_EVT(RTDS, 3): /* BUDGET_BURN */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; uint64_t cur_bg; int delta; } __attribute__((packed)) *r = (typeof(r))ri->d; printf(" %s rtds:burn_budget d%uv%u, budget = %"PRIu64", " "delta = %d\n", ri->dump_header, r->domid, r->vcpuid, r->cur_bg, r->delta); } break; case TRC_SCHED_CLASS_EVT(RTDS, 4): /* BUDGET_REPLENISH */ if(opt.dump_all) { struct { unsigned int vcpuid:16, domid:16; uint64_t cur_dl, cur_bg; } __attribute__((packed)) *r = (typeof(r))ri->d; printf(" %s rtds:repl_budget d%uv%u, deadline = %"PRIu64", " "budget = %"PRIu64"\n", ri->dump_header, r->domid, r->vcpuid, r->cur_dl, r->cur_bg); } break; case TRC_SCHED_CLASS_EVT(RTDS, 5): /* SCHED_TASKLET */ if(opt.dump_all) printf(" %s rtds:sched_tasklet\n", ri->dump_header); break; case TRC_SCHED_CLASS_EVT(RTDS, 6): /* SCHEDULE */ if (opt.dump_all) { struct { unsigned cpu:16, tasklet:8, idle:4, tickled:4; } __attribute__((packed)) *r = (typeof(r))ri->d; printf(" %s rtds:schedule cpu %u, %s%s%s\n", ri->dump_header, r->cpu, r->tasklet ? ", tasklet scheduled" : "", r->idle ? ", idle" : ", busy", r->tickled ? ", tickled" : ", not tickled"); } break; default: process_generic(ri); } } else { UPDATE_VOLUME(p, sched_verbose, ri->size); process_generic(&p->ri); } } /* ---- Memory ---- */ void mem_summary_domain(struct domain_data *d) { int i, j; printf(" Grant table ops:\n"); printf(" Done by:\n"); for(i=0; imemops.done[i]) printf(" %-14s: %d\n", mem_name[i], d->memops.done[i]); printf(" Done for:\n"); for(i=0; imemops.done_for[i]) printf(" %-14s: %d\n", mem_name[i], d->memops.done_for[i]); printf(" Populate-on-demand:\n"); printf(" Populated:\n"); for(i=0; i<4; i++) { if ( d->pod.populate_order[i] ) printf(" [%d] %d\n", i, d->pod.populate_order[i]); } printf(" Reclaim order:\n"); for(i=0; i<4; i++) { if ( d->pod.reclaim_order[i] ) printf(" [%d] %d\n", i, d->pod.reclaim_order[i]); } printf(" Reclaim contexts:\n"); for(j=0; jpod.reclaim_context[j] ) { printf(" * [%s] %d\n", pod_reclaim_context_name[j], d->pod.reclaim_context[j]); for(i=0; i<4; i++) { if ( d->pod.reclaim_context_order[j][i] ) printf(" [%d] %d\n", i, d->pod.reclaim_context_order[j][i]); } } } } int p2m_canonical_order(int order) { if ( order % 9 || (order / 9) > 2 ) { fprintf(warn, "%s: Strange, non-canonical order %d\n", __func__, order); order = 4; } else { order /= 9; } return order; } void mem_pod_zero_reclaim_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; int context = POD_RECLAIM_CONTEXT_UNKNOWN; struct vcpu_data *v = p->current; struct { uint64_t gfn, mfn; int d:16,order:16; } *r = (typeof(r))ri->d; if ( v && v->hvm.vmexit_valid ) { switch(v->hvm.exit_reason) { case EXIT_REASON_EPT_VIOLATION: case EXIT_REASON_EXCEPTION_NMI: context = POD_RECLAIM_CONTEXT_FAULT; break; case EXIT_REASON_VMCALL: context = POD_RECLAIM_CONTEXT_BALLOON; break; } } if ( opt.dump_all ) { printf(" %s pod_zero_reclaim d%d o%d g %llx m %llx ctx %s\n", ri->dump_header, r->d, r->order, (unsigned long long)r->gfn, (unsigned long long)r->mfn, pod_reclaim_context_name[context]); } if ( opt.summary_info ) { struct domain_data *d; if ( v && (d=v->d) ) { int order; order = p2m_canonical_order(r->order); d->pod.reclaim_order[order]++; d->pod.reclaim_context[context]++; d->pod.reclaim_context_order[context][order]++; } } } void mem_pod_populate_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct { uint64_t gfn, mfn; int d:16,order:16; } *r = (typeof(r))ri->d; if ( opt.dump_all ) { printf(" %s pod_populate d%d o%d g %llx m %llx\n", ri->dump_header, r->d, r->order, (unsigned long long)r->gfn, (unsigned long long)r->mfn); } if ( opt.summary_info ) { struct vcpu_data *v = p->current; struct domain_data *d; if ( v && (d=v->d) ) { int order; order = p2m_canonical_order(r->order); d->pod.populate_order[order]++; } } } void mem_pod_superpage_splinter_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct { uint64_t gfn; int d:16; } *r = (typeof(r))ri->d; if ( opt.dump_all ) { printf(" %s pod_spage_splinter d%d g %llx\n", ri->dump_header, r->d, (unsigned long long)r->gfn); } } void mem_page_grant(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct { unsigned domain; } *r = (typeof(r))ri->d; union pv_event pevt = { .event = ri->event }; if ( opt.dump_all ) { printf(" %s %s domain %u\n", ri->dump_header, mem_name[pevt.minor], r->domain); } } void mem_set_p2m_entry_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct { uint64_t gfn, mfn; int p2mt; int d:16,order:16; } *r = (typeof(r))ri->d; if ( opt.dump_all ) { printf(" %s set_p2m_entry d%d o%d t %d g %llx m %llx\n", ri->dump_header, r->d, r->order, r->p2mt, (unsigned long long)r->gfn, (unsigned long long)r->mfn); } } void mem_decrease_reservation_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct { uint64_t gfn; int d:16,order:16; } *r = (typeof(r))ri->d; if ( opt.dump_all ) { printf(" %s decrease_reservation d%d o%d g %llx\n", ri->dump_header, r->d, r->order, (unsigned long long)r->gfn); } } void mem_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct { int dom; } *r = (typeof(r))ri->d; int minor = ri->evt.minor; switch ( minor ) { case MEM_PAGE_GRANT_MAP: case MEM_PAGE_GRANT_UNMAP: case MEM_PAGE_GRANT_TRANSFER: mem_page_grant(p); break; case MEM_SET_P2M_ENTRY: mem_set_p2m_entry_process(p); break; case MEM_DECREASE_RESERVATION: mem_decrease_reservation_process(p); break; case MEM_POD_POPULATE: mem_pod_populate_process(p); break; case MEM_POD_ZERO_RECLAIM: mem_pod_zero_reclaim_process(p); break; case MEM_POD_SUPERPAGE_SPLINTER: mem_pod_superpage_splinter_process(p); break; default: if(opt.dump_all) { dump_generic(stdout, ri); } if(opt.summary_info && minor < MEM_MAX) { struct domain_data *d; if(p->current) { if (p->current->d) { p->current->d->memops.done[minor]++; p->current->d->memops.done_interval[minor]++; } if((d=domain_find(r->dom))) { d->memops.done_for[minor]++; d->memops.done_for_interval[minor]++; } } } break; } } /* ---- PM ---- */ #define CSTATE_MAX 5 #define CSTATE_INVALID ((CSTATE_MAX)+1) void pm_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; switch ( ri->event ) { case TRC_PM_FREQ_CHANGE: if (opt.dump_all ) printf(" %s pm_freq_change o%d n%d\n", ri->dump_header, ri->d[0], ri->d[1]); break; case TRC_PM_IDLE_ENTRY: if (opt.dump_all ) printf(" %s pm_idle_start c%d\n", ri->dump_header, ri->d[0]); if ( ri->d[0] <= CSTATE_MAX ) { p->power_state=ri->d[0]; pcpu_string_draw(p); } break; case TRC_PM_IDLE_EXIT: if (opt.dump_all ) printf(" %s pm_idle_end c%d\n", ri->dump_header, ri->d[0]); if ( p->power_state != ri->d[0] && p->power_state != CSTATE_INVALID ) printf("Strange, pm_idle_end %d, power_state %d!\n", ri->d[0], p->power_state); p->power_state = 0; pcpu_string_draw(p); break; default: if(opt.dump_all) { dump_generic(stdout, ri); } break; } } /* * IRQ related stuff */ #define MAX_VECTOR 256 int global_vector_used[256] = {0}; struct pci_dev { uint8_t bus; uint8_t devfn; int vector_used[MAX_VECTOR]; struct pci_dev *next; } *pdev_list; #define MAX_IRQ 512 struct irq_desc { enum { IRQ_NONE, IRQ_MSI, IRQ_GSI } type; struct pci_dev *dev; } irq_table[MAX_IRQ]; struct pci_dev * pdev_find(uint8_t bus, uint8_t devfn) { struct pci_dev *d, *n, **q; /* Look for domain, keeping track of the last pointer so we can add a domain if we need to. */ for ( d = pdev_list, q=&pdev_list ; d && ( (d->bus < bus) || (d->bus == bus && d->devfn < devfn) ) ; q = &d->next, d=d->next ) ; if(d && d->bus == bus && d->devfn == devfn) return d; /* Make a new domain */ fprintf(warn, "Creating pdev %02x:%02x.%x\n", bus, devfn>>4, devfn&3); if((n=malloc(sizeof(*n)))==NULL) { fprintf(stderr, "%s: malloc %zd failed!\n", __func__, sizeof(*n)); error(ERR_SYSTEM, NULL); } bzero(n, sizeof(*n)); n->bus=bus; n->devfn=devfn; /* Insert it into the list */ n->next = d; *q = n; return n; } void irq_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; switch ( ri->event ) { case TRC_HW_IRQ_BIND_VECTOR: { struct { int irq, vec; unsigned mask[4]; } *r = (typeof(r))ri->d; if ( opt.dump_all ) { printf(" %s irq_bind_vector irq %x vec %x mask %04x %04x %04x %04x\n", ri->dump_header, r->irq, r->vec, r->mask[3], r->mask[2], r->mask[1], r->mask[0]); } break; } case TRC_HW_IRQ_HANDLED: { struct { int irq, start_tsc, end_tsc; } *r = (typeof(r))ri->d; int arctime; arctime = r->end_tsc - r->start_tsc; if ( opt.dump_all ) { printf(" %s irq_handled irq %x %d (%d,%d)\n", ri->dump_header, r->irq, arctime, r->start_tsc, r->end_tsc); } if ( opt.scatterplot_irq ) { struct time_struct t; abs_cycles_to_time(ri->tsc, &t); printf("i%x %u.%09u %d\n", (unsigned)r->irq, t.s, t.ns, p->pid); } break; } case TRC_HW_IRQ_ASSIGN_VECTOR: { struct { int irq, vec; unsigned mask[4]; } *r = (typeof(r))ri->d; if ( opt.dump_all ) { printf(" %s irq_assign_vector irq %x vec %x mask %04x %04x %04x %04x\n", ri->dump_header, r->irq, r->vec, r->mask[3], r->mask[2], r->mask[1], r->mask[0]); } if ( r->irq < MAX_IRQ && r->vec < MAX_VECTOR ) { if ( irq_table[r->irq].type == IRQ_MSI ) { if(global_vector_used[r->vec]) fprintf(warn, " Vector collision on global table!\n"); global_vector_used[r->vec]=1; } if( irq_table[r->irq].dev ) { struct pci_dev * pdev=irq_table[r->irq].dev; if(pdev->vector_used[r->vec]) fprintf(warn, " Vector collision on %02x.%02x!\n", pdev->bus, pdev->devfn); pdev->vector_used[r->vec]=1; } } break; } case TRC_HW_IRQ_MOVE_CLEANUP_DELAY: { struct { int irq, vec, cpu; } *r = (typeof(r))ri->d; if ( opt.dump_all ) { printf(" %s irq_move_cleanup_delay irq %x vec %x cpu %d\n", ri->dump_header, r->irq, r->vec, r->cpu); } break; } case TRC_HW_IRQ_MOVE_CLEANUP: { struct { int irq; int vec; int cpu; } *r = (typeof(r))ri->d; if ( opt.dump_all ) { printf(" %s irq_move_cleanup irq %x vec %x cpu %d\n", ri->dump_header, r->irq, r->vec, r->cpu); } if ( r->irq < MAX_IRQ && r->vec < MAX_VECTOR ) { if ( irq_table[r->irq].type == IRQ_MSI ) { if(!global_vector_used[r->vec]) fprintf(warn," Strange, cleanup on non-used vector\n"); global_vector_used[r->vec]=0; } if ( irq_table[r->irq].dev ) { struct pci_dev * pdev=irq_table[r->irq].dev; if(!pdev->vector_used[r->vec]) fprintf(warn," Strange, cleanup on non-used vector\n"); pdev->vector_used[r->vec]=0; } } break; } case TRC_HW_IRQ_UNMAPPED_VECTOR: { struct { int vec; } *r = (typeof(r))ri->d; if ( opt.dump_all ) { printf(" %s irq_unmapped_vector vec %x\n", ri->dump_header, r->vec); } break; } case TRC_HW_IRQ_CLEAR_VECTOR: case TRC_HW_IRQ_MOVE_FINISH : default: if(opt.dump_all) { dump_generic(stdout, ri); } break; } } #define TRC_HW_SUB_PM 1 #define TRC_HW_SUB_IRQ 2 void hw_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; switch(ri->evt.sub) { case TRC_HW_SUB_PM: pm_process(p); break; case TRC_HW_SUB_IRQ: irq_process(p); break; } } #define TRC_DOM0_SUB_DOMOPS 1 void dom0_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; switch(ri->evt.sub) { case TRC_DOM0_SUB_DOMOPS: if(opt.dump_all) { struct { unsigned int domid; } *r = (typeof(r))ri->d; printf(" %s %s domain d%u\n", ri->dump_header, ri->event == TRC_DOM0_DOM_ADD ? "creating" : "destroying", r->domid); } break; default: process_generic(&p->ri); } } /* ---- Base ----- */ void dump_generic(FILE * f, struct record_info *ri) { int i; fprintf(f, "]%s %7x(%x:%x:%x) %u [", ri->dump_header, ri->event, ri->evt.main, ri->evt.sub, ri->evt.minor, ri->extra_words); for(i=0; iextra_words; i++) { fprintf(f, " %x", ri->d[i]); } fprintf(f, " ]\n"); } void dump_raw(char * s, struct record_info *ri) { int i; if(ri->rec.cycle_flag) printf("%s %7x %d %14lld [", s, ri->event, ri->extra_words, ri->tsc); else printf("%s %7x %d %14s [", s, ri->event, ri->extra_words, "-"); for(i=0; i<7; i++) { if ( i < ri->extra_words ) printf(" %8x", ri->d[i]); else printf(" "); } printf(" ] | "); for (i=0; i<8; i++) { printf(" %08x", ri->rec.raw[i]); } printf(" |\n"); } void error(enum error_level l, struct record_info *ri) { if ( l > opt.tolerance ) { if ( ri ) dump_generic(warn, ri); exit(1); } } int check_extra_words(struct record_info *ri, int expected_size, const char *record) { static int off_by_one = 0; int expected_extra = expected_size / sizeof(unsigned int); if(ri->extra_words != expected_extra && !(off_by_one && ri->extra_words == expected_extra + 1) ) { if ( !off_by_one && ri->extra_words == expected_extra + 1 ) { fprintf(warn, "Detected off-by-one bug; relaxing expectations\n"); off_by_one=1; } else { fprintf(warn, "ERROR: %s extra_words %d, expected %d!\n", record, ri->extra_words, expected_extra); error(ERR_RECORD, ri); return 1; } } return 0; } void process_generic(struct record_info *ri) { error(ERR_STRICT, ri); if(opt.dump_all) { dump_generic(stdout, ri); } } int vcpu_set_data_type(struct vcpu_data *v, int type) { if (v->data_type == VCPU_DATA_NONE ) { v->data_type = type; switch(type) { case VCPU_DATA_HVM: init_hvm_data(&v->hvm, v); break; default: break; } } else assert(v->data_type == type); return 0; } void lose_vcpu(struct vcpu_data *v, tsc_t tsc) { if(v->data_type == VCPU_DATA_HVM) v->hvm.vmexit_valid=0; runstate_update(v, RUNSTATE_LOST, tsc); hvm_vlapic_clear(&v->vlapic); if(v->data_type == VCPU_DATA_HVM) { int i; if(opt.dump_all) printf(" [w2h] Clearing w2h state for d%dv%d\n", v->d->did, v->vid); v->hvm.w2h.interrupts=0; v->hvm.w2h.vector=0; v->hvm.w2h.waking = 0; for(i=0; ihvm.summary.guest_interrupt[i].start_tsc) { printf(" Interrupt %d clearing start_tsc %lld\n", i, v->hvm.summary.guest_interrupt[i].start_tsc); } v->hvm.summary.guest_interrupt[i].start_tsc = 0; } } } struct lost_record_struct { int lost_records; unsigned did:16,vid:16; tsc_t first_tsc; }; void process_lost_records(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct lost_record_struct *r = (typeof(r))ri->d; tsc_t first_tsc; /* TSC of first record that was lost */ /* Sanity checks */ if(ri->extra_words != 4) { fprintf(warn, "FATAL: Lost record has unexpected extra words %d!\n", ri->extra_words); error(ERR_RECORD, ri); return; } first_tsc = r->first_tsc; if(opt.dump_all) { if(p->current) printf(" %s lost_records count %d d%uv%u (cur d%dv%d) first_tsc %lld\n", ri->dump_header, r->lost_records, r->did, r->vid, p->current->d->did, p->current->vid, r->first_tsc); else printf(" %s lost_records count %d d%uv%u (cur X) first_tsc %lld\n", ri->dump_header, r->lost_records, r->did, r->vid, r->first_tsc); } #if 0 if(opt.dump_trace_volume_on_lost_record) volume_summary(&p->volume.last_buffer); #endif if ( p->current ) { hvm_vlapic_clear(&p->current->vlapic); if(p->current->data_type == VCPU_DATA_HVM) { p->current->hvm.vmexit_valid=0; cr3_switch(0, &p->current->hvm); } /* We may lose scheduling records; so we need to: * - Point all records until now to the next schedule in the * "default" domain * - Make sure there are no warnings / strangeness with the * current vcpu (if it gets scheduled elsewhere). */ vcpu_prev_update(p, p->current, first_tsc, RUNSTATE_LOST); } #if 0 vcpu_next_update(p, default_domain.vcpu[p->pid], first_tsc); if(p->current->data_type == VCPU_DATA_HVM) { p->current->hvm.vmexit_valid=0; } #endif /* The lost record trace is processed early -- i.e., * After the last good record, rather than when the next * record is processed. Between the time it's processed and * the time it actually went in, the vcpu may be scheduled on * other processors. So we can't switch vcpus until the first * TSC'd record after the lost record. */ if(!p->lost_record.active) { P.lost_cpus++; if(P.lost_cpus > P.max_active_pcpu + 1) { fprintf(warn, "ERROR: P.lost_cpus %d > P.max_active_pcpu + 1 %d!\n", P.lost_cpus, P.max_active_pcpu + 1); error(ERR_ASSERT, NULL); } } else fprintf(warn, "Strange, lost record for pcpu %d, but lost_record still active!\n", p->pid); p->lost_record.active = 1; p->lost_record.tsc = first_tsc; pcpu_string_draw(p); { /* Any vcpu which is not actively running may be scheduled on the * lost cpu. To avoid mis-accounting, we need to reset */ struct domain_data *d; int i; for(d=domain_list ; d; d=d->next) { if(d->did != DEFAULT_DOMAIN) { for(i=0; ivcpu[i] && d->vcpu[i]->runstate.state != RUNSTATE_RUNNING) { if(opt.dump_all) fprintf(warn, "%s: setting d%dv%d to RUNSTATE_LOST\n", __func__, d->did, i); lose_vcpu(d->vcpu[i], first_tsc); } } } } p->lost_record.domain_valid=1; p->lost_record.did=r->did; p->lost_record.vid=r->vid; } void process_lost_records_end(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct lost_record_struct *r = (typeof(r))ri->d; if(!p->lost_record.active) { fprintf(warn, "FATAL: lost_records_end but pid %d not lost!\n", p->pid); error(ERR_FILE, NULL); return; } /* Lost records. If this is the first record on a pcpu after the loss, * Update the information. */ if(ri->tsc > p->lost_record.tsc) { if(opt.dump_all) printf(" %s lost_records end ---\n", pcpu_string(p->pid)); update_cycles(&p->time.lost, ri->tsc - p->lost_record.tsc); if(p->lost_record.domain_valid) { int did = p->lost_record.did, vid = p->lost_record.vid; if(opt.dump_all) printf(" %s lost_records end d%dv%d---\n", pcpu_string(p->pid), did, vid); if(p->current) { fprintf(warn, "FATAL: lost_record valid (d%dv%d), but current d%dv%d!\n", did, vid, p->current->d->did, p->current->vid); error(ERR_FILE, NULL); return; } if(opt.dump_all) fprintf(warn, "Changing p%d current to d%dv%d\n", p->pid, did, vid); vcpu_next_update(p, vcpu_find(did, vid), ri->tsc); p->lost_record.domain_valid=0; p->lost_record.seen_valid_schedule=0; /* Let next vcpu_next_update know that this one was inferred */ } else { if(opt.dump_all) printf(" %s lost_records end (domain invalid)---\n", pcpu_string(p->pid)); } p->lost_record.active = 0; pcpu_string_draw(p); P.lost_cpus--; if(P.lost_cpus < 0) { fprintf(warn, "ERROR: lost_cpus fell below 0 for pcpu %d!\n", p->pid); error(ERR_ASSERT, NULL); } } } void base_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; switch(ri->event) { case TRC_TRACE_WRAP_BUFFER: break; case TRC_LOST_RECORDS: process_lost_records(p); break; case TRC_LOST_RECORDS_END: process_lost_records_end(p); break; default: process_generic(ri); } } /* Non-compat only */ void record_order_insert(struct pcpu_info *new); void record_order_remove(struct pcpu_info *rem); void record_order_bubble(struct pcpu_info *last); struct cpu_change_data { int cpu; unsigned window_size; }; void activate_early_eof(void) { struct pcpu_info *p; int i; fprintf(warn, "Short cpu_change window, activating early_eof\n"); P.early_eof = 1; for(i=0; i<=P.max_active_pcpu; i++) { p = P.pcpu + i; if(p->active && p->file_offset > P.last_epoch_offset) { fprintf(warn, " deactivating pid %d\n", p->pid); p->active = 0; } } } off_t scan_for_new_pcpu(off_t offset) { ssize_t r; struct trace_record rec; struct cpu_change_data *cd; r=__read_record(&rec, offset); if(r==0) return 0; if(rec.event != TRC_TRACE_CPU_CHANGE || rec.cycle_flag) { fprintf(stderr, "%s: Unexpected record event %x!\n", __func__, rec.event); error(ERR_ASSERT, NULL); /* Actually file, but can't recover */ } cd = (typeof(cd))rec.u.notsc.data; if ( cd->cpu > MAX_CPUS ) { fprintf(stderr, "%s: cpu %d exceeds MAX_CPU %d!\n", __func__, cd->cpu, MAX_CPUS); /* FIXME: Figure out if we could handle this more gracefully */ error(ERR_ASSERT, NULL); } if(cd->cpu > P.max_active_pcpu || !P.pcpu[cd->cpu].active) { struct pcpu_info *p = P.pcpu + cd->cpu; fprintf(warn, "%s: Activating pcpu %d at offset %lld\n", __func__, cd->cpu, (unsigned long long)offset); p->active = 1; /* Process this cpu_change record first */ p->ri.rec = rec; p->ri.size = r; __fill_in_record_info(p); p->file_offset = offset; p->next_cpu_change_offset = offset; record_order_insert(p); offset += r + cd->window_size; sched_default_vcpu_activate(p); if ( cd->cpu > P.max_active_pcpu ) P.max_active_pcpu = cd->cpu; return offset; } else { return 0; } } /* * Conceptually, when we reach a cpu_change record that's not for our pcpu, * we want to scan forward through the file until we reach one that's for us. * However, looping through involves reading the file, which we'd rather * do in one place. Because cpu_change records don't include a tsc, * the same pcpu will be processed repeatedly until the cpu_change * equals p->pid. * * There are two additional things we need to do in this algorithm: * + Detect new pcpus as they come online * + De-activate pcpus which don't have any more records * * Detecting new pcpus which are less than P.max_active_pcpu is straight- * forward: when max_active_pcpu is searching for its next cpu window, * it will pass by the new cpu's window, and can activate it then. * * Detecting new pcpus greater than P.max_active_pcpu is a little harder; * When max_active_pcpu is scanning for its next cpu window, after it's found * it, we need to scan one more window forward to see if its' an already-active * pcpu; if not, activate it. * * We also need to deal with truncated files, where records from one pcpu may * be present but not from another pcpu due to lack of disk space. The best * thing to do is to find the last "epoch" and essentially truncate the file * to that. */ void deactivate_pcpu(struct pcpu_info *p) { if ( p->current ) { pcpu_runstate_update(p, p->last_tsc); fprintf(warn, "%s: setting d%dv%d to state LOST\n", __func__, p->current->d->did, p->current->vid); lose_vcpu(p->current, p->last_tsc); } p->active = 0; record_order_remove(p); if ( p->pid == P.max_active_pcpu ) { int i, max_active_pcpu = -1; for(i=0; i<=P.max_active_pcpu; i++) { if(!P.pcpu[i].active) continue; max_active_pcpu = i; } P.max_active_pcpu = max_active_pcpu; fprintf(warn, "%s: Setting max_active_pcpu to %d\n", __func__, max_active_pcpu); } } /* Helper function to process tsc-related record info */ void process_record_tsc(tsc_t order_tsc, struct record_info *ri) { /* Find the first tsc set */ if(ri->tsc && ri->tsc >= P.f.first_tsc) { /* We use the order_tsc to account for the second processing of * a lost record. */ tsc_t tsc = order_tsc; if(P.f.first_tsc == 0) { P.f.first_tsc = tsc; if ( opt.interval_mode ) { P.interval.start_tsc = tsc; } } else { if ( opt.interval_mode ) { if(P.interval.start_tsc > tsc) { fprintf(warn, "FATAL: order_tsc %lld < interval.start_tsc %lld!\n", tsc, P.interval.start_tsc); error(ERR_FILE, NULL); } else { while ( tsc - P.interval.start_tsc > opt.interval.cycles ) { interval_callback(); P.interval.start_tsc += opt.interval.cycles; } } } } P.f.last_tsc=tsc; P.f.total_cycles = P.f.last_tsc - P.f.first_tsc; P.now = tsc; } } /* Standardized part of dump output */ void create_dump_header(struct record_info *ri, struct pcpu_info *p) { char * c; int len, r; len = DUMP_HEADER_MAX; c = ri->dump_header; abs_cycles_to_time(ri->tsc, &ri->t); if ( ri->t.time ) { r=snprintf(c, len, "%3u.%09u", ri->t.s, ri->t.ns); c+=r; len-=r; } else { r=snprintf(c, len, " "); c+=r; len-=r; } r = snprintf(c, len, " %s", pcpu_string(ri->cpu)); c+=r; len-=r; if ( p->current ) { r = snprintf(c, len, " d%dv%d", p->current->d->did, p->current->vid); c+=r; len-=r; } else { r = snprintf(c, len, " d?v?"); c+=r; len-=r; } } int find_toplevel_event(struct record_info *ri) { int toplevel=0, i, count; for(i=0, count=0; ievt.main & (1UL<event, ri->evt.main, ri->evt.sub, ri->evt.minor); error(ERR_RECORD, NULL); return -1; } return toplevel; } void process_cpu_change(struct pcpu_info *p) { struct record_info *ri = &p->ri; struct cpu_change_data *r = (typeof(r))ri->d; if(opt.dump_all && verbosity >= 6) { printf("]%s cpu_change this-cpu %u record-cpu %u window_size %u(0x%08x)\n", ri->dump_header, p->pid, r->cpu, r->window_size, r->window_size); } /* File sanity check */ if(p->file_offset != p->next_cpu_change_offset) { fprintf(warn, "Strange, pcpu %d expected offet %llx, actual %llx!\n", p->pid, (unsigned long long)p->next_cpu_change_offset, (unsigned long long)p->file_offset); } if(r->cpu > MAX_CPUS) { fprintf(stderr, "FATAL: cpu %d > MAX_CPUS %d.\n", r->cpu, MAX_CPUS); /* Actually file, but takes some work to skip */ error(ERR_ASSERT, NULL); } /* Detect beginning of new "epoch" while scanning thru file */ if((p->last_cpu_change_pid > r->cpu) && (p->file_offset > P.last_epoch_offset)) { P.last_epoch_offset = p->file_offset; } /* If that pcpu has never been activated, activate it. */ if(!P.pcpu[r->cpu].active && P.pcpu[r->cpu].file_offset == 0) { struct pcpu_info * p2 = P.pcpu + r->cpu; p2->active = 1; if(r->cpu > P.max_active_pcpu) P.max_active_pcpu = r->cpu; /* Taking this record as the first record should make everything * run swimmingly. */ p2->ri = *ri; p2->ri.cpu = r->cpu; p2->ri.d = p2->ri.rec.u.notsc.data; p2->file_offset = p->file_offset; p2->next_cpu_change_offset = p->file_offset; fprintf(warn, "%s: Activating pcpu %d at offset %lld\n", __func__, r->cpu, (unsigned long long)p->file_offset); record_order_insert(p2); sched_default_vcpu_activate(p2); } p->last_cpu_change_pid = r->cpu; /* If this isn't the cpu we're looking for, skip the whole bunch */ if(p->pid != r->cpu) { p->file_offset += ri->size + r->window_size; p->next_cpu_change_offset = p->file_offset; if(p->file_offset > G.file_size) { activate_early_eof(); } else if(P.early_eof && p->file_offset > P.last_epoch_offset) { fprintf(warn, "%s: early_eof activated, pcpu %d past last_epoch_offset %llx, deactivating.\n", __func__, p->pid, (unsigned long long)P.last_epoch_offset); deactivate_pcpu(p); } } else { /* Track information about dom0 scheduling and records */ if(opt.dump_trace_volume_on_lost_record) { tsc_t cycles; struct time_struct t; /* Update dom0 runstates */ cycles = (p->volume.buffer_first_tsc > p->volume.buffer_dom0_runstate_tsc) ? p->volume.buffer_first_tsc : p->volume.buffer_dom0_runstate_tsc; p->volume.buffer_dom0_runstate_cycles[p->volume.buffer_dom0_runstate] += ri->tsc - cycles; printf(" - updated p%d dom0_runstate %s to %lld cycles (+%lld)\n", p->pid, runstate_name[p->volume.buffer_dom0_runstate], p->volume.buffer_dom0_runstate_cycles[p->volume.buffer_dom0_runstate], ri->tsc - cycles); /* print info */ cycles = ri->tsc - p->volume.buffer_first_tsc; cycles_to_time(cycles, &t); printf("Buffer time: %u.%09u (%lld cycles)\n", t.s, t.ns, cycles); if(p->volume.buffer_size) printf("Rate: %lld cycles / byte\n", cycles / p->volume.buffer_size); if(P.buffer_trace_virq_tsc) { cycles = ri->tsc - P.buffer_trace_virq_tsc; cycles_to_time(cycles, &t); printf("trace_virq latency: %u.%09u (%lld cycles)\n", t.s, t.ns, cycles); P.buffer_trace_virq_tsc = 0; } else { printf("No trace_virq record found.\n"); } printf("Dom0 runstates this buffer:\n"); runstate_summary(p->volume.buffer_dom0_runstate_cycles); volume_summary(&p->volume.last_buffer); /* reset info */ p->volume.buffer_first_tsc = 0; p->volume.buffer_size = r->window_size; runstate_clear(p->volume.buffer_dom0_runstate_cycles); volume_clear(&p->volume.last_buffer); } p->file_offset += ri->size; p->next_cpu_change_offset = p->file_offset + r->window_size; if(p->next_cpu_change_offset > G.file_size) activate_early_eof(); else if(p->pid == P.max_active_pcpu) scan_for_new_pcpu(p->next_cpu_change_offset); } } struct tl_assert_mask { unsigned p_current:1, not_idle_domain:1; int vcpu_data_mode; }; static struct tl_assert_mask tl_assert_checks[TOPLEVEL_MAX] = { [TRC_HVM_MAIN]={ .p_current=1, .not_idle_domain=1, .vcpu_data_mode=VCPU_DATA_HVM }, [TRC_SHADOW_MAIN]={ .p_current=1, .not_idle_domain=1, .vcpu_data_mode=VCPU_DATA_HVM }, [TRC_PV_MAIN]={ .p_current=1, .not_idle_domain=1, .vcpu_data_mode=VCPU_DATA_PV }, }; /* There are a lot of common assumptions for the various processing * routines. Check them all in one place, doing something else if * they don't pass. */ int toplevel_assert_check(int toplevel, struct pcpu_info *p) { struct tl_assert_mask mask; mask = tl_assert_checks[toplevel]; if (mask.p_current && p->current == NULL) { fprintf(warn, "WARNING: p->current null! Not processing\n"); goto fail; } if( mask.not_idle_domain ) { /* Can't do this check w/o first doing above check */ assert(mask.p_current); if ( p->current->d->did == IDLE_DOMAIN) { fprintf(warn, "WARNING: Unexpected record for idle domain! Not processing\n"); goto fail; } } if ( mask.vcpu_data_mode ) { struct vcpu_data *v; assert(mask.p_current); v = p->current; if ( ! (v->data_type == VCPU_DATA_NONE || v->data_type == mask.vcpu_data_mode) ) { /* This may happen for track_dirty_vram, which causes a SHADOW_WRMAP_BF trace f/ dom0 */ fprintf(warn, "WARNING: Unexpected vcpu data type for d%dv%d on proc %d! Expected %d got %d. Not processing\n", v->d->did, v->vid, p->pid, mask.vcpu_data_mode, v->data_type); goto fail; } } return 1; fail: dump_generic(warn, &p->ri); return 0; } void process_record(struct pcpu_info *p) { struct record_info *ri = &p->ri; int toplevel; /* Process only TRC_TRACE_CPU_CHANGE */ if(ri->event == TRC_TRACE_CPU_CHANGE) { process_cpu_change(p); return; } if ( opt.dump_no_processing ) goto out; p->summary = 1; if( opt.dump_raw_process ) dump_raw("* ", ri); process_record_tsc(p->order_tsc, ri); if(opt.dump_all) create_dump_header(ri, p); toplevel = find_toplevel_event(ri); if ( toplevel < 0 ) return; /* Unify toplevel assertions */ if ( toplevel_assert_check(toplevel, p) ) { switch(toplevel) { case TRC_GEN_MAIN: base_process(p); break; case TRC_SCHED_MAIN: sched_process(p); break; case TRC_HVM_MAIN: hvm_process(p); break; case TRC_SHADOW_MAIN: shadow_process(p); break; case TRC_PV_MAIN: pv_process(p); break; case TRC_MEM_MAIN: mem_process(p); break; case TRC_HW_MAIN: hw_process(p); break; case TRC_DOM0OP_MAIN: dom0_process(p); break; default: process_generic(ri); } } UPDATE_VOLUME(p, toplevel[toplevel], ri->size); if(!p->volume.buffer_first_tsc) p->volume.buffer_first_tsc = ri->tsc; out: /* Lost records gets processed twice */ if(ri->event != TRC_LOST_RECORDS) p->file_offset += ri->size; } static inline ssize_t get_rec_size(struct trace_record *rec) { ssize_t s; s = sizeof(uint32_t); if(rec->cycle_flag) s += sizeof(tsc_t); s += rec->extra_words * sizeof(uint32_t); return s; } #define STDIN 0 void progress_child_exec(void) { fclose(stdin); dup2(G.progress.pipe[0], STDIN); execlp("zenity", "zenity", "--progress", "--auto-close", "--title", "Analyzing", "--text", G.trace_file, "--auto-kill", NULL); } void progress_init(void) { int pid; if (pipe(G.progress.pipe) < 0) perror("pipe"); if(!(pid = fork())) { progress_child_exec(); fprintf(stderr, "%s: exec failed (%s), disabling progress bar\n", __func__, strerror(errno)); opt.progress = 0; exit(1); } else if( pid < 0 ) { fprintf(stderr, "%s: could not fork: %s, disabling progress bar\n", __func__, strerror(errno)); opt.progress = 0; } if( (G.progress.out = fdopen(G.progress.pipe[1], "w")) == NULL ) { fprintf(stderr, "%s: could not fdopen pipe: %s, disabling progress bar\n", __func__, strerror(errno)); opt.progress = 0; } } void progress_update(off_t offset) { long long p; p = ( offset * 100 ) / G.file_size; fprintf(G.progress.out, "%lld\n", p); fflush(G.progress.out); p += 1; G.progress.update_offset = ( G.file_size * p ) / 100; #if 0 fprintf(stderr, "Progress: %lld %% Next update_offset: %lld\n", p-1, G.progress.update_offset); #endif } void progress_finish(void) { int pid; fprintf(G.progress.out, "100\n"); fflush(G.progress.out); fclose(G.progress.out); wait(NULL); if(!(pid = fork())) { /* Child */ char text[128]; snprintf(text, 128, "Finished analyzing %s", G.trace_file); execlp("zenity", "zenity", "--info", "--text", text, NULL); } } ssize_t __read_record(struct trace_record *rec, off_t offset) { ssize_t r, rsize; r=mread64(G.mh, rec, sizeof(*rec), offset); if(r < 0) { /* Read error */ perror("read"); fprintf(stderr, "offset %llx\n", (unsigned long long)offset); return 0; } else if(r==0) { /* End-of-file */ return 0; } else if(r < sizeof(uint32_t)) { /* Full header not read */ fprintf(stderr, "%s: short read (%zd bytes)\n", __func__, r); error(ERR_SYSTEM, NULL); } rsize=get_rec_size(rec); if(r < rsize) { /* Full record not read */ fprintf(stderr, "%s: short read (%zd, expected %zd)\n", __func__, r, rsize); return 0; } return rsize; } void __fill_in_record_info(struct pcpu_info *p) { struct record_info *ri; tsc_t tsc=0; ri = &p->ri; ri->event = ri->rec.event; ri->extra_words = ri->rec.extra_words; if(ri->rec.cycle_flag) { tsc = (((tsc_t)ri->rec.u.tsc.tsc_hi) << 32) | ri->rec.u.tsc.tsc_lo; tsc += p->tsc_skew.offset; ri->tsc = tsc; ri->d = ri->rec.u.tsc.data; if(p->first_tsc == 0) p->first_tsc = tsc; /* We process lost record twice: once at the first_tsc, once at the time it was placed in the log */ if(ri->event == TRC_LOST_RECORDS && ri->extra_words == 4) { struct lost_record_struct *r = (typeof(r))ri->d; p->order_tsc = r->first_tsc + p->tsc_skew.offset; } else p->order_tsc = tsc; p->last_tsc = tsc; } else { ri->tsc = p->last_tsc; ri->d = ri->rec.u.notsc.data; } if ( opt.dump_raw_reads ) { char s[256]; snprintf(s, 256, "R p%2d o%016llx ", p->pid, (unsigned long long)p->file_offset); dump_raw(s, ri); } /* Updated tracing uses CPU_CHANGE. If we hit one of these, * it will process very next (since the tsc isn't updated), and * we'll skip forward appropriately. */ ri->cpu = p->pid; } ssize_t read_record(struct pcpu_info * p) { off_t * offset; struct record_info *ri; offset = &p->file_offset; ri = &p->ri; ri->size = __read_record(&ri->rec, *offset); if(ri->size) { __fill_in_record_info(p); } else { fprintf(warn, "%s: read returned zero, deactivating pcpu %d\n", __func__, p->pid); deactivate_pcpu(p); } return ri->size; } /* * This funciton gets called for every record when doing dump. Try to * make it efficient by changing the minimum amount from the last * call. Do this by: * - Keeping track of the last pcpu called, so we can just set that to - * - Keeping track of how many pcpus we've "drawn", and only "drawing" new ones * - Updating the current one * * FIXME: Need to deal with pcpu states changing... * * WARNING not thread-safe */ char __pcpu_string[MAX_CPUS+1] = { 0 }; void pcpu_string_draw(struct pcpu_info *p) { char *s = __pcpu_string; int i=p->pid; if(p->lost_record.active) s[i]='l'; else if (!p->current) s[i]=' '; else if (p->current->d->did == DEFAULT_DOMAIN) s[i]='.'; else if (p->current->d->did == IDLE_DOMAIN) { if ( opt.dump_show_power_states ) s[i]=p->power_state+'0'; else s[i]='-'; } else s[i]='|'; } char * pcpu_string(int pcpu) { char *s = __pcpu_string; static int max_active_pcpu=-1, last_pcpu=-1; assert(P.max_active_pcpu < MAX_CPUS); assert(pcpu <= P.max_active_pcpu); if(last_pcpu >= 0) pcpu_string_draw(P.pcpu+last_pcpu); if(P.max_active_pcpu > max_active_pcpu) { int i; for(i=max_active_pcpu + 1; i<= P.max_active_pcpu; i++) pcpu_string_draw(P.pcpu+i); max_active_pcpu=P.max_active_pcpu; } s[pcpu]='x'; last_pcpu = pcpu; return s; } /* Null terminated */ struct pcpu_info *record_order[MAX_CPUS+1] = { 0 }; /* In the case of identical tsc values, the old algorithm would favor the * pcpu with the lowest number. By default the new algorithm favors the * pcpu which has been processed most recently. * * I think the second way is better; but it's good to be able to use the * old ordering, at very lest to verify that there are no (other) ordering * differences. Enabling the below flag will cause the insertion / bubble * routines to order by pcpu id as well as tsc, preserving the old order. */ //#define PRESERVE_PCPU_ORDERING /* Steady state: * + Entire list is in order, except (potentially) for the first entry * + last is pointing to the first entry. */ void record_order_bubble(struct pcpu_info *last) { int i; /* Find the pcpu to "bubble". This is usually the * first one, but if other pcpus have been activated, it may * not be. */ for(i=0; record_order[i] && record_order[i]!=last; i++); assert(record_order[i]); /* Now bubble it down */ for( ; record_order[i+1] && ( record_order[i+1]->order_tsc < last->order_tsc #ifdef PRESERVE_PCPU_ORDERING || ( record_order[i+1]->order_tsc == last->order_tsc && record_order[i+1]->pid < last->pid ) #endif ) ; i++) record_order[i]=record_order[i+1]; record_order[i]=last; } void record_order_insert(struct pcpu_info *new) { int i; struct pcpu_info *p=NULL, *t=NULL; /* Sanity check: Make sure it's not already in there */ for(i=0; record_order[i]; i++) assert(record_order[i]!=new); /* Find where to insert it */ for(i=0; record_order[i] && ( record_order[i]->order_tsc < new->order_tsc #ifdef PRESERVE_PCPU_ORDERING || ( record_order[i]->order_tsc == new->order_tsc && record_order[i]->pid < new->pid ) #endif ) ; i++) ; /* And insert it */ for( p=new; p ; i++) { t=record_order[i]; record_order[i]=p; p=t; } } void record_order_remove(struct pcpu_info *rem) { int i; /* Find where the record is */ for(i=0; record_order[i] && record_order[i]!=rem; i++) ; /* Sanity check: Make sure it's actually there! */ assert(record_order[i]); /* And move everyone forward */ for(; (record_order[i]=record_order[i+1]); i++) ; } struct pcpu_info * choose_next_record(void) { struct pcpu_info *min_p=NULL; min_p=record_order[0]; if(opt.progress && min_p && min_p->file_offset >= G.progress.update_offset) progress_update(min_p->file_offset); /* If there are active pcpus, make sure we chose one */ assert(min_p || (P.max_active_pcpu==-1)); return min_p; } void process_records(void) { while(1) { struct pcpu_info *p = NULL; if(!(p=choose_next_record())) return; process_record(p); /* Lost records gets processed twice. */ if(p->ri.event == TRC_LOST_RECORDS) { p->ri.event = TRC_LOST_RECORDS_END; if(p->ri.tsc > p->order_tsc) p->order_tsc = p->ri.tsc; else { fprintf(warn, "Strange, lost_record ri->tsc %lld !> p->order_tsc %lld!\n", p->ri.tsc, p->order_tsc); error(ERR_FILE, NULL); } } else read_record(p); /* Update this pcpu in the processing order */ if ( p->active ) record_order_bubble(p); } } void vcpu_summary(struct vcpu_data *v) { printf("-- v%d --\n", v->vid); sched_summary_vcpu(v); switch(v->data_type) { case VCPU_DATA_HVM: hvm_summary(&v->hvm); break; case VCPU_DATA_PV: pv_summary(&v->pv); break; default: break; } } void domain_summary(void) { struct domain_data * d; int i; if(opt.show_default_domain_summary) { d = &default_domain; printf("|-- Default domain --|\n"); for( i = 0; i < MAX_CPUS ; i++ ) { if(d->vcpu[i]) vcpu_summary(d->vcpu[i]); } } for ( d = domain_list ; d ; d=d->next ) { int i; printf("|-- Domain %d --|\n", d->did); sched_summary_domain(d); mem_summary_domain(d); for( i = 0; i < MAX_CPUS ; i++ ) { if(d->vcpu[i]) vcpu_summary(d->vcpu[i]); } printf("Emulate eip list\n"); dump_eip(d->emulate_eip_list); if ( opt.with_interrupt_eip_enumeration ) { printf("Interrupt eip list (vector %d)\n", opt.interrupt_eip_enumeration_vector); dump_eip(d->interrupt_eip_list); } cr3_dump_list(d->cr3_value_head); } } char * stringify_cpu_hz(long long cpu_hz); void summary(void) { int i; printf("Total time: %.2lf seconds (using cpu speed %s)\n", ((double)(P.f.total_cycles))/opt.cpu_hz, stringify_cpu_hz(opt.cpu_hz)); printf("--- Log volume summary ---\n"); for(i=0; isummary) continue; printf(" - cpu %d -\n", i); volume_summary(&p->volume.total); } domain_summary(); } void report_pcpu(void) { int i, active=0; for(i=0; isummary) continue; printf("pcpu %d\n", i); print_cycle_summary(&p->time.running, " running"); print_cycle_summary(&p->time.idle, " idle"); print_cycle_summary(&p->time.lost, " lost"); if ( p->time.running.count ) active++; } printf("Total active cpus: %d\n", active); } void init_pcpus(void) { int i=0; off_t offset = 0; for(i=0; i GHZ) { hz = (float)cpu_hz / GHZ; suffix = 'G'; } else if(cpu_hz > MHZ) { hz = (float)cpu_hz / MHZ; suffix = 'M'; } else if(cpu_hz > KHZ) { hz = (float)cpu_hz / KHZ; suffix = 'k'; } else { hz = cpu_hz; suffix = ' '; } snprintf(cpu_string, 20, "%1.2lf %cHz", hz, suffix); return cpu_string; } int parse_array(char *arg, struct array_struct *a) { char *p, *q; int n=1, i; /* Count the number of commas (and thus the number of elements) */ for(p=arg; *p; p++) if(*p == ',') n++; fprintf(warn, "%s: Found %d elements\n", __func__, n); fflush(warn); a->count = n; a->values = malloc(n * sizeof(unsigned long long)); if(!a->values) { fprintf(stderr, "Malloc failed!\n"); error(ERR_SYSTEM, NULL); } /* Now parse the elements */ p = q = arg; for(i=0; ivalues[i] = strtoull(p, &q, 0); if(p == q) { fprintf(stderr, "Bad format: %s\n", q); return -1; } fprintf(warn, "%s: Found element 0x%llx (%lld)\n", __func__, a->values[i], a->values[i]); fflush(warn); if(*q == ',') q++; else if(*q != '\0') { fprintf(stderr, "Bad format: %s\n", q); return -1; } p=q; } return n; } error_t cmd_parser(int key, char *arg, struct argp_state *state) { switch (key) { /* Dump group */ case OPT_DUMP_ALL: opt.dump_all = 1; G.output_defined = 1; break; case OPT_DUMP_RAW_READS: opt.dump_raw_reads = 1; G.output_defined = 1; break; case OPT_DUMP_NO_PROCESSING: opt.dump_no_processing = 1; opt.dump_raw_reads = 1; G.output_defined = 1; break; case OPT_DUMP_RAW_PROCESS: opt.dump_raw_process = 1; G.output_defined = 1; break; case OPT_DUMP_IPI_LATENCY: opt.dump_ipi_latency = 1; break; case OPT_DUMP_TRACE_VOLUME_ON_LOST_RECORD: opt.dump_trace_volume_on_lost_record = 1; break; case OPT_DUMP_SHOW_POWER_STATES: opt.dump_show_power_states = 1; break; /* Extra group */ case OPT_WITH_CR3_ENUMERATION: opt.with_cr3_enumeration=1; break; case OPT_WITH_PIO_ENUMERATION: opt.with_pio_enumeration=1; break; case OPT_WITH_MMIO_ENUMERATION: opt.with_mmio_enumeration=1; break; case OPT_SHOW_DEFAULT_DOMAIN_SUMMARY: opt.show_default_domain_summary=1; break; case OPT_SAMPLE_SIZE: { char * inval; opt.sample_size = (int)strtol(arg, &inval, 0); if( inval == arg ) argp_usage(state); break; } case OPT_SAMPLE_MAX: { char * inval; opt.sample_max = (int)strtol(arg, &inval, 0); if( inval == arg ) argp_usage(state); break; } case OPT_MMIO_ENUMERATION_SKIP_VGA: { char * inval; opt.mmio_enumeration_skip_vga = (int)strtol(arg, &inval, 0); if( inval == arg ) argp_usage(state); break; } case OPT_SCATTERPLOT_INTERRUPT_EIP: { char * inval; G.output_defined = 1; opt.scatterplot_interrupt_eip=1; opt.scatterplot_interrupt_vector = (int)strtol(arg, &inval, 0); if( inval == arg ) argp_usage(state); } break; case OPT_WITH_INTERRUPT_EIP_ENUMERATION: { char * inval; opt.with_interrupt_eip_enumeration=1; opt.interrupt_eip_enumeration_vector = (int)strtol(arg, &inval, 0); if( inval == arg ) argp_usage(state); } break; case OPT_SCATTERPLOT_UNPIN_PROMOTE: G.output_defined = 1; opt.scatterplot_unpin_promote=1; break; case OPT_SCATTERPLOT_CR3_SWITCH: G.output_defined = 1; opt.scatterplot_cr3_switch=1; break; case OPT_SCATTERPLOT_WAKE_TO_HALT: G.output_defined = 1; opt.scatterplot_wake_to_halt=1; break; case OPT_SCATTERPLOT_VMEXIT_EIP: G.output_defined = 1; opt.scatterplot_vmexit_eip=1; break; case OPT_SCATTERPLOT_EXTINT_CYCLES: { char * inval; G.output_defined = 1; opt.scatterplot_extint_cycles=1; opt.scatterplot_extint_cycles_vector = (int)strtol(arg, &inval, 0); if( inval == arg ) argp_usage(state); } break; case OPT_SCATTERPLOT_RDTSC: G.output_defined = 1; opt.scatterplot_rdtsc=1; break; case OPT_SCATTERPLOT_IRQ: G.output_defined = 1; opt.scatterplot_irq=1; break; case OPT_SCATTERPLOT_IO: { char * inval; G.output_defined = 1; opt.scatterplot_io=1; opt.scatterplot_io_port = (int)strtol(arg, &inval, 0); if( inval == arg ) argp_usage(state); } break; case OPT_SCATTERPLOT_RUNSTATE: G.output_defined = 1; opt.scatterplot_runstate=1; break; case OPT_SCATTERPLOT_RUNSTATE_TIME: G.output_defined = 1; opt.scatterplot_runstate_time=1; break; case OPT_SCATTERPLOT_PCPU: G.output_defined = 1; opt.scatterplot_pcpu=1; break; case OPT_HISTOGRAM_INTERRUPT_EIP: { char * inval, *p; opt.histogram_interrupt_eip=1; opt.histogram_interrupt_vector = (int)strtol(arg, &inval, 0); if( inval == arg ) argp_usage(state); p = inval; if(*p == ',') opt.histogram_interrupt_increment = (unsigned long long)strtoull(p+1, &inval, 0); else opt.histogram_interrupt_increment = 0x1000000; printf("Making histogram of eips at interrupt %d, increment %llx\n", opt.histogram_interrupt_vector, opt.histogram_interrupt_increment); } break; case OPT_INTERVAL_LENGTH: { char * inval; opt.interval.msec = (unsigned) (strtof(arg, &inval) * 1000); if ( inval == arg ) argp_usage(state); break; } case OPT_INTERVAL_CR3_SCHEDULE_TIME: { if(parse_array(arg, &opt.interval.array) < 0) goto usage; interval_table_alloc(opt.interval.array.count); opt.interval.output = INTERVAL_CR3_SCHEDULE_TIME; opt.interval.check = INTERVAL_CHECK_CR3; opt.interval.mode = INTERVAL_MODE_ARRAY; opt.interval_mode = 1; opt.summary_info = 1; opt.with_cr3_enumeration = 1; G.output_defined = 1; break; usage: fprintf(stderr, "Invalid input for cr3_schedule_time\n"); argp_usage(state); break; } case OPT_INTERVAL_CR3_SCHEDULE_TIME_ALL: opt.interval.output = INTERVAL_CR3_SCHEDULE_TIME; opt.interval.check = INTERVAL_CHECK_CR3; opt.interval.mode = INTERVAL_MODE_LIST; opt.interval_mode = 1; opt.summary_info = 1; opt.with_cr3_enumeration = 1; G.output_defined = 1; break; case OPT_INTERVAL_CR3_SCHEDULE_ORDERED: opt.interval.output = INTERVAL_CR3_SCHEDULE_ORDERED; opt.interval.check = INTERVAL_CHECK_CR3; opt.interval_mode = 1; opt.summary_info = 1; opt.with_cr3_enumeration = 1; G.output_defined = 1; break; case OPT_INTERVAL_CR3_SHORT_SUMMARY: { if(parse_array(arg, &opt.interval.array) < 0 || opt.interval.array.count != 1) goto usage; opt.interval.output = INTERVAL_CR3_SHORT_SUMMARY; opt.interval.check = INTERVAL_CHECK_CR3; opt.interval_mode = 1; opt.summary_info = 1; opt.with_cr3_enumeration = 1; G.output_defined = 1; break; } case OPT_INTERVAL_DOMAIN_TOTAL_TIME: { if(parse_array(arg, &opt.interval.array) < 0) goto idtt_usage; interval_table_alloc(opt.interval.array.count); opt.interval.output = INTERVAL_DOMAIN_TOTAL_TIME; opt.interval.check = INTERVAL_CHECK_DOMAIN; opt.interval.mode = INTERVAL_MODE_ARRAY; opt.interval_mode = 1; opt.summary_info = 1; G.output_defined = 1; break; idtt_usage: fprintf(stderr, "Invalid input for domain_total_time\n"); argp_usage(state); break; } case OPT_INTERVAL_DOMAIN_TOTAL_TIME_ALL: opt.interval.output = INTERVAL_DOMAIN_TOTAL_TIME; opt.interval.check = INTERVAL_CHECK_DOMAIN; opt.interval.mode = INTERVAL_MODE_LIST; opt.interval_mode = 1; opt.summary_info = 1; G.output_defined = 1; break; case OPT_INTERVAL_DOMAIN_SHORT_SUMMARY: { if((parse_array(arg, &opt.interval.array) < 0) || opt.interval.array.count != 1) argp_usage(state); opt.interval.output = INTERVAL_DOMAIN_SHORT_SUMMARY; opt.interval.check = INTERVAL_CHECK_DOMAIN; opt.interval_mode = 1; opt.summary_info = 1; G.output_defined = 1; break; } case OPT_INTERVAL_DOMAIN_GUEST_INTERRUPT: { if((parse_array(arg, &opt.interval.array) < 0) || opt.interval.array.count != 1) argp_usage(state); opt.interval.output = INTERVAL_DOMAIN_GUEST_INTERRUPT; opt.interval.check = INTERVAL_CHECK_DOMAIN; opt.interval_mode = 1; opt.summary_info = 1; G.output_defined = 1; break; } case OPT_INTERVAL_DOMAIN_GRANT_MAPS: { if((parse_array(arg, &opt.interval.array) < 0) || opt.interval.array.count != 1) argp_usage(state); opt.interval.output = INTERVAL_DOMAIN_GRANT_MAPS; opt.interval.check = INTERVAL_CHECK_DOMAIN; opt.interval_mode = 1; opt.summary_info = 1; G.output_defined = 1; break; } /* Summary group */ case OPT_SUMMARY: opt.summary = 1; opt.summary_info = 1; G.output_defined = 1; break; case OPT_REPORT_PCPU: opt.report_pcpu = 1; //opt.summary_info = 1; G.output_defined = 1; break; /* Guest info group */ case OPT_DEFAULT_GUEST_PAGING_LEVELS: { char *inval; opt.default_guest_paging_levels = (int)strtol(arg, &inval, 0); if ( inval == arg ) argp_usage(state); } break; case OPT_SYMBOL_FILE: /* FIXME - strcpy */ G.symbol_file = arg; break; /* Hardware info group */ case OPT_SVM_MODE: opt.svm_mode = 1; break; case OPT_CPU_HZ: parse_cpu_hz(arg); break; break; case OPT_TOLERANCE: { char * inval; opt.tolerance = (int)strtol(arg, &inval, 0); if( inval == arg ) argp_usage(state); if ( opt.tolerance > ERR_MAX_TOLERABLE ) { fprintf(stderr, "ERROR: Max tolerable error %d\n", ERR_MAX_TOLERABLE); exit(1); } printf("Tolerating errors at or below %d\n", opt.tolerance); } break; case OPT_PROGRESS: opt.progress = 1; break; case OPT_TSC_LOOP_FATAL: opt.tsc_loop_fatal = 1; break; case ARGP_KEY_ARG: { /* FIXME - strcpy */ if (state->arg_num == 0) G.trace_file = arg; else argp_usage(state); } break; case ARGP_KEY_END: { if(opt.interval_mode) { opt.interval.cycles = ( opt.interval.msec * opt.cpu_hz ) / 1000 ; interval_header(); } if(!G.output_defined) { fprintf(stderr, "No output defined, using summary.\n"); opt.summary = 1; opt.summary_info = 1; } fprintf(stderr, "Using %s hardware-assisted virtualization.\n", opt.svm_mode?"SVM":"VMX"); } break; default: return ARGP_ERR_UNKNOWN; } return 0; } const struct argp_option cmd_opts[] = { /* Dump group */ { .name = "dump-all", .key = OPT_DUMP_ALL, .group = OPT_GROUP_DUMP, .doc = "Dump all records as they come in.", }, { .name = "dump-raw-reads", .key = OPT_DUMP_RAW_READS, .group = OPT_GROUP_DUMP, .doc = "Dump raw data as it's read from disk. Useful mainly for debugging the analysis tool.", }, { .name = "dump-no-processing", .key = OPT_DUMP_NO_PROCESSING, .group = OPT_GROUP_DUMP, .doc = "Don't do any processing on records other than cpu changes. Implies dump-raw-reads (or you wouldn't get anything).", }, { .name = "dump-raw-process", .key = OPT_DUMP_RAW_PROCESS, .group = OPT_GROUP_DUMP, .doc = "Dump raw data as it's processed. Useful mainly for debugging the analysis tool.", }, { .name = "dump-ipi-latency", .key = OPT_DUMP_IPI_LATENCY, .group = OPT_GROUP_DUMP, .doc = "Dump IPI latency info as IPIs are delivered (vector 0xd1 only).", }, { .name = "dump-trace-volume-on-lost-record", .key = OPT_DUMP_TRACE_VOLUME_ON_LOST_RECORD, .group = OPT_GROUP_DUMP, .doc = "Dump the volume of trace types in the previous cpu buffer when a lost record is created.", }, { .name = "dump-show-power-states", .key = OPT_DUMP_SHOW_POWER_STATES, .group = OPT_GROUP_DUMP, .doc = "Show the power-state of the physical cpu when dumping output.", }, /* Extra processing group */ { .name = "with-cr3-enumeration", .key = OPT_WITH_CR3_ENUMERATION, .group = OPT_GROUP_EXTRA, .doc = "Keep track of per-cr3 values", }, { .name = "with-pio-enumeration", .key = OPT_WITH_PIO_ENUMERATION, .group = OPT_GROUP_EXTRA, .doc = "Report summary info on indiviaul IO addresses", }, { .name = "with-mmio-enumeration", .key = OPT_WITH_MMIO_ENUMERATION, .group = OPT_GROUP_EXTRA, .doc = "Report summary info on indiviaul MMIO addresses.", }, { .name = "with-interrupt-eip-enumeration", .key = OPT_WITH_INTERRUPT_EIP_ENUMERATION, .arg = "vector", .group = OPT_GROUP_EXTRA, .doc = "Report a summary on eips interrupted by specified vector.", }, { .name = "scatterplot-interrupt-eip", .key = OPT_SCATTERPLOT_INTERRUPT_EIP, .arg = "vector", .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of eips as a function of time.", }, { .name = "scatterplot-extint-cycles", .key = OPT_SCATTERPLOT_EXTINT_CYCLES, .arg = "vector", .group = OPT_GROUP_EXTRA, .doc = "Output a scatterplot of vmexit cycles for external interrupts of the given vector as a funciton of time.", }, { .name = "scatterplot-unpin-promote", .key = OPT_SCATTERPLOT_UNPIN_PROMOTE, .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of unpins and promotions. If " \ "--with-cr3-enumeration is included, promotions include current cr3.", }, { .name = "scatterplot-cr3-switch", .key = OPT_SCATTERPLOT_CR3_SWITCH, .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of cr3 switches.", }, { .name = "scatterplot-wake-to-halt", .key = OPT_SCATTERPLOT_WAKE_TO_HALT, .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of wake-to-halt.", }, { .name = "scatterplot-vmexit-eip", .key = OPT_SCATTERPLOT_VMEXIT_EIP, .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of vmexit eips.", }, { .name = "scatterplot-io", .key = OPT_SCATTERPLOT_IO, .arg = "port", .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of io latencies for givein address as a function of time.", }, { .name = "scatterplot-runstate", .key = OPT_SCATTERPLOT_RUNSTATE, .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of runstate.", }, { .name = "scatterplot-runstate-time", .key = OPT_SCATTERPLOT_RUNSTATE_TIME, .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of time in a runstate.", }, { .name = "scatterplot-pcpu", .key = OPT_SCATTERPLOT_PCPU, .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of which pcpu vcpus are run on.", }, { .name = "scatterplot-rdtsc", .key = OPT_SCATTERPLOT_RDTSC, .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of rdtsc values.", }, { .name = "scatterplot-irq", .key = OPT_SCATTERPLOT_IRQ, .group = OPT_GROUP_EXTRA, .doc = "Output scatterplot of irqs on pcpus.", }, { .name = "histogram-interrupt-eip", .key = OPT_HISTOGRAM_INTERRUPT_EIP, .arg = "vector[,increment]", .group = OPT_GROUP_EXTRA, .doc = "Output histograms of eips.", }, { .name = "interval", .key = OPT_INTERVAL_LENGTH, .arg = "sec", .group = OPT_GROUP_INTERVAL, .doc = "Interval length to do time-based graphs, in seconds", }, { .name = "interval-cr3-schedule-time", .key = OPT_INTERVAL_CR3_SCHEDULE_TIME, .arg = "gmfn[,gmfn...]", .group = OPT_GROUP_INTERVAL, .doc = "Print a csv with the listed cr3 value(s) every interval.", }, { .name = "interval-cr3-schedule-time-all", .key = OPT_INTERVAL_CR3_SCHEDULE_TIME_ALL, .group = OPT_GROUP_INTERVAL, .doc = "Print a csv with all cr3 values every interval.", }, { .name = "interval-cr3-schedule-ordered", .key = OPT_INTERVAL_CR3_SCHEDULE_ORDERED, .group = OPT_GROUP_INTERVAL, .doc = "Print summary with the top 10 cr3 values every interval.", }, { .name = "interval-cr3-short-summary", .key = OPT_INTERVAL_CR3_SHORT_SUMMARY, .arg = "gmfn", .group = OPT_GROUP_INTERVAL, .doc = "Print a csv with the hvm short summary of cr3 value every interval.", }, { .name = "interval-domain-total-time", .key = OPT_INTERVAL_DOMAIN_TOTAL_TIME, .arg = "domain[,domain...]", .group = OPT_GROUP_INTERVAL, .doc = "Print a csv with the listed domain(s) total runtime every interval.", }, { .name = "interval-domain-total-time-all", .key = OPT_INTERVAL_DOMAIN_TOTAL_TIME_ALL, .group = OPT_GROUP_INTERVAL, .doc = "Print a csv with all domains every interval.", }, { .name = "interval-domain-short-summary", .key = OPT_INTERVAL_DOMAIN_SHORT_SUMMARY, .arg = "domain-id", .group = OPT_GROUP_INTERVAL, .doc = "Print a csv with the hvm short summary of given domain every interval.", }, { .name = "interval-domain-guest-interrupt", .key = OPT_INTERVAL_DOMAIN_GUEST_INTERRUPT, .arg = "domain-id", .group = OPT_GROUP_INTERVAL, .doc = "Print a csv with the guest interrupt count of given domain every interval.", }, { .name = "interval-domain-grant-maps", .key = OPT_INTERVAL_DOMAIN_GRANT_MAPS, .arg = "domain-id", .group = OPT_GROUP_INTERVAL, .doc = "Print a csv with the grant maps done on behalf of a given domain every interval.", }, /* Summary group */ { .name = "show-default-domain-summary", .key = OPT_SHOW_DEFAULT_DOMAIN_SUMMARY, .group = OPT_GROUP_SUMMARY, .doc = "Show default domain information on summary", }, { .name = "mmio-enumeration-skip-vga", .key = OPT_MMIO_ENUMERATION_SKIP_VGA, .arg = "[0|1]", .group = OPT_GROUP_SUMMARY, .doc = "Control whether we enumerate MMIO accesses to the VGA area, which can be extremly high during boot. Default: 0", }, { .name = "sample-size", .key = OPT_SAMPLE_SIZE, .arg = "size", .group = OPT_GROUP_SUMMARY, .doc = "Start with [size] samples for percentile purposes. Enter 0 to" \ "disable. Default 1024.", }, { .name = "sample-max", .key = OPT_SAMPLE_MAX, .arg = "size", .group = OPT_GROUP_SUMMARY, .doc = "Do not allow sample to grow beyond [size] samples for percentile"\ " purposes. Enter 0 for no limit.", }, { .name = "summary", .key = OPT_SUMMARY, .group = OPT_GROUP_SUMMARY, .doc = "Output a summary", }, { .name = "report-pcpu", .key = OPT_REPORT_PCPU, .group = OPT_GROUP_SUMMARY, .doc = "Report utilization for pcpus", }, /* Guest info */ { .name = "default-guest-paging-levels", .key = OPT_DEFAULT_GUEST_PAGING_LEVELS, .group = OPT_GROUP_GUEST, .arg = "L", .doc = "Default guest paging levels. Mainly necessary for Rio, as Miami traces include guest paging levels where appropriate.", }, { .name = "symbol-file", .key = OPT_SYMBOL_FILE, .group = OPT_GROUP_GUEST, .arg = "filename", .doc = "A symbol file for interpreting guest eips.", }, /* Hardware info */ { .name = "cpu-hz", .key = OPT_CPU_HZ, .group = OPT_GROUP_HARDWARE, .arg = "HZ", .doc = "Cpu speed of the tracing host, used to convert tsc into seconds.", }, { .name = "svm-mode", .key = OPT_SVM_MODE, .group = OPT_GROUP_HARDWARE, .doc = "Assume AMD SVM-style vmexit error codes. (Default is Intel VMX.)", }, { .name = "progress", .key = OPT_PROGRESS, .doc = "Progress dialog. Requires the zenity (GTK+) executable.", }, { .name = "tsc-loop-fatal", .key = OPT_TSC_LOOP_FATAL, .doc = "Stop processing and exit if tsc skew tracking detects a dependency loop.", }, { .name = "tolerance", .key = OPT_TOLERANCE, .arg = "errlevel", .doc = "Sets tolerance for errors found in the file. Default is 3; max is 6.", }, { 0 }, }; const struct argp parser_def = { .options = cmd_opts, .parser = cmd_parser, .args_doc = "[trace file]", .doc = "", }; const char *argp_program_bug_address = "George Dunlap "; int main(int argc, char *argv[]) { /* Start with warn at stderr. */ warn = stderr; argp_parse(&parser_def, argc, argv, 0, NULL, NULL); if (G.trace_file == NULL) exit(1); if ( (G.fd = open(G.trace_file, O_RDONLY)) < 0) { perror("open"); error(ERR_SYSTEM, NULL); } else { struct stat s; fstat(G.fd, &s); G.file_size = s.st_size; } if ( (G.mh = mread_init(G.fd)) == NULL ) perror("mread"); if (G.symbol_file != NULL) parse_symbol_file(G.symbol_file); if(opt.dump_all) warn = stdout; init_pcpus(); if(opt.progress) progress_init(); process_records(); if(opt.interval_mode) interval_tail(); if(opt.summary) summary(); if(opt.report_pcpu) report_pcpu(); if(opt.progress) progress_finish(); return 0; } /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xentrace/mread.c0000664000175000017500000001050513256712137014663 0ustar smbsmb#include #include #include #include #include #include #include #include "mread.h" mread_handle_t mread_init(int fd) { struct stat s; mread_handle_t h; h=malloc(sizeof(struct mread_ctrl)); if (!h) { perror("malloc"); exit(1); } bzero(h, sizeof(struct mread_ctrl)); h->fd = fd; fstat(fd, &s); h->file_size = s.st_size; return h; } ssize_t mread64(mread_handle_t h, void *rec, ssize_t len, off_t offset) { /* Idea: have a "cache" of N mmaped regions. If the offset is * in one of the regions, just copy it. If not, evict one of the * regions and map the appropriate range. * * Basic algorithm: * - See if the offset is in one of the regions * - If not, map it * - evict an old region * - map the new region * - Copy */ char * b=NULL; int bind=-1; off_t boffset=0; ssize_t bsize; #define dprintf(x...) //#define dprintf fprintf dprintf(warn, "%s: offset %llx len %d\n", __func__, offset, len); if ( offset > h->file_size ) { dprintf(warn, " offset > file size %llx, returning 0\n", h->file_size); return 0; } if ( offset + len > h->file_size ) { dprintf(warn, " offset+len > file size %llx, truncating\n", h->file_size); len = h->file_size - offset; } /* Try to find the offset in our range */ dprintf(warn, " Trying last, %d\n", last); if ( h->map[h->last].buffer && (offset & MREAD_BUF_MASK) == h->map[h->last].start_offset ) { bind=h->last; goto copy; } /* Scan to see if it's anywhere else */ dprintf(warn, " Scanning\n"); for(bind=0; bindmap[bind].buffer && (offset & MREAD_BUF_MASK) == h->map[bind].start_offset ) { dprintf(warn, " Found, index %d\n", bind); break; } /* If we didn't find it, evict someone and map it */ if ( bind == MREAD_MAPS ) { dprintf(warn, " Clock\n"); while(1) { h->clock++; if(h->clock >= MREAD_MAPS) h->clock=0; dprintf(warn, " %d\n", h->clock); if(h->map[h->clock].buffer == NULL) { dprintf(warn, " Buffer null, using\n"); break; } if(!h->map[h->clock].accessed) { dprintf(warn, " Not accessed, using\n"); break; } h->map[h->clock].accessed=0; } if(h->map[h->clock].buffer) { dprintf(warn, " Unmapping\n"); munmap(h->map[h->clock].buffer, MREAD_BUF_SIZE); } /* FIXME: Try MAP_HUGETLB? */ /* FIXME: Make sure this works on large files... */ h->map[h->clock].start_offset = offset & MREAD_BUF_MASK; dprintf(warn, " Mapping %llx from offset %llx\n", MREAD_BUF_SIZE, h->map[h->clock].start_offset); h->map[h->clock].buffer = mmap(NULL, MREAD_BUF_SIZE, PROT_READ, MAP_SHARED, h->fd, h->map[h->clock].start_offset); dprintf(warn, " mmap returned %p\n", h->map[h->clock].buffer); if ( h->map[h->clock].buffer == MAP_FAILED ) { h->map[h->clock].buffer = NULL; perror("mmap"); exit(1); } bind = h->clock; } h->last=bind; copy: h->map[bind].accessed=1; b=h->map[bind].buffer; boffset=offset - h->map[bind].start_offset; if ( boffset + len > MREAD_BUF_SIZE ) bsize = MREAD_BUF_SIZE - boffset; else bsize = len; dprintf(warn, " Using index %d, buffer at %p, buffer offset %llx len %d\n", bind, b, boffset, bsize); bcopy(b+boffset, rec, bsize); /* Handle the boundary case; make sure this is after doing anything * with the static variables*/ if ( len > bsize ) { dprintf(warn, " Finishing up by reading l %d o %llx\n", len-bsize, offset+bsize); mread64(h, rec+bsize, len-bsize, offset+bsize); } /* FIXME: ?? */ return len; #undef dprintf } xen-4.9.2/tools/xentrace/formats0000664000175000017500000005123013256712137015025 0ustar smbsmb0x00000000 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) unknown (0x%(event)016x) [ 0x%(1)08x 0x%(2)08x 0x%(3)08x 0x%(4)08x 0x%(5)08x 0x%(6)08x 0x%(7)08x ] 0x0001f001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) lost_records 0x%(1)08x 0x0001f002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) wrap_buffer 0x%(1)08x 0x0001f003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) cpu_change 0x%(1)08x 0x0001f004 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) trace_irq [ vector = %(1)d, count = %(2)d, tot_cycles = 0x%(3)08x, max_cycles = 0x%(4)08x ] 0x00021002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) continue_running [ dom:vcpu = 0x%(1)08x ] 0x00021011 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) running_to_runnable [ dom:vcpu = 0x%(1)08x ] 0x00021021 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) running_to_blocked [ dom:vcpu = 0x%(1)08x ] 0x00021031 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) running_to_offline [ dom:vcpu = 0x%(1)08x ] 0x00021101 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) runnable_to_running [ dom:vcpu = 0x%(1)08x ] 0x00021121 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) runnable_to_blocked [ dom:vcpu = 0x%(1)08x ] 0x00021131 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) runnable_to_offline [ dom:vcpu = 0x%(1)08x ] 0x00021201 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) blocked_to_running [ dom:vcpu = 0x%(1)08x ] 0x00021211 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) blocked_to_runnable [ dom:vcpu = 0x%(1)08x ] 0x00021231 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) blocked_to_offline [ dom:vcpu = 0x%(1)08x ] 0x00021301 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) offline_to_running [ dom:vcpu = 0x%(1)08x ] 0x00021311 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) offline_to_runnable [ dom:vcpu = 0x%(1)08x ] 0x00021321 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) offline_to_blocked [ dom:vcpu = 0x%(1)08x ] 0x00028001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) sched_add_domain [ domid = 0x%(1)08x ] 0x00028002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) sched_rem_domain [ domid = 0x%(1)08x ] 0x00028003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) domain_sleep [ dom:vcpu = 0x%(1)04x%(2)04x ] 0x00028004 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) domain_wake [ dom:vcpu = 0x%(1)04x%(2)04x ] 0x00028005 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) do_yield [ dom:vcpu = 0x%(1)04x%(2)04x ] 0x00028006 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) do_block [ dom:vcpu = 0x%(1)04x%(2)04x ] 0x00028007 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) domain_shutdown [ dom:vcpu = 0x%(1)04x%(2)04x, reason = 0x%(3)08x ] 0x00028008 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) sched_ctl 0x00028009 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) sched_adjdom [ domid = 0x%(1)08x ] 0x0002800a CPU%(cpu)d %(tsc)d (+%(reltsc)8d) __enter_scheduler [ prev = 0x%(1)04x%(2)04x, next = 0x%(3)04x%(4)04x ] 0x0002800b CPU%(cpu)d %(tsc)d (+%(reltsc)8d) s_timer_fn 0x0002800c CPU%(cpu)d %(tsc)d (+%(reltsc)8d) t_timer_fn 0x0002800d CPU%(cpu)d %(tsc)d (+%(reltsc)8d) dom_timer_fn 0x0002800e CPU%(cpu)d %(tsc)d (+%(reltsc)8d) switch_infprev [ dom:vcpu = 0x%(1)04x%(2)04x, runtime = %(3)d ] 0x0002800f CPU%(cpu)d %(tsc)d (+%(reltsc)8d) switch_infnext [ new_dom:vcpu = 0x%(1)04x%(2)04x, time = %(3)d, r_time = %(4)d ] 0x00028010 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) domain_shutdown_code [ dom:vcpu = 0x%(1)04x%(2)04x, reason = 0x%(3)08x ] 0x00028011 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) switch_infcont [ dom:vcpu = 0x%(1)04x%(2)04x, runtime = %(3)d, r_time = %(4)d ] 0x00022001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:sched_tasklet 0x00022002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:account_start [ dom:vcpu = 0x%(1)04x%(2)04x, active = %(3)d ] 0x00022003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:account_stop [ dom:vcpu = 0x%(1)04x%(2)04x, active = %(3)d ] 0x00022004 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:stolen_vcpu [ dom:vcpu = 0x%(2)04x%(3)04x, from = %(1)d ] 0x00022005 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:picked_cpu [ dom:vcpu = 0x%(1)04x%(2)04x, cpu = %(3)d ] 0x00022006 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:tickle [ cpu = %(1)d ] 0x00022007 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:boost [ dom:vcpu = 0x%(1)04x%(2)04x ] 0x00022008 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:unboost [ dom:vcpu = 0x%(1)04x%(2)04x ] 0x00022009 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:schedule [ cpu[16]:tasklet[8]:idle[8] = %(1)08x ] 0x0002200A CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:ratelimit [ dom:vcpu = 0x%(1)08x, runtime = %(2)d ] 0x0002200B CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched:steal_check [ peer_cpu = %(1)d, checked = %(2)d ] 0x00022201 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:tick 0x00022202 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:runq_pos [ dom:vcpu = 0x%(1)08x, pos = %(2)d] 0x00022203 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:credit burn [ dom:vcpu = 0x%(1)08x, credit = %(2)d, delta = %(3)d ] 0x00022204 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:credit_add 0x00022205 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:tickle_check [ dom:vcpu = 0x%(1)08x, credit = %(2)d ] 0x00022206 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:tickle [ cpu = %(1)d ] 0x00022207 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:credit_reset [ dom:vcpu = 0x%(1)08x, cr_start = %(2)d, cr_end = %(3)d, mult = %(4)d ] 0x00022208 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:sched_tasklet 0x00022209 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:update_load 0x0002220a CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:runq_assign [ dom:vcpu = 0x%(1)08x, rq_id = %(2)d ] 0x0002220b CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:updt_vcpu_load [ dom:vcpu = 0x%(3)08x, vcpuload = 0x%(2)08x%(1)08x, wshift = %(4)d ] 0x0002220c CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:updt_runq_load [ rq_load[16]:rq_id[8]:wshift[8] = 0x%(5)08x, rq_avgload = 0x%(2)08x%(1)08x, b_avgload = 0x%(4)08x%(3)08x ] 0x0002220d CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:tickle_new [ dom:vcpu = 0x%(1)08x, processor = %(2)d credit = %(3)d ] 0x0002220e CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:runq_max_weight [ rq_id[16]:max_weight[16] = 0x%(1)08x ] 0x0002220f CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:migrrate [ dom:vcpu = 0x%(1)08x, rq_id[16]:trq_id[16] = 0x%(2)08x ] 0x00022210 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:load_check [ lrq_id[16]:orq_id[16] = 0x%(1)08x, delta = %(2)d ] 0x00022211 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:load_balance [ l_bavgload = 0x%(2)08x%(1)08x, o_bavgload = 0x%(4)08x%(3)08x, lrq_id[16]:orq_id[16] = 0x%(5)08x ] 0x00022212 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:pick_cpu [ b_avgload = 0x%(2)08x%(1)08x, dom:vcpu = 0x%(3)08x, rq_id[16]:new_cpu[16] = %(4)d ] 0x00022213 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:runq_candidate [ dom:vcpu = 0x%(1)08x, credit = %(4)d, skipped_vcpus = %(3)d, tickled_cpu = %(2)d ] 0x00022214 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:schedule [ rq:cpu = 0x%(1)08x, tasklet[8]:idle[8]:smt_idle[8]:tickled[8] = %(2)08x ] 0x00022215 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:ratelimit [ dom:vcpu = 0x%(1)08x, runtime = %(2)d ] 0x00022216 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) csched2:runq_cand_chk [ dom:vcpu = 0x%(1)08x ] 0x00022801 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) rtds:tickle [ cpu = %(1)d ] 0x00022802 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) rtds:runq_pick [ dom:vcpu = 0x%(1)08x, cur_deadline = 0x%(3)08x%(2)08x, cur_budget = 0x%(5)08x%(4)08x ] 0x00022803 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) rtds:burn_budget [ dom:vcpu = 0x%(1)08x, cur_budget = 0x%(3)08x%(2)08x, delta = %(4)d ] 0x00022804 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) rtds:repl_budget [ dom:vcpu = 0x%(1)08x, cur_deadline = 0x%(3)08x%(2)08x, cur_budget = 0x%(5)08x%(4)08x ] 0x00022805 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) rtds:sched_tasklet 0x00022806 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) rtds:schedule [ cpu[16]:tasklet[8]:idle[4]:tickled[4] = %(1)08x ] 0x00041001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) domain_create [ dom = 0x%(1)08x ] 0x00041002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) domain_destroy [ dom = 0x%(1)08x ] 0x00081001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) VMENTRY 0x00081002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) VMEXIT [ exitcode = 0x%(1)08x, rIP = 0x%(2)08x ] 0x00081102 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) VMEXIT [ exitcode = 0x%(1)08x, rIP = 0x%(3)08x%(2)08x ] 0x00081401 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) nVMENTRY 0x00081402 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) nVMEXIT [ exitcode = 0x%(1)08x, rIP = 0x%(2)08x ] 0x00081502 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) nVMEXIT [ exitcode = 0x%(1)08x, rIP = 0x%(3)08x%(2)08x ] 0x00082001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) PF_XEN [ errorcode = 0x%(2)02x, virt = 0x%(1)08x ] 0x00082101 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) PF_XEN [ errorcode = 0x%(3)02x, virt = 0x%(2)08x%(1)08x ] 0x00082002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) PF_INJECT [ errorcode = 0x%(1)02x, virt = 0x%(2)08x ] 0x00082102 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) PF_INJECT [ errorcode = 0x%(1)02x, virt = 0x%(3)08x%(2)08x ] 0x00082003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) INJ_EXC [ vector = 0x%(1)02x, errorcode = 0x%(2)04x ] 0x00082004 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) INJ_VIRQ [ vector = 0x%(1)02x, fake = %(2)d ] 0x00082005 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) REINJ_VIRQ [ vector = 0x%(1)02x ] 0x00082006 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) IO_READ [ port = 0x%(1)04x, size = %(2)d ] 0x00082007 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) IO_WRITE [ port = 0x%(1)04x, size = %(2)d ] 0x00082008 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) CR_READ [ CR# = %(1)d, value = 0x%(2)08x ] 0x00082108 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) CR_READ [ CR# = %(1)d, value = 0x%(3)08x%(2)08x ] 0x00082009 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) CR_WRITE [ CR# = %(1)d, value = 0x%(2)08x ] 0x00082109 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) CR_WRITE [ CR# = %(1)d, value = 0x%(3)08x%(2)08x ] 0x0008200A CPU%(cpu)d %(tsc)d (+%(reltsc)8d) DR_READ 0x0008200B CPU%(cpu)d %(tsc)d (+%(reltsc)8d) DR_WRITE 0x0008200C CPU%(cpu)d %(tsc)d (+%(reltsc)8d) MSR_READ [ MSR# = 0x%(1)08x, value = 0x%(3)08x%(2)08x ] 0x0008200D CPU%(cpu)d %(tsc)d (+%(reltsc)8d) MSR_WRITE [ MSR# = 0x%(1)08x, value = 0x%(3)08x%(2)08x ] 0x0008200E CPU%(cpu)d %(tsc)d (+%(reltsc)8d) CPUID [ func = 0x%(1)08x, eax = 0x%(2)08x, ebx = 0x%(3)08x, ecx=0x%(4)08x, edx = 0x%(5)08x ] 0x0008200F CPU%(cpu)d %(tsc)d (+%(reltsc)8d) INTR [ vector = 0x%(1)02x ] 0x00082010 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) NMI 0x00082011 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) SMI 0x00082012 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) VMMCALL [ func = 0x%(1)08x ] 0x00082013 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) HLT [ intpending = %(1)d ] 0x00082014 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) INVLPG [ is invlpga? = %(1)d, virt = 0x%(2)08x ] 0x00082114 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) INVLPG [ is invlpga? = %(1)d, virt = 0x%(3)08x%(2)08x ] 0x00082015 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) MCE 0x00082016 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) IOPORT_READ [ port = 0x%(1)04x, data = 0x%(2)08x ] 0x00082216 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) IOPORT_WRITE [ port = 0x%(1)04x, data = 0x%(2)08x ] 0x00082017 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) MMIO_READ [ port = 0x%(1)08x, data = 0x%(2)08x ] 0x00082217 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) MMIO_WRITE [ port = 0x%(1)08x, data = 0x%(2)08x ] 0x00082018 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) CLTS 0x00082019 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) LMSW [ value = 0x%(1)08x ] 0x00082119 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) LMSW [ value = 0x%(2)08x%(1)08x ] 0x0008201a CPU%(cpu)d %(tsc)d (+%(reltsc)8d) RDTSC [ value = 0x%(2)08x%(1)08x ] 0x00082020 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) INTR_WINDOW [ value = 0x%(1)08x ] 0x00082021 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) NPF [ gpa = 0x%(2)08x%(1)08x mfn = 0x%(4)08x%(3)08x qual = 0x%(5)04x p2mt = 0x%(6)04x ] 0x00082023 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) TRAP [ vector = 0x%(1)02x ] 0x0010f001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) page_grant_map [ domid = %(1)d ] 0x0010f002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) page_grant_unmap [ domid = %(1)d ] 0x0010f003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) page_grant_transfer [ domid = %(1)d ] 0x00201001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) hypercall [ eip = 0x%(1)08x, eax = 0x%(2)08x ] 0x00201101 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) hypercall [ rip = 0x%(2)08x%(1)08x, eax = 0x%(3)08x ] 0x00201003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) trap [ eip = 0x%(1)08x, trapnr:error = 0x%(2)08x ] 0x00201103 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) trap [ rip = 0x%(2)08x%(1)08x, trapnr:error = 0x%(3)08x ] 0x00201004 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) page_fault [ eip = 0x%(1)08x, addr = 0x%(2)08x, error = 0x%(3)08x ] 0x00201104 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) page_fault [ rip = 0x%(2)08x%(1)08x, addr = 0x%(4)08x%(3)08x, error = 0x%(5)08x ] 0x00201005 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) forced_invalid_op [ eip = 0x%(1)08x ] 0x00201105 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) forced_invalid_op [ rip = 0x%(2)08x%(1)08x ] 0x00201006 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) emulate_privop [ eip = 0x%(1)08x ] 0x00201106 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) emulate_privop [ rip = 0x%(2)08x%(1)08x ] 0x00201007 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) emulate_4G [ eip = 0x%(1)08x ] 0x00201107 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) emulate_4G [ rip = 0x%(2)08x%(1)08x ] 0x00201008 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) math_state_restore 0x00201108 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) math_state_restore 0x00201009 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) paging_fixup [ eip = 0x%(1)08x, addr = 0x%(2)08x ] 0x00201109 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) paging_fixup [ rip = 0x%(2)08x%(1)08x, addr = 0x%(4)08x%(3)08x ] 0x0020100a CPU%(cpu)d %(tsc)d (+%(reltsc)8d) gdt_ldt_mapping_fault [ eip = 0x%(1)08x, offset = 0x%(2)08x ] 0x0020110a CPU%(cpu)d %(tsc)d (+%(reltsc)8d) gdt_ldt_mapping_fault [ rip = 0x%(2)08x%(1)08x, offset = 0x%(4)08x%(3)08x ] 0x0020100b CPU%(cpu)d %(tsc)d (+%(reltsc)8d) ptwr_emulation [ addr = 0x%(3)08x, eip = 0x%(4)08x, npte = 0x%(2)08x%(1)08x ] 0x0020110b CPU%(cpu)d %(tsc)d (+%(reltsc)8d) ptwr_emulation [ addr = 0x%(4)08x%(3)08x, rip = 0x%(6)08x%(5)08x, npte = 0x%(2)08x%(1)08x ] 0x0020100c CPU%(cpu)d %(tsc)d (+%(reltsc)8d) ptwr_emulation_pae [ addr = 0x%(3)08x, eip = 0x%(4)08x, npte = 0x%(2)08x%(1)08x ] 0x0020110c CPU%(cpu)d %(tsc)d (+%(reltsc)8d) ptwr_emulation_pae [ addr = 0x%(4)08x%(3)08x, rip = 0x%(6)08x%(5)08x, npte = 0x%(2)08x%(1)08x ] 0x0020100d CPU%(cpu)d %(tsc)d (+%(reltsc)8d) hypercall [ op = 0x%(1)08x ] 0x0020200e CPU%(cpu)d %(tsc)d (+%(reltsc)8d) hypercall [ op = 0x%(1)08x ] 0x0040f001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_not_shadow [ gl1e = 0x%(2)08x%(1)08x, va = 0x%(3)08x, flags = 0x%(4)08x ] 0x0040f101 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_not_shadow [ gl1e = 0x%(2)08x%(1)08x, va = 0x%(4)08x%(3)08x, flags = 0x%(5)08x ] 0x0040f002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_fast_propagate [ va = 0x%(1)08x ] 0x0040f102 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_fast_propagate [ va = 0x%(2)08x%(1)08x ] 0x0040f003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_fast_mmio [ va = 0x%(1)08x ] 0x0040f103 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_fast_mmio [ va = 0x%(2)08x%(1)08x ] 0x0040f004 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_false_fast_path [ va = 0x%(1)08x ] 0x0040f104 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_false_fast_path [ va = 0x%(2)08x%(1)08x ] 0x0040f005 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_mmio [ va = 0x%(1)08x ] 0x0040f105 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_mmio [ va = 0x%(2)08x%(1)08x ] 0x0040f006 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_fixup [ gl1e = 0x%(1)08x, va = 0x%(2)08x, flags = 0x%(3)08x ] 0x0040f106 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_fixup [ gl1e = 0x%(2)08x%(1)08x, va = 0x%(4)08x%(3)08x, flags = 0x%(3)08x ] 0x0040f007 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_domf_dying [ va = 0x%(1)08x ] 0x0040f107 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_domf_dying [ va = 0x%(2)08x%(1)08x ] 0x0040f008 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate [ gl1e = 0x%(1)08x, write_val = 0x%(2)08x, va = 0x%(3)08x, flags = 0x%(4)08x ] 0x0040f108 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate [ gl1e = 0x%(2)08x%(1)08x, write_val = 0x%(4)08x%(3)08x, va = 0x%(6)08x%(5)08x, flags = 0x%(7)08x ] 0x0040f009 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_unshadow_user [ va = 0x%(1)08x, gfn = 0x%(2)08x ] 0x0040f109 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_unshadow_user [ va = 0x%(2)08x%(1)08x, gfn = 0x%(4)08x%(3)08x ] 0x0040f00a CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_unshadow_evtinj [ va = 0x%(1)08x, gfn = 0x%(2)08x ] 0x0040f10a CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_unshadow_evtinj [ va = 0x%(2)08x%(1)08x, gfn = 0x%(4)08x%(3)08x ] 0x0040f00b CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_unshadow_unhandled [ va = 0x%(1)08x, gfn = 0x%(2)08x ] 0x0040f10b CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_unshadow_unhandled [ va = 0x%(2)08x%(1)08x, gfn = 0x%(4)08x%(3)08x ] 0x0040f00c CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_wrmap_bf [ gfn = 0x%(1)08x ] 0x0040f10c CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_wrmap_bf [ gfn = 0x%(2)08x%(1)08x ] 0x0040f00d CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_prealloc_unpin [ gfn = 0x%(1)08x ] 0x0040f10d CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_prealloc_unpin [ gfn = 0x%(2)08x%(1)08x ] 0x0040f00e CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_resync_full [ gfn = 0x%(1)08x ] 0x0040f10e CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_resync_full [ gfn = 0x%(2)08x%(1)08x ] 0x0040f00f CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_resync_only [ gfn = 0x%(1)08x ] 0x0040f10f CPU%(cpu)d %(tsc)d (+%(reltsc)8d) shadow_emulate_resync_only [ gfn = 0x%(2)08x%(1)08x ] 0x00801001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) cpu_freq_change [ %(1)dMHz -> %(2)dMHz ] 0x00801002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) cpu_idle_entry [ C0 -> C%(1)d, acpi_pm_tick = %(2)d, expected = %(3)dus, predicted = %(4)dus ] 0x00801003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) cpu_idle_exit [ C%(1)d -> C0, acpi_pm_tick = %(2)d, irq = %(3)d %(4)d %(5)d %(6)d ] 0x00802001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) cleanup_move_delayed [ irq = %(1)d, vector 0x%(2)x on CPU%(3)d ] 0x00802002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) cleanup_move [ irq = %(1)d, vector 0x%(2)x on CPU%(3)d ] 0x00802003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) bind_vector [ irq = %(1)d = vector 0x%(2)x, CPU mask: 0x%(3)08x ] 0x00802004 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) clear_vector [ irq = %(1)d = vector 0x%(2)x, CPU mask: 0x%(3)08x ] 0x00802005 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) move_vector [ irq = %(1)d had vector 0x%(2)x on CPU%(3)d ] 0x00802006 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) assign_vector [ irq = %(1)d = vector 0x%(2)x, CPU mask: 0x%(3)08x ] 0x00802007 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) bogus_vector [ 0x%(1)x ] 0x00802008 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) do_irq [ irq = %(1)d, began = %(2)dus, ended = %(3)dus ] 0x00084001 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) hpet create [ tn = %(1)d, irq = %(2)d, delta = 0x%(4)08x%(3)08x, period = 0x%(6)08x%(5)08x ] 0x00084002 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) pit create [ delta = 0x%(1)016x, period = 0x%(2)016x ] 0x00084003 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) rtc create [ delta = 0x%(1)016x , period = 0x%(2)016x ] 0x00084004 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) vlapic create [ delta = 0x%(2)08x%(1)08x , period = 0x%(4)08x%(3)08x, irq = %(5)d ] 0x00084005 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) hpet destroy [ tn = %(1)d ] 0x00084006 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) pit destroy [ ] 0x00084007 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) rtc destroy [ ] 0x00084008 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) vlapic destroy [ ] 0x00084009 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) pit callback [ ] 0x0008400a CPU%(cpu)d %(tsc)d (+%(reltsc)8d) vlapic callback [ ] 0x0008400b CPU%(cpu)d %(tsc)d (+%(reltsc)8d) vpic_update_int_output [ int_output = %(1)d, is_master = %(2)d, irq = %(3)d ] 0x0008400c CPU%(cpu)d %(tsc)d (+%(reltsc)8d) vpic vcpu_kick [ irq = %(1)d ] 0x0008400d CPU%(cpu)d %(tsc)d (+%(reltsc)8d) __vpic_intack [ is_master = %(1)d, irq = %(2)d ] 0x0008400e CPU%(cpu)d %(tsc)d (+%(reltsc)8d) vpic_irq_positive_edge [ irq = %(1)d ] 0x0008400f CPU%(cpu)d %(tsc)d (+%(reltsc)8d) vpic_irq_negative_edge [ irq = %(1)d ] 0x00084010 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) vpic_ack_pending_irq [ accept_pic_intr = %(1)d, int_output = %(2)d ] 0x00084011 CPU%(cpu)d %(tsc)d (+%(reltsc)8d) vlapic_accept_pic_intr [ i8259_target = %(1)d, accept_pic_int = %(2)d ] xen-4.9.2/tools/xentrace/xenctx.c0000664000175000017500000011366313256712137015115 0ustar smbsmb/****************************************************************************** * tools/xentrace/xenctx.c * * Tool for dumping the cpu context * * Copyright (C) 2005 by Intel Corp * * Author: Arun Sharma * Date: February 2005 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define XC_WANT_COMPAT_MAP_FOREIGN_API #include #include #include #include #define DEFAULT_NR_STACK_PAGES 1 #define DEFAULT_BYTES_PER_LINE 32 #define DEFAULT_LINES 5 /* Note: the order of these matter. * NOT_KERNEL_ADDR must be < both KERNEL_DATA_ADDR and KERNEL_TEXT_ADDR. * KERNEL_DATA_ADDR must be < KERNEL_TEXT_ADDR. */ typedef enum type_of_addr_ { NOT_KERNEL_ADDR, KERNEL_DATA_ADDR, KERNEL_TEXT_ADDR, } type_of_addr; #if defined (__i386__) || defined (__x86_64__) static const uint64_t cr_reg_mask[5] = { [2] = ~UINT64_C(0) }; static const uint64_t dr_reg_mask[8] = { [0 ... 3] = ~UINT64_C(0) }; typedef unsigned long long guest_word_t; #define FMT_16B_WORD "%04llx" #define FMT_32B_WORD "%08llx" #define FMT_64B_WORD "%016llx" /* Word-length of the guest's own data structures */ int guest_word_size = sizeof (unsigned long); /* Word-length of the context record we get from xen */ int ctxt_word_size = sizeof (unsigned long); int guest_protected_mode = 1; #elif defined(__arm__) #define NO_TRANSLATION typedef uint64_t guest_word_t; #define FMT_16B_WORD "%04llx" #define FMT_32B_WORD "%08llx" #define FMT_64B_WORD "%016llx" #elif defined(__aarch64__) #define NO_TRANSLATION typedef uint64_t guest_word_t; #define FMT_16B_WORD "%04llx" #define FMT_32B_WORD "%08llx" #define FMT_64B_WORD "%016llx" #endif #define MAX_BYTES_PER_LINE 128 static struct xenctx { xc_interface *xc_handle; int domid; int frame_ptrs; int stack_trace; int disp_all; int nr_stack_pages; int bytes_per_line; int lines; int decode_as_ascii; int tag_stack_dump; int tag_call_trace; int all_vcpus; #ifndef NO_TRANSLATION guest_word_t mem_addr; guest_word_t stk_addr; int do_memory; int do_stack; #endif int kernel_start_set; xc_dominfo_t dominfo; } xenctx; struct symbol { guest_word_t address; char *name; struct symbol *next; } *symbol_table = NULL; guest_word_t kernel_stext, kernel_etext, kernel_sinittext, kernel_einittext, kernel_hypercallpage; guest_word_t kernel_text; #if defined (__i386__) || defined (__arm__) unsigned long long kernel_start = 0xc0000000; unsigned long long kernel_end = 0xffffffffULL; #elif defined (__x86_64__) unsigned long long kernel_start = 0xffffffff80000000UL; unsigned long long kernel_end = 0xffffffffffffffffUL; #elif defined (__aarch64__) unsigned long long kernel_start = 0xffffff8000000000UL; unsigned long long kernel_end = 0xffffffffffffffffULL; #endif static type_of_addr kernel_addr(guest_word_t addr) { if ( symbol_table == NULL ) { if ( addr > kernel_start ) return KERNEL_TEXT_ADDR; else return NOT_KERNEL_ADDR; } if (addr >= kernel_stext && addr <= kernel_etext) return KERNEL_TEXT_ADDR; if ( kernel_hypercallpage && (addr >= kernel_hypercallpage && addr <= kernel_hypercallpage + 4096) ) return KERNEL_TEXT_ADDR; if (addr >= kernel_sinittext && addr <= kernel_einittext) return KERNEL_TEXT_ADDR; if ( xenctx.kernel_start_set ) { if ( addr > kernel_start ) return KERNEL_TEXT_ADDR; } else { if ( addr >= kernel_text && addr <= kernel_end ) return KERNEL_DATA_ADDR; if ( addr >= kernel_start && addr <= kernel_end ) return KERNEL_TEXT_ADDR; } return NOT_KERNEL_ADDR; } #if 0 static void free_symbol(struct symbol *symbol) { if (symbol == NULL) return; if (symbol->name) free(symbol->name); free(symbol); } #endif static void insert_symbol(struct symbol *symbol) { static struct symbol *prev = NULL; struct symbol *s = symbol_table; if (s == NULL) { symbol_table = symbol; symbol->next = NULL; return; } /* The System.map is usually already sorted... */ if (prev && prev->address <= symbol->address && (!prev->next || prev->next->address > symbol->address)) { s = prev; } else { /* ... otherwise do crappy/slow search for the correct place */ while (s->next && s->next->address <= symbol->address) s = s->next; } symbol->next = s->next; s->next = symbol; prev = symbol; } static struct symbol *lookup_symbol(guest_word_t address) { struct symbol *s = symbol_table; if (!s) return NULL; while (s->next && s->next->address < address) s = s->next; return s->next && s->next->address <= address ? s->next : s; } static void print_symbol(guest_word_t addr, type_of_addr type) { struct symbol *s; if ( kernel_addr(addr) < type ) return; s = lookup_symbol(addr); if (s==NULL) return; if (addr==s->address) printf(" %s", s->name); else printf(" %s+%#x", s->name, (unsigned int)(addr - s->address)); } static void read_symbol_table(const char *symtab) { char type, line[256]; char *p; struct symbol *symbol; FILE *f; guest_word_t address; f = fopen(symtab, "r"); if(f == NULL) { fprintf(stderr, "failed to open symbol table %s\n", symtab); exit(-1); } while(!feof(f)) { if(fgets(line,256,f)==NULL) break; /* need more checks for syntax here... */ address = strtoull(line, &p, 16); if (!isspace((uint8_t)*p++)) continue; type = *p++; if (!isalpha((uint8_t)type) && type != '?') continue; if (!isspace((uint8_t)*p++)) continue; /* in the future we should handle the module name * being appended here, this would allow us to use * /proc/kallsyms as our symbol table */ if (p[strlen(p)-1] == '\n') p[strlen(p)-1] = '\0'; switch (type) { case 'A': /* global absolute */ case 'a': /* local absolute */ break; case 'U': /* undefined */ case 'v': /* undefined weak object */ case 'w': /* undefined weak function */ continue; default: symbol = malloc(sizeof(*symbol)); if (symbol == NULL) { fclose(f); return; } symbol->address = address; symbol->name = strdup(p); if (symbol->name == NULL) { free(symbol); fclose(f); return; } insert_symbol(symbol); break; } if (strcmp(p, "_stext") == 0) kernel_stext = address; else if (strcmp(p, "_etext") == 0) kernel_etext = address; else if ( strcmp(p, "_text") == 0 ) kernel_text = address; else if ( strcmp(p, "_end") == 0 || strcmp(p, "__bss_stop") == 0 ) kernel_end = address; else if (strcmp(p, "_sinittext") == 0) kernel_sinittext = address; else if (strcmp(p, "_einittext") == 0) kernel_einittext = address; else if (strcmp(p, "hypercall_page") == 0) kernel_hypercallpage = address; } fclose(f); } #if defined(__i386__) || defined(__x86_64__) #define CR0_PE 0x1 char *flag_values[22][2] = {/* clear, set, bit# */ { NULL, "c" }, // 0 Carry { NULL, NULL }, // 1 { NULL, "p" }, // 2 Parity { NULL, NULL }, // 3 { NULL, "a" }, // 4 Adjust { NULL, NULL }, // 5 { "nz", "z" }, // 6 Zero { NULL, "s" }, // 7 Sign { NULL, "tf" }, // 8 Trap { NULL, "i" }, // 9 Interrupt (enabled) { NULL, "d=b" }, // 10 Direction { NULL, "o" }, // 11 Overflow { NULL, NULL }, // 12 12+13 == IOPL { NULL, NULL }, // 13 { NULL, "nt" }, // 14 Nested Task { NULL, NULL }, // 15 { NULL, "rf" }, // 16 Resume Flag { NULL, "v86" }, // 17 Virtual 8086 mode { NULL, "ac" }, // 18 Alignment Check (enabled) { NULL, "vif" }, // 19 Virtual Interrupt (enabled) { NULL, "vip" }, // 20 Virtual Interrupt Pending { NULL, "cid" } // 21 Cpuid Identification Flag }; static void print_flags(uint64_t flags) { int i; printf("\nflags: %08" PRIx64, flags); for (i = 21; i >= 0; i--) { char *s = flag_values[i][(flags >> i) & 1]; if (s != NULL) printf(" %s", s); } printf("\n"); } static void print_special(void *regs, const char *name, unsigned int mask, const uint64_t reg_is_addr_mask[], int width) { unsigned int i; printf("\n"); for (i = 0; mask; mask >>= 1, ++i) if (mask & 1) { if ( width == 4 ) { printf("%s%u: %08"PRIx32, name, i, ((uint32_t *) regs)[i]); if ( reg_is_addr_mask[i] ) print_symbol(reg_is_addr_mask[i] & ((uint32_t *) regs)[i], KERNEL_DATA_ADDR); } else { printf("%s%u: %016"PRIx64, name, i, ((uint64_t *) regs)[i]); if ( reg_is_addr_mask[i] ) print_symbol(reg_is_addr_mask[i] & ((uint64_t *) regs)[i], KERNEL_DATA_ADDR); } printf("\n"); } } static void print_ctx_32(vcpu_guest_context_x86_32_t *ctx) { struct cpu_user_regs_x86_32 *regs = &ctx->user_regs; printf("cs:eip: %04x:%08x", regs->cs, regs->eip); print_symbol(regs->eip, KERNEL_TEXT_ADDR); print_flags(regs->eflags); printf("ss:esp: %04x:%08x\n", regs->ss, regs->esp); printf("eax: %08x\t", regs->eax); printf("ebx: %08x\t", regs->ebx); printf("ecx: %08x\t", regs->ecx); printf("edx: %08x\n", regs->edx); printf("esi: %08x\t", regs->esi); printf("edi: %08x\t", regs->edi); printf("ebp: %08x\n", regs->ebp); printf(" ds: %04x\t", regs->ds); printf(" es: %04x\t", regs->es); printf(" fs: %04x\t", regs->fs); printf(" gs: %04x\n", regs->gs); if (xenctx.disp_all) { print_special(ctx->ctrlreg, "cr", 0x1d, cr_reg_mask, 4); print_special(ctx->debugreg, "dr", 0xcf, dr_reg_mask, 4); } } static void print_ctx_32on64(vcpu_guest_context_x86_64_t *ctx) { struct cpu_user_regs_x86_64 *regs = &ctx->user_regs; printf("cs:eip: %04x:%08x", regs->cs, (uint32_t)regs->eip); print_symbol((uint32_t)regs->eip, KERNEL_TEXT_ADDR); print_flags((uint32_t)regs->eflags); printf("ss:esp: %04x:%08x\n", regs->ss, (uint32_t)regs->esp); printf("eax: %08x\t", (uint32_t)regs->eax); printf("ebx: %08x\t", (uint32_t)regs->ebx); printf("ecx: %08x\t", (uint32_t)regs->ecx); printf("edx: %08x\n", (uint32_t)regs->edx); printf("esi: %08x\t", (uint32_t)regs->esi); printf("edi: %08x\t", (uint32_t)regs->edi); printf("ebp: %08x\n", (uint32_t)regs->ebp); printf(" ds: %04x\t", regs->ds); printf(" es: %04x\t", regs->es); printf(" fs: %04x\t", regs->fs); printf(" gs: %04x\n", regs->gs); if (xenctx.disp_all) { uint32_t tmp_regs[8]; int i; for (i = 0; i < 5; i++) tmp_regs[i] = ctx->ctrlreg[i]; print_special(tmp_regs, "cr", 0x1d, cr_reg_mask, 4); for (i = 0; i < 8; i++) tmp_regs[i] = ctx->debugreg[i]; print_special(tmp_regs, "dr", 0xcf, dr_reg_mask, 4); } } static void print_ctx_64(vcpu_guest_context_x86_64_t *ctx) { struct cpu_user_regs_x86_64 *regs = &ctx->user_regs; printf("rip: %016"PRIx64, regs->rip); print_symbol(regs->rip, KERNEL_TEXT_ADDR); print_flags(regs->rflags); printf("rsp: %016"PRIx64"\n", regs->rsp); printf("rax: %016"PRIx64"\t", regs->rax); printf("rcx: %016"PRIx64"\t", regs->rcx); printf("rdx: %016"PRIx64"\n", regs->rdx); printf("rbx: %016"PRIx64"\t", regs->rbx); printf("rsi: %016"PRIx64"\t", regs->rsi); printf("rdi: %016"PRIx64"\n", regs->rdi); printf("rbp: %016"PRIx64"\t", regs->rbp); printf(" r8: %016"PRIx64"\t", regs->r8); printf(" r9: %016"PRIx64"\n", regs->r9); printf("r10: %016"PRIx64"\t", regs->r10); printf("r11: %016"PRIx64"\t", regs->r11); printf("r12: %016"PRIx64"\n", regs->r12); printf("r13: %016"PRIx64"\t", regs->r13); printf("r14: %016"PRIx64"\t", regs->r14); printf("r15: %016"PRIx64"\n", regs->r15); printf(" cs: %04x\t", regs->cs); printf(" ss: %04x\t", regs->ss); printf(" ds: %04x\t", regs->ds); printf(" es: %04x\n", regs->es); printf(" fs: %04x @ %016"PRIx64, regs->fs, ctx->fs_base); print_symbol(ctx->fs_base, KERNEL_DATA_ADDR); printf("\n"); printf(" gs: %04x @ %016"PRIx64"/%016"PRIx64, regs->gs, ctx->gs_base_kernel, ctx->gs_base_user); if ( symbol_table ) { print_symbol(ctx->gs_base_kernel, KERNEL_DATA_ADDR); printf("/"); print_symbol(ctx->gs_base_user, KERNEL_DATA_ADDR); } printf("\n"); if (xenctx.disp_all) { print_special(ctx->ctrlreg, "cr", 0x1d, cr_reg_mask, 8); print_special(ctx->debugreg, "dr", 0xcf, dr_reg_mask, 8); } } static void print_ctx(vcpu_guest_context_any_t *ctx) { if (ctxt_word_size == 4) print_ctx_32(&ctx->x32); else if (guest_word_size != 8) print_ctx_32on64(&ctx->x64); else print_ctx_64(&ctx->x64); } #define NONPROT_MODE_SEGMENT_SHIFT 4 static guest_word_t instr_pointer(vcpu_guest_context_any_t *ctx) { guest_word_t r; if (ctxt_word_size == 4) { r = ctx->x32.user_regs.eip; if ( !guest_protected_mode ) r += ctx->x32.user_regs.cs << NONPROT_MODE_SEGMENT_SHIFT; } else { r = ctx->x64.user_regs.rip; if ( !guest_protected_mode ) r += ctx->x64.user_regs.cs << NONPROT_MODE_SEGMENT_SHIFT; } return r; } static guest_word_t stack_pointer(vcpu_guest_context_any_t *ctx) { guest_word_t r; if (ctxt_word_size == 4) { r = ctx->x32.user_regs.esp; if ( !guest_protected_mode ) r += ctx->x32.user_regs.ss << NONPROT_MODE_SEGMENT_SHIFT; } else { r = ctx->x64.user_regs.rsp; if ( !guest_protected_mode ) r += ctx->x64.user_regs.ss << NONPROT_MODE_SEGMENT_SHIFT; } return r; } static guest_word_t frame_pointer(vcpu_guest_context_any_t *ctx) { if (ctxt_word_size == 4) return ctx->x32.user_regs.ebp; else return ctx->x64.user_regs.rbp; } #elif defined(__arm__) || defined(__aarch64__) static void print_ctx_32(vcpu_guest_context_t *ctx) { vcpu_guest_core_regs_t *regs = &ctx->user_regs; printf("PC: %08"PRIx32, regs->pc32); print_symbol(regs->pc32, KERNEL_TEXT_ADDR); printf("\n"); printf("CPSR: %08"PRIx32"\n", regs->cpsr); printf("USR: SP:%08"PRIx32" LR:%08"PRIx32"\n", regs->sp_usr, regs->lr_usr); printf("SVC: SPSR:%08"PRIx32" SP:%08"PRIx32" LR:%08"PRIx32"\n", regs->spsr_svc, regs->sp_svc, regs->lr_svc); printf("FIQ: SPSR:%08"PRIx32" SP:%08"PRIx32" LR:%08"PRIx32"\n", regs->spsr_fiq, regs->sp_fiq, regs->lr_fiq); printf("IRQ: SPSR:%08"PRIx32" SP:%08"PRIx32" LR:%08"PRIx32"\n", regs->spsr_irq, regs->sp_irq, regs->lr_irq); printf("ABT: SPSR:%08"PRIx32" SP:%08"PRIx32" LR:%08"PRIx32"\n", regs->spsr_abt, regs->sp_abt, regs->lr_abt); printf("UND: SPSR:%08"PRIx32" SP:%08"PRIx32" LR:%08"PRIx32"\n", regs->spsr_und, regs->sp_und, regs->lr_und); printf("\n"); printf(" r0_usr: %08"PRIx32"\t", regs->r0_usr); printf(" r1_usr: %08"PRIx32"\t", regs->r1_usr); printf(" r2_usr: %08"PRIx32"\n", regs->r2_usr); printf(" r3_usr: %08"PRIx32"\t", regs->r3_usr); printf(" r4_usr: %08"PRIx32"\t", regs->r4_usr); printf(" r5_usr: %08"PRIx32"\n", regs->r5_usr); printf(" r6_usr: %08"PRIx32"\t", regs->r6_usr); printf(" r7_usr: %08"PRIx32"\t", regs->r7_usr); printf(" r8_usr: %08"PRIx32"\n", regs->r8_usr); printf(" r9_usr: %08"PRIx32"\t", regs->r9_usr); printf("r10_usr: %08"PRIx32"\t", regs->r10_usr); printf("r11_usr: %08"PRIx32"\n", regs->r11_usr); printf("r12_usr: %08"PRIx32"\n", regs->r12_usr); printf("\n"); printf(" r8_fiq: %08"PRIx32"\n", regs->r8_fiq); printf(" r9_fiq: %08"PRIx32"\t", regs->r9_fiq); printf("r10_fiq: %08"PRIx32"\t", regs->r10_fiq); printf("r11_fiq: %08"PRIx32"\n", regs->r11_fiq); printf("r12_fiq: %08"PRIx32"\n", regs->r12_fiq); printf("\n"); } #ifdef __aarch64__ static void print_ctx_64(vcpu_guest_context_t *ctx) { vcpu_guest_core_regs_t *regs = &ctx->user_regs; printf("PC: %016"PRIx64, regs->pc64); print_symbol(regs->pc64, KERNEL_TEXT_ADDR); printf("\n"); printf("LR: %016"PRIx64"\n", regs->x30); printf("ELR_EL1: %016"PRIx64"\n", regs->elr_el1); printf("CPSR: %08"PRIx32"\n", regs->cpsr); printf("SPSR_EL1: %08"PRIx32"\n", regs->spsr_el1); printf("SP_EL0: %016"PRIx64"\n", regs->sp_el0); printf("SP_EL1: %016"PRIx64"\n", regs->sp_el1); printf("\n"); printf(" x0: %016"PRIx64"\t", regs->x0); printf(" x1: %016"PRIx64"\t", regs->x1); printf(" x2: %016"PRIx64"\n", regs->x2); printf(" x3: %016"PRIx64"\t", regs->x3); printf(" x4: %016"PRIx64"\t", regs->x4); printf(" x5: %016"PRIx64"\n", regs->x5); printf(" x6: %016"PRIx64"\t", regs->x6); printf(" x7: %016"PRIx64"\t", regs->x7); printf(" x8: %016"PRIx64"\n", regs->x8); printf(" x9: %016"PRIx64"\t", regs->x9); printf("x10: %016"PRIx64"\t", regs->x10); printf("x11: %016"PRIx64"\n", regs->x11); printf("x12: %016"PRIx64"\t", regs->x12); printf("x13: %016"PRIx64"\t", regs->x13); printf("x14: %016"PRIx64"\n", regs->x14); printf("x15: %016"PRIx64"\t", regs->x15); printf("x16: %016"PRIx64"\t", regs->x16); printf("x17: %016"PRIx64"\n", regs->x17); printf("x18: %016"PRIx64"\t", regs->x18); printf("x19: %016"PRIx64"\t", regs->x19); printf("x20: %016"PRIx64"\n", regs->x20); printf("x21: %016"PRIx64"\t", regs->x21); printf("x22: %016"PRIx64"\t", regs->x22); printf("x23: %016"PRIx64"\n", regs->x23); printf("x24: %016"PRIx64"\t", regs->x24); printf("x25: %016"PRIx64"\t", regs->x25); printf("x26: %016"PRIx64"\n", regs->x26); printf("x27: %016"PRIx64"\t", regs->x27); printf("x28: %016"PRIx64"\t", regs->x28); printf("x29: %016"PRIx64"\n", regs->x29); printf("\n"); } #endif /* __aarch64__ */ static void print_ctx(vcpu_guest_context_any_t *ctx_any) { vcpu_guest_context_t *ctx = &ctx_any->c; #ifdef __aarch64__ if (ctx->user_regs.cpsr & PSR_MODE_BIT) print_ctx_32(ctx); else print_ctx_64(ctx); #else print_ctx_32(ctx); #endif printf("SCTLR: %08"PRIx32"\n", ctx->sctlr); printf("TTBCR: %016"PRIx64"\n", ctx->ttbcr); printf("TTBR0: %016"PRIx64"\n", ctx->ttbr0); printf("TTBR1: %016"PRIx64"\n", ctx->ttbr1); } #endif #ifndef NO_TRANSLATION static void *map_page(vcpu_guest_context_any_t *ctx, int vcpu, guest_word_t virt) { static unsigned long previous_mfn = 0; static void *mapped = NULL; unsigned long mfn = xc_translate_foreign_address(xenctx.xc_handle, xenctx.domid, vcpu, virt); unsigned long offset = virt & ~XC_PAGE_MASK; if (mapped && mfn == previous_mfn) goto out; if (mapped) munmap(mapped, XC_PAGE_SIZE); previous_mfn = mfn; mapped = xc_map_foreign_range(xenctx.xc_handle, xenctx.domid, XC_PAGE_SIZE, PROT_READ, mfn); if (mapped == NULL) { fprintf(stderr, "\nfailed to map page for "FMT_32B_WORD".\n", virt); return NULL; } out: return (void *)(mapped + offset); } static guest_word_t read_stack_word(guest_word_t *src, int width) { guest_word_t word = 0; /* Little-endian only */ memcpy(&word, src, width); return word; } static guest_word_t read_mem_word(vcpu_guest_context_any_t *ctx, int vcpu, guest_word_t virt, int width) { if ( (virt & 7) == 0 ) { guest_word_t *p = map_page(ctx, vcpu, virt); if ( p ) return read_stack_word(p, width); else return -1; } else { guest_word_t word = 0; char *src, *dst; int i; /* Little-endian only */ dst = (char *)&word; for (i = 0; i < width; i++) { src = map_page(ctx, vcpu, virt + i); if ( src ) *dst++ = *src; else { guest_word_t missing = -1LL; /* Return all ones for missing memory */ memcpy(dst, &missing, width - i); return word; } } return word; } } static void print_stack_word(guest_word_t word, int width) { if (width == 2) printf(FMT_16B_WORD, word); else if (width == 4) printf(FMT_32B_WORD, word); else printf(FMT_64B_WORD, word); } static int print_lines(vcpu_guest_context_any_t *ctx, int vcpu, int width, guest_word_t mem_addr, guest_word_t mem_limit) { guest_word_t mem_start = mem_addr; guest_word_t word; guest_word_t ascii[MAX_BYTES_PER_LINE/4]; int i; for (i = 1; i < xenctx.lines + 1 && mem_addr < mem_limit; i++) { int j = 0; int k; if ( xenctx.tag_stack_dump ) { print_stack_word(mem_addr, width); printf(":"); } while ( mem_addr < mem_limit && mem_addr < mem_start + i * xenctx.bytes_per_line ) { void *p = map_page(ctx, vcpu, mem_addr); if ( !p ) return -1; word = read_mem_word(ctx, vcpu, mem_addr, width); if ( xenctx.decode_as_ascii ) ascii[j++] = word; printf(" "); print_stack_word(word, width); mem_addr += width; } if ( xenctx.decode_as_ascii ) { /* * Line up ascii output if less than bytes_per_line * were printed. */ for (k = j; k < xenctx.bytes_per_line / width; k++) printf(" %*s", width * 2, ""); printf(" "); for (k = 0; k < j; k++) { int l; unsigned char *bytep = (unsigned char *)&ascii[k]; for (l = 0; l < width; l++) { if (isprint(*bytep)) printf("%c", *bytep); else printf("."); bytep++; } } } printf("\n"); } printf("\n"); return 0; } static void print_mem(vcpu_guest_context_any_t *ctx, int vcpu, int width, guest_word_t mem_addr) { printf("Memory (address "); print_stack_word(mem_addr, width); printf("):\n"); print_lines(ctx, vcpu, width, mem_addr, mem_addr + xenctx.lines * xenctx.bytes_per_line); } static int print_code(vcpu_guest_context_any_t *ctx, int vcpu) { guest_word_t instr; int i; instr = instr_pointer(ctx); printf("Code (instr addr %08llx)\n", instr); instr -= 21; for(i=0; i<32; i++) { unsigned char *c = map_page(ctx, vcpu, instr+i); if (!c) return -1; if (instr+i == instr_pointer(ctx)) printf("<%02x> ", *c); else printf("%02x ", *c); } printf("\n\n\n"); return 0; } static void print_stack_addr(guest_word_t addr, int width) { print_stack_word(addr, width); printf(": "); } static int print_stack(vcpu_guest_context_any_t *ctx, int vcpu, int width, guest_word_t stk_addr_start) { guest_word_t stack = stk_addr_start; guest_word_t stack_limit; guest_word_t frame; guest_word_t word; guest_word_t *p; if ( width ) xenctx.bytes_per_line = ((xenctx.bytes_per_line + width - 1) / width) * width; stack_limit = ((stack_pointer(ctx) + XC_PAGE_SIZE) & ~((guest_word_t) XC_PAGE_SIZE - 1)) + (xenctx.nr_stack_pages - 1) * XC_PAGE_SIZE; if ( xenctx.lines ) { printf("Stack:\n"); if ( print_lines(ctx, vcpu, width, stack, stack_limit) ) return -1; } if ( !guest_protected_mode ) return 0; if(xenctx.stack_trace) printf("Stack Trace:\n"); else printf("Call Trace:\n"); if ( !xenctx.do_stack ) { printf("%*s %c [<", width*2, "", xenctx.stack_trace ? '*' : ' '); print_stack_word(instr_pointer(ctx), width); printf(">]"); print_symbol(instr_pointer(ctx), KERNEL_TEXT_ADDR); printf(" <--\n"); } if (xenctx.frame_ptrs) { stack = stack_pointer(ctx); frame = frame_pointer(ctx); while(frame && stack < stack_limit) { if (xenctx.stack_trace) { while (stack < frame) { p = map_page(ctx, vcpu, stack); if (!p) return -1; print_stack_addr(stack, width); printf("| "); print_stack_word(read_stack_word(p, width), width); printf("\n"); stack += width; } } else { stack = frame; } p = map_page(ctx, vcpu, stack); if (!p) return -1; frame = read_stack_word(p, width); if (xenctx.stack_trace) { print_stack_addr(stack, width); printf("|-- "); print_stack_word(read_stack_word(p, width), width); printf("\n"); } stack += width; if (frame) { p = map_page(ctx, vcpu, stack); if (!p) return -1; word = read_stack_word(p, width); print_stack_addr(stack, width); printf("%c [<", xenctx.stack_trace ? '|' : ' '); print_stack_word(word, width); printf(">]"); print_symbol(word, KERNEL_TEXT_ADDR); printf("\n"); stack += width; } } } else { stack = stk_addr_start; while(stack < stack_limit) { p = map_page(ctx, vcpu, stack); if (!p) return -1; word = read_mem_word(ctx, vcpu, stack, width); if ( kernel_addr(word) >= KERNEL_TEXT_ADDR ) { print_stack_addr(stack, width); printf(" [<"); print_stack_word(word, width); printf(">]"); print_symbol(word, KERNEL_TEXT_ADDR); printf("\n"); } else if (xenctx.stack_trace) { print_stack_addr(stack, width); printf(" "); print_stack_word(word, width); printf("\n"); } stack += width; } } return 0; } #endif static void dump_ctx(int vcpu) { vcpu_guest_context_any_t ctx; if (xc_vcpu_getcontext(xenctx.xc_handle, xenctx.domid, vcpu, &ctx) < 0) { perror("xc_vcpu_getcontext"); return; } #if defined(__i386__) || defined(__x86_64__) { if (xenctx.dominfo.hvm) { struct hvm_hw_cpu cpuctx; xen_capabilities_info_t xen_caps = ""; if (xc_domain_hvm_getcontext_partial( xenctx.xc_handle, xenctx.domid, HVM_SAVE_CODE(CPU), vcpu, &cpuctx, sizeof cpuctx) != 0) { perror("xc_domain_hvm_getcontext_partial"); return; } guest_protected_mode = (cpuctx.cr0 & CR0_PE); guest_word_size = (cpuctx.msr_efer & 0x400) ? 8 : guest_protected_mode ? 4 : 2; /* HVM guest context records are always host-sized */ if (xc_version(xenctx.xc_handle, XENVER_capabilities, &xen_caps) != 0) { perror("xc_version"); return; } ctxt_word_size = (strstr(xen_caps, "xen-3.0-x86_64")) ? 8 : 4; } else { unsigned int gw; if ( !xc_domain_get_guest_width(xenctx.xc_handle, xenctx.domid, &gw) ) ctxt_word_size = guest_word_size = gw; } } #endif #ifndef NO_TRANSLATION if ( xenctx.do_memory ) { print_mem(&ctx, vcpu, guest_word_size, xenctx.mem_addr); return; } if ( xenctx.do_stack ) { print_stack(&ctx, vcpu, guest_word_size, xenctx.stk_addr); return; } #endif print_ctx(&ctx); #ifndef NO_TRANSLATION if (print_code(&ctx, vcpu)) return; if ( !guest_protected_mode || kernel_addr(instr_pointer(&ctx)) >= KERNEL_TEXT_ADDR ) if ( print_stack(&ctx, vcpu, guest_word_size, stack_pointer(&ctx)) ) return; #endif } static void dump_all_vcpus(void) { xc_vcpuinfo_t vinfo; int vcpu; for (vcpu = 0; vcpu <= xenctx.dominfo.max_vcpu_id; vcpu++) { if ( xc_vcpu_getinfo(xenctx.xc_handle, xenctx.domid, vcpu, &vinfo) ) continue; if ( vinfo.online ) { printf("vcpu%d:\n", vcpu); dump_ctx(vcpu); printf("\n"); } else printf("vcpu%d offline\n\n", vcpu); } } static void usage(void) { printf("usage:\n\n"); printf(" xenctx [options] [VCPU]\n\n"); printf("options:\n"); printf(" -f, --frame-pointers\n"); printf(" assume the kernel was compiled with\n"); printf(" frame pointers.\n"); printf(" -s SYMTAB, --symbol-table=SYMTAB\n"); printf(" read symbol table from SYMTAB.\n"); printf(" -S, --stack-trace print a complete stack trace.\n"); printf(" -k KADDR, --kernel-start=KADDR\n"); printf(" set user/kernel split. (default 0x"FMT_32B_WORD")\n", kernel_start); printf(" -a, --all display more registers\n"); printf(" -C, --all-vcpus print info for all vcpus\n"); printf(" -n PAGES, --display-stack-pages=PAGES\n"); printf(" Display N pages from the stack pointer. (default %d)\n", DEFAULT_NR_STACK_PAGES); printf(" Changes stack limit. Note: use with caution (easy\n"); printf(" to get garbage).\n"); printf(" -b , --bytes-per-line \n"); printf(" change the number of bytes per line output for Stack.\n"); printf(" (default %d) Note: rounded to native size (4 or 8 bytes).\n", DEFAULT_BYTES_PER_LINE); printf(" -l , --lines \n"); printf(" change the number of lines output for Stack. (default %d)\n", DEFAULT_LINES); printf(" Can be specified as MAX. Note: Fewer lines will be output\n"); printf(" if stack limit reached.\n"); printf(" -D, --decode-as-ascii\n"); printf(" add a decode of Stack dump as ascii.\n"); printf(" -t, --tag-stack-dump\n"); printf(" add address on each line of Stack dump.\n"); #ifndef NO_TRANSLATION printf(" -m maddr, --memory=maddr\n"); printf(" dump memory at maddr.\n"); printf(" -d daddr, --dump-as-stack=daddr\n"); printf(" dump memory as a stack at daddr.\n"); #endif } int main(int argc, char **argv) { int ch; int ret; const char *prog = argv[0]; static const char *sopts = "fs:hak:SCn:b:l:Dt" #ifndef NO_TRANSLATION "m:d:" #endif ; static const struct option lopts[] = { {"stack-trace", 0, NULL, 'S'}, {"symbol-table", 1, NULL, 's'}, {"frame-pointers", 0, NULL, 'f'}, {"kernel-start", 1, NULL, 'k'}, {"display-stack-pages", 0, NULL, 'n'}, {"decode-as-ascii", 0, NULL, 'D'}, {"tag-stack-dump", 0, NULL, 't'}, #ifndef NO_TRANSLATION {"memory", 1, NULL, 'm'}, {"dump-as-stack", 1, NULL, 'd'}, #endif {"bytes-per-line", 1, NULL, 'b'}, {"lines", 1, NULL, 'l'}, {"all", 0, NULL, 'a'}, {"all-vcpus", 0, NULL, 'C'}, {"help", 0, NULL, 'h'}, {0, 0, 0, 0} }; const char *symbol_table = NULL; int vcpu = 0; int do_default = 1; xenctx.bytes_per_line = DEFAULT_BYTES_PER_LINE; xenctx.lines = DEFAULT_LINES; xenctx.nr_stack_pages = DEFAULT_NR_STACK_PAGES; while ((ch = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { switch(ch) { case 'f': xenctx.frame_ptrs = 1; break; case 's': symbol_table = optarg; break; case 'S': xenctx.stack_trace = 1; break; case 'a': xenctx.disp_all = 1; break; case 'n': xenctx.nr_stack_pages = strtol(optarg, NULL, 0); if ( xenctx.nr_stack_pages < 1) { fprintf(stderr, "%s: Unsupported value(%d) for --display-stack-pages '%s'. Needs to be >= 1\n", prog, xenctx.nr_stack_pages, optarg); exit(-1); } break; case 'D': xenctx.decode_as_ascii = 1; break; case 't': xenctx.tag_stack_dump = 1; break; case 'b': xenctx.bytes_per_line = strtol(optarg, NULL, 0); if ( xenctx.bytes_per_line < 4 || xenctx.bytes_per_line > MAX_BYTES_PER_LINE ) { fprintf(stderr, "%s: Unsupported value for --bytes-per-line '%s'. Needs to be 4 <= %d <= %d\n", prog, optarg, xenctx.bytes_per_line, MAX_BYTES_PER_LINE); exit(-1); } break; case 'l': if ( !strcmp(optarg, "all") || !strcmp(optarg, "ALL") || !strcmp(optarg, "max") || !strcmp(optarg, "MAX") ) xenctx.lines = INT_MAX - 1; else xenctx.lines = strtol(optarg, NULL, 0); if ( xenctx.lines < 0 || xenctx.lines == INT_MAX) { fprintf(stderr, "%s: Unsupported value(%d) for --lines '%s'. Needs to be >= 0, < %d\n", prog, xenctx.lines, optarg, INT_MAX); exit(-1); } break; case 'C': xenctx.all_vcpus = 1; do_default = 0; break; case 'k': kernel_start = strtoull(optarg, NULL, 0); xenctx.kernel_start_set = 1; break; #ifndef NO_TRANSLATION case 'm': xenctx.mem_addr = strtoull(optarg, NULL, 0); xenctx.do_memory = 1; do_default = 0; break; case 'd': xenctx.stk_addr = strtoull(optarg, NULL, 0); xenctx.do_stack = 1; do_default = 0; break; #endif case 'h': usage(); exit(-1); case '?': fprintf(stderr, "%s --help for more options\n", prog); exit(-1); } } argv += optind; argc -= optind; if (argc < 1 || argc > 2) { printf("usage: xenctx [options] \n"); exit(-1); } #ifndef NO_TRANSLATION if ( xenctx.frame_ptrs && xenctx.do_stack ) { fprintf(stderr, "%s: both --frame-pointers and --dump-as-stack is not supported\n", prog); exit(-1); } #endif xenctx.domid = atoi(argv[0]); if (xenctx.domid==0) { fprintf(stderr, "cannot trace dom0\n"); exit(-1); } if ( argc == 2 ) { if ( xenctx.all_vcpus ) { fprintf(stderr, "%s: both --all-vcpus and [VCPU] is not supported\n", prog); exit(-1); } vcpu = atoi(argv[1]); } if (symbol_table) read_symbol_table(symbol_table); xenctx.xc_handle = xc_interface_open(0,0,0); /* for accessing control interface */ if (xenctx.xc_handle == NULL) { perror("xc_interface_open"); exit(-1); } ret = xc_domain_getinfo(xenctx.xc_handle, xenctx.domid, 1, &xenctx.dominfo); if (ret < 0) { perror("xc_domain_getinfo"); exit(-1); } ret = xc_domain_pause(xenctx.xc_handle, xenctx.domid); if (ret < 0) { perror("xc_domain_pause"); exit(-1); } #ifndef NO_TRANSLATION if ( xenctx.do_memory ) { dump_ctx(vcpu); if ( xenctx.do_stack || xenctx.all_vcpus ) printf("\n"); } xenctx.do_memory = 0; if ( xenctx.do_stack ) { dump_ctx(vcpu); if ( xenctx.all_vcpus ) printf("\n"); } xenctx.do_stack = 0; #endif if (xenctx.all_vcpus) dump_all_vcpus(); if ( do_default ) dump_ctx(vcpu); ret = xc_domain_unpause(xenctx.xc_handle, xenctx.domid); if (ret < 0) { perror("xc_domain_unpause"); exit(-1); } ret = xc_interface_close(xenctx.xc_handle); if (ret < 0) { perror("xc_interface_close"); exit(-1); } return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xentrace/Makefile0000664000175000017500000000243313256712137015070 0ustar smbsmbXEN_ROOT=$(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk CFLAGS += -Werror CFLAGS += $(CFLAGS_libxenevtchn) CFLAGS += $(CFLAGS_libxenctrl) LDLIBS += $(LDLIBS_libxenevtchn) LDLIBS += $(LDLIBS_libxenctrl) LDLIBS += $(ARGP_LDFLAGS) BIN-$(CONFIG_X86) = xenalyze BIN = $(BIN-y) SBIN = xentrace xentrace_setsize LIBBIN = xenctx SCRIPTS = xentrace_format .PHONY: all all: build .PHONY: build build: $(BIN) $(SBIN) $(LIBBIN) .PHONY: install install: build $(INSTALL_DIR) $(DESTDIR)$(bindir) $(INSTALL_DIR) $(DESTDIR)$(sbindir) [ -z "$(LIBBIN)" ] || $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN) ifneq ($(BIN),) $(INSTALL_PROG) $(BIN) $(DESTDIR)$(bindir) endif $(INSTALL_PROG) $(SBIN) $(DESTDIR)$(sbindir) $(INSTALL_PYTHON_PROG) $(SCRIPTS) $(DESTDIR)$(bindir) [ -z "$(LIBBIN)" ] || $(INSTALL_PROG) $(LIBBIN) $(DESTDIR)$(LIBEXEC_BIN) .PHONY: clean clean: $(RM) *.a *.so *.o *.rpm $(BIN) $(SBIN) $(LIBBIN) $(DEPS) .PHONY: distclean distclean: clean xentrace: xentrace.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS) $(APPEND_LDFLAGS) xenctx: xenctx.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS) $(APPEND_LDFLAGS) xentrace_setsize: setsize.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS) $(APPEND_LDFLAGS) xenalyze: xenalyze.o mread.o $(CC) $(LDFLAGS) -o $@ $^ $(ARGP_LDFLAGS) $(APPEND_LDFLAGS) -include $(DEPS) xen-4.9.2/tools/xentrace/xentrace_format0000664000175000017500000001612313256712137016535 0ustar smbsmb#!/usr/bin/env python # by Mark Williamson, (C) 2004 Intel Research Cambridge # Program for reformatting trace buffer output according to user-supplied rules import re, sys, string, signal, struct, os, getopt def usage(): print >> sys.stderr, \ "Usage: " + sys.argv[0] + """ defs-file Parses trace data in binary format, as output by Xentrace and reformats it according to the rules in a file of definitions. The rules in this file should have the format ({ and } show grouping and are not part of the syntax): {event_id}{whitespace}{text format string} The textual format string may include format specifiers, such as: %(cpu)d, %(tsc)d, %(event)d, %(1)d, %(2)d, %(3)d, %(4)d, ... [ the 'd' format specifier outputs in decimal, alternatively 'x' will output in hexadecimal and 'o' will output in octal ] Which correspond to the CPU number, event ID, timestamp counter and the 7 data fields from the trace record. There should be one such rule for each type of event. Depending on your system and the volume of trace buffer data, this script may not be able to keep up with the output of xentrace if it is piped directly. In these circumstances you should have xentrace output to a file for processing off-line. """ sys.exit(1) def read_defs(defs_file): defs = {} fd = open(defs_file) reg = re.compile('(\S+)\s+(\S.*)') while True: line = fd.readline() if not line: break if line[0] == '#' or line[0] == '\n': continue m = reg.match(line) if not m: print >> sys.stderr, "Bad format file" ; sys.exit(1) defs[str(eval(m.group(1)))] = m.group(2) return defs def sighand(x,y): global interrupted interrupted = 1 ##### Main code mhz = 0 if len(sys.argv) < 2: usage() try: opts, arg = getopt.getopt(sys.argv[1:], "c:" ) for opt in opts: if opt[0] == '-c' : mhz = int(opt[1]) except getopt.GetoptError: usage() signal.signal(signal.SIGTERM, sighand) signal.signal(signal.SIGHUP, sighand) signal.signal(signal.SIGINT, sighand) interrupted = 0 try: defs = read_defs(arg[0]) except IOError, exn: print exn sys.exit(1) # structure of trace record (as output by xentrace): # HDR(I) {TSC(Q)} D1(I) D2(I) D3(I) D4(I) D5(I) D6(I) D7(I) # # HDR consists of EVENT:28:, n_data:3:, tsc_in:1: # EVENT means Event ID # n_data means number of data (like D1, D2, ...) # tsc_in means TSC data exists(1) or not(0). # if tsc_in == 0, TSC(Q) does not exists. # # CPU ID exists on trace data of EVENT=0x0001f003 # HDRREC = "I" TSCREC = "Q" D1REC = "I" D2REC = "II" D3REC = "III" D4REC = "IIII" D5REC = "IIIII" D6REC = "IIIIII" D7REC = "IIIIIII" last_tsc = [0] TRC_TRACE_IRQ = 0x1f004 TRC_PV_HYPERCALL_V2 = 0x20100d TRC_PV_HYPERCALL_SUBCALL = 0x20100e NR_VECTORS = 256 irq_measure = [{'count':0, 'tot_cycles':0, 'max_cycles':0}] * NR_VECTORS i=0 while not interrupted: try: i=i+1 line = sys.stdin.read(struct.calcsize(HDRREC)) if not line: break event = struct.unpack(HDRREC, line)[0] n_data = event >> 28 & 0x7 tsc_in = event >> 31 d1 = 0 d2 = 0 d3 = 0 d4 = 0 d5 = 0 d6 = 0 d7 = 0 tsc = 0 if tsc_in == 1: line = sys.stdin.read(struct.calcsize(TSCREC)) if not line: break tsc = struct.unpack(TSCREC, line)[0] if n_data == 1: line = sys.stdin.read(struct.calcsize(D1REC)) if not line: break d1 = struct.unpack(D1REC, line)[0] if n_data == 2: line = sys.stdin.read(struct.calcsize(D2REC)) if not line: break (d1, d2) = struct.unpack(D2REC, line) if n_data == 3: line = sys.stdin.read(struct.calcsize(D3REC)) if not line: break (d1, d2, d3) = struct.unpack(D3REC, line) if n_data == 4: line = sys.stdin.read(struct.calcsize(D4REC)) if not line: break (d1, d2, d3, d4) = struct.unpack(D4REC, line) if n_data == 5: line = sys.stdin.read(struct.calcsize(D5REC)) if not line: break (d1, d2, d3, d4, d5) = struct.unpack(D5REC, line) if n_data == 6: line = sys.stdin.read(struct.calcsize(D6REC)) if not line: break (d1, d2, d3, d4, d5, d6) = struct.unpack(D6REC, line) if n_data == 7: line = sys.stdin.read(struct.calcsize(D7REC)) if not line: break (d1, d2, d3, d4, d5, d6, d7) = struct.unpack(D7REC, line) # Event field is 28bit of 'uint32_t' in header, not 'long'. event &= 0x0fffffff if event == 0x1f003: cpu = d1 if event == TRC_TRACE_IRQ: # IN - d1:vector, d2:tsc_in, d3:tsc_out # OUT - d1:vector, d2:count, d3:tot_cycles, d4:max_cycles tsc_diff = d3 - d2 if tsc_diff < 0: break irq_measure[d1]['count'] += 1 irq_measure[d1]['tot_cycles'] += tsc_diff if irq_measure[d1]['max_cycles'] < tsc_diff: irq_measure[d1]['max_cycles'] = tsc_diff d2 = irq_measure[d1]['count'] d3 = irq_measure[d1]['tot_cycles'] d4 = irq_measure[d1]['max_cycles'] if event == TRC_PV_HYPERCALL_V2 or event == TRC_PV_HYPERCALL_SUBCALL: # Mask off the argument present bits. d1 &= 0x000fffff #tsc = (tscH<<32) | tscL #print i, tsc if cpu >= len(last_tsc): last_tsc += [0] * (cpu - len(last_tsc) + 1) elif tsc < last_tsc[cpu] and tsc_in == 1: print "TSC stepped backward cpu %d ! %d %d" % (cpu,tsc,last_tsc[cpu]) # provide relative TSC if last_tsc[cpu] > 0 and tsc_in == 1: reltsc = tsc - last_tsc[cpu] else: reltsc = 0 if tsc_in == 1: last_tsc[cpu] = tsc if mhz: tsc = tsc / (mhz*1000000.0) args = {'cpu' : cpu, 'tsc' : tsc, 'event' : event, 'reltsc': reltsc, '1' : d1, '2' : d2, '3' : d3, '4' : d4, '5' : d5, '6' : d6, '7' : d7 } try: if defs.has_key(str(event)): print defs[str(event)] % args else: if defs.has_key(str(0)): print defs[str(0)] % args except TypeError: if defs.has_key(str(event)): print defs[str(event)] print args else: if defs.has_key(str(0)): print defs[str(0)] print args except IOError, struct.error: sys.exit() xen-4.9.2/tools/xentrace/mread.h0000664000175000017500000000077213256712137014675 0ustar smbsmb#define MREAD_MAPS 8 #define MREAD_BUF_SHIFT 9 #define PAGE_SHIFT 12 #define MREAD_BUF_SIZE (1ULL<<(PAGE_SHIFT+MREAD_BUF_SHIFT)) #define MREAD_BUF_MASK (~(MREAD_BUF_SIZE-1)) typedef struct mread_ctrl { int fd; off_t file_size; struct mread_buffer { char * buffer; off_t start_offset; int accessed; } map[MREAD_MAPS]; int clock, last; } *mread_handle_t; mread_handle_t mread_init(int fd); ssize_t mread64(mread_handle_t h, void *dst, ssize_t len, off_t offset); xen-4.9.2/tools/libxc/0000775000175000017500000000000013256712137012716 5ustar smbsmbxen-4.9.2/tools/libxc/xc_freebsd.c0000664000175000017500000000351313256712137015170 0ustar smbsmb/****************************************************************************** * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" /* Optionally flush file to disk and discard page cache */ void discard_file_cache(xc_interface *xch, int fd, int flush) { off_t cur = 0; int saved_errno = errno; if ( flush && (fsync(fd) < 0) ) goto out; /* * Calculate last page boundary of amount written so far * unless we are flushing in which case entire cache * is discarded. */ if ( !flush ) { if ( (cur = lseek(fd, 0, SEEK_CUR)) == (off_t)-1 ) cur = 0; cur &= ~(XC_PAGE_SIZE-1); } /* Discard from the buffer cache. */ if ( posix_fadvise(fd, 0, cur, POSIX_FADV_DONTNEED) < 0 ) goto out; out: errno = saved_errno; } void *xc_memalign(xc_interface *xch, size_t alignment, size_t size) { int ret; void *ptr; ret = posix_memalign(&ptr, alignment, size); if ( ret != 0 || !ptr ) return NULL; return ptr; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_csched.c0000664000175000017500000000556013256712137015013 0ustar smbsmb/**************************************************************************** * (C) 2006 - Emmanuel Ackaouy - XenSource Inc. **************************************************************************** * * File: xc_csched.c * Author: Emmanuel Ackaouy * * Description: XC Interface to the credit scheduler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" int xc_sched_credit_domain_set( xc_interface *xch, uint32_t domid, struct xen_domctl_sched_credit *sdom) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_scheduler_op; domctl.domain = (domid_t) domid; domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_CREDIT; domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_putinfo; domctl.u.scheduler_op.u.credit = *sdom; if ( do_domctl(xch, &domctl) ) return -1; return 0; } int xc_sched_credit_domain_get( xc_interface *xch, uint32_t domid, struct xen_domctl_sched_credit *sdom) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_scheduler_op; domctl.domain = (domid_t) domid; domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_CREDIT; domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_getinfo; if ( do_domctl(xch, &domctl) ) return -1; *sdom = domctl.u.scheduler_op.u.credit; return 0; } int xc_sched_credit_params_set( xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_credit_schedule *schedule) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_scheduler_op; sysctl.u.scheduler_op.cpupool_id = cpupool_id; sysctl.u.scheduler_op.sched_id = XEN_SCHEDULER_CREDIT; sysctl.u.scheduler_op.cmd = XEN_SYSCTL_SCHEDOP_putinfo; sysctl.u.scheduler_op.u.sched_credit = *schedule; if ( do_sysctl(xch, &sysctl) ) return -1; *schedule = sysctl.u.scheduler_op.u.sched_credit; return 0; } int xc_sched_credit_params_get( xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_credit_schedule *schedule) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_scheduler_op; sysctl.u.scheduler_op.cpupool_id = cpupool_id; sysctl.u.scheduler_op.sched_id = XEN_SCHEDULER_CREDIT; sysctl.u.scheduler_op.cmd = XEN_SYSCTL_SCHEDOP_getinfo; if ( do_sysctl(xch, &sysctl) ) return -1; *schedule = sysctl.u.scheduler_op.u.sched_credit; return 0; } xen-4.9.2/tools/libxc/xc_dom_core.c0000664000175000017500000010733513256712137015354 0ustar smbsmb/* * Xen domain builder -- core bits. * * The core code goes here: * - allocate and release domain structs. * - memory management functions. * - misc helper functions. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * written 2006 by Gerd Hoffmann . * */ #include #include #include #include #include #include #include #include "xg_private.h" #include "xc_dom.h" #include "_paths.h" /* ------------------------------------------------------------------------ */ /* debugging */ static const char *default_logfile = XEN_LOG_DIR "/domain-builder-ng.log"; int xc_dom_loginit(xc_interface *xch) { if (xch->dombuild_logger) return 0; if (!xch->dombuild_logger_file) { xch->dombuild_logger_file = fopen(default_logfile, "a"); if (!xch->dombuild_logger_file) { PERROR("Could not open logfile `%s'", default_logfile); return -1; } } xch->dombuild_logger = xch->dombuild_logger_tofree = (xentoollog_logger*) xtl_createlogger_stdiostream(xch->dombuild_logger_file, XTL_DETAIL, XTL_STDIOSTREAM_SHOW_DATE|XTL_STDIOSTREAM_SHOW_PID); if (!xch->dombuild_logger) return -1; xc_dom_printf(xch, "### ----- xc domain builder logfile opened -----"); return 0; } void xc_dom_printf(xc_interface *xch, const char *fmt, ...) { va_list args; if (!xch->dombuild_logger) return; va_start(args, fmt); xtl_logv(xch->dombuild_logger, XTL_DETAIL, -1, "domainbuilder", fmt, args); va_end(args); } void xc_dom_panic_func(xc_interface *xch, const char *file, int line, xc_error_code err, const char *fmt, ...) { va_list args; char msg[XC_MAX_ERROR_MSG_LEN]; va_start(args, fmt); vsnprintf(msg, sizeof(msg), fmt, args); va_end(args); msg[sizeof(msg)-1] = 0; xc_report(xch, xch->dombuild_logger ? xch->dombuild_logger : xch->error_handler, XTL_ERROR, err, "panic: %s:%d: %s", file, line, msg); } static void print_mem(struct xc_dom_image *dom, const char *name, size_t mem) { if ( mem > (32 * 1024 * 1024) ) DOMPRINTF("%-24s : %zd MB", name, mem / (1024 * 1024)); else if ( mem > (32 * 1024) ) DOMPRINTF("%-24s : %zd kB", name, mem / 1024); else DOMPRINTF("%-24s : %zd bytes", name, mem); } void xc_dom_log_memory_footprint(struct xc_dom_image *dom) { DOMPRINTF("domain builder memory footprint"); DOMPRINTF(" allocated"); print_mem(dom, " malloc", dom->alloc_malloc); print_mem(dom, " anon mmap", dom->alloc_mem_map); DOMPRINTF(" mapped"); print_mem(dom, " file mmap", dom->alloc_file_map); print_mem(dom, " domU mmap", dom->alloc_domU_map); } /* ------------------------------------------------------------------------ */ /* simple memory pool */ void *xc_dom_malloc(struct xc_dom_image *dom, size_t size) { struct xc_dom_mem *block; if ( size > SIZE_MAX - sizeof(*block) ) { DOMPRINTF("%s: unreasonable allocation size", __FUNCTION__); return NULL; } block = malloc(sizeof(*block) + size); if ( block == NULL ) { DOMPRINTF("%s: allocation failed", __FUNCTION__); return NULL; } memset(block, 0, sizeof(*block) + size); block->type = XC_DOM_MEM_TYPE_MALLOC_INTERNAL; block->next = dom->memblocks; dom->memblocks = block; dom->alloc_malloc += sizeof(*block) + size; if ( size > (100 * 1024) ) print_mem(dom, __FUNCTION__, size); return block->memory; } void *xc_dom_malloc_page_aligned(struct xc_dom_image *dom, size_t size) { struct xc_dom_mem *block; block = malloc(sizeof(*block)); if ( block == NULL ) { DOMPRINTF("%s: allocation failed", __FUNCTION__); return NULL; } memset(block, 0, sizeof(*block)); block->len = size; block->ptr = mmap(NULL, block->len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if ( block->ptr == MAP_FAILED ) { DOMPRINTF("%s: mmap failed", __FUNCTION__); free(block); return NULL; } block->type = XC_DOM_MEM_TYPE_MMAP; block->next = dom->memblocks; dom->memblocks = block; dom->alloc_malloc += sizeof(*block); dom->alloc_mem_map += block->len; if ( size > (100 * 1024) ) print_mem(dom, __FUNCTION__, size); return block->ptr; } int xc_dom_register_external(struct xc_dom_image *dom, void *ptr, size_t size) { struct xc_dom_mem *block; block = malloc(sizeof(*block)); if ( block == NULL ) { DOMPRINTF("%s: allocation failed", __FUNCTION__); return -1; } memset(block, 0, sizeof(*block)); block->ptr = ptr; block->len = size; block->type = XC_DOM_MEM_TYPE_MALLOC_EXTERNAL; block->next = dom->memblocks; dom->memblocks = block; dom->alloc_malloc += sizeof(*block); dom->alloc_mem_map += block->len; return 0; } void *xc_dom_malloc_filemap(struct xc_dom_image *dom, const char *filename, size_t * size, const size_t max_size) { struct xc_dom_mem *block = NULL; int fd = -1; off_t offset; fd = open(filename, O_RDONLY); if ( fd == -1 ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "failed to open file '%s': %s", filename, strerror(errno)); goto err; } if ( (lseek(fd, 0, SEEK_SET) == -1) || ((offset = lseek(fd, 0, SEEK_END)) == -1) ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "failed to seek on file '%s': %s", filename, strerror(errno)); goto err; } *size = offset; if ( max_size && *size > max_size ) { xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY, "tried to map file which is too large"); goto err; } block = malloc(sizeof(*block)); if ( block == NULL ) { xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY, "failed to allocate block (%zu bytes)", sizeof(*block)); goto err; } memset(block, 0, sizeof(*block)); block->len = *size; block->ptr = mmap(NULL, block->len, PROT_READ, MAP_SHARED, fd, 0); if ( block->ptr == MAP_FAILED ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "failed to mmap file '%s': %s", filename, strerror(errno)); goto err; } block->type = XC_DOM_MEM_TYPE_MMAP; block->next = dom->memblocks; dom->memblocks = block; dom->alloc_malloc += sizeof(*block); dom->alloc_file_map += block->len; close(fd); if ( *size > (100 * 1024) ) print_mem(dom, __FUNCTION__, *size); return block->ptr; err: if ( fd != -1 ) close(fd); free(block); DOMPRINTF("%s: failed (on file `%s')", __FUNCTION__, filename); return NULL; } static void xc_dom_free_all(struct xc_dom_image *dom) { struct xc_dom_mem *block; while ( (block = dom->memblocks) != NULL ) { dom->memblocks = block->next; switch ( block->type ) { case XC_DOM_MEM_TYPE_MALLOC_INTERNAL: break; case XC_DOM_MEM_TYPE_MALLOC_EXTERNAL: free(block->ptr); break; case XC_DOM_MEM_TYPE_MMAP: munmap(block->ptr, block->len); break; } free(block); } } char *xc_dom_strdup(struct xc_dom_image *dom, const char *str) { size_t len = strlen(str) + 1; char *nstr = xc_dom_malloc(dom, len); if ( nstr == NULL ) return NULL; memcpy(nstr, str, len); return nstr; } /* ------------------------------------------------------------------------ */ /* decompression buffer sizing */ int xc_dom_kernel_check_size(struct xc_dom_image *dom, size_t sz) { /* No limit */ if ( !dom->max_kernel_size ) return 0; if ( sz > dom->max_kernel_size ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "kernel image too large"); return 1; } return 0; } int xc_dom_ramdisk_check_size(struct xc_dom_image *dom, size_t sz) { /* No limit */ if ( !dom->max_ramdisk_size ) return 0; if ( sz > dom->max_ramdisk_size ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "ramdisk image too large"); return 1; } return 0; } /* ------------------------------------------------------------------------ */ /* read files, copy memory blocks, with transparent gunzip */ size_t xc_dom_check_gzip(xc_interface *xch, void *blob, size_t ziplen) { unsigned char *gzlen; size_t unziplen; if ( ziplen < 6 ) /* Too small. We need (i.e. the subsequent code relies on) * 2 bytes for the magic number plus 4 bytes length. */ return 0; if ( strncmp(blob, "\037\213", 2) ) /* not gzipped */ return 0; gzlen = blob + ziplen - 4; unziplen = (size_t)gzlen[3] << 24 | gzlen[2] << 16 | gzlen[1] << 8 | gzlen[0]; if ( unziplen > XC_DOM_DECOMPRESS_MAX ) { xc_dom_printf (xch, "%s: size (zip %zd, unzip %zd) looks insane, skip gunzip", __FUNCTION__, ziplen, unziplen); return 0; } return unziplen + 16; } int xc_dom_do_gunzip(xc_interface *xch, void *src, size_t srclen, void *dst, size_t dstlen) { z_stream zStream; int rc; memset(&zStream, 0, sizeof(zStream)); zStream.next_in = src; zStream.avail_in = srclen; zStream.next_out = dst; zStream.avail_out = dstlen; rc = inflateInit2(&zStream, (MAX_WBITS + 32)); /* +32 means "handle gzip" */ if ( rc != Z_OK ) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "%s: inflateInit2 failed (rc=%d)", __FUNCTION__, rc); return -1; } rc = inflate(&zStream, Z_FINISH); inflateEnd(&zStream); if ( rc != Z_STREAM_END ) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "%s: inflate failed (rc=%d)", __FUNCTION__, rc); return -1; } xc_dom_printf(xch, "%s: unzip ok, 0x%zx -> 0x%zx", __FUNCTION__, srclen, dstlen); return 0; } int xc_dom_try_gunzip(struct xc_dom_image *dom, void **blob, size_t * size) { void *unzip; size_t unziplen; unziplen = xc_dom_check_gzip(dom->xch, *blob, *size); if ( unziplen == 0 ) return 0; if ( xc_dom_kernel_check_size(dom, unziplen) ) return 0; unzip = xc_dom_malloc(dom, unziplen); if ( unzip == NULL ) return -1; if ( xc_dom_do_gunzip(dom->xch, *blob, *size, unzip, unziplen) == -1 ) return -1; *blob = unzip; *size = unziplen; return 0; } /* ------------------------------------------------------------------------ */ /* domain memory */ void *xc_dom_pfn_to_ptr(struct xc_dom_image *dom, xen_pfn_t pfn, xen_pfn_t count) { xen_pfn_t count_out_dummy; return xc_dom_pfn_to_ptr_retcount(dom, pfn, count, &count_out_dummy); } void *xc_dom_pfn_to_ptr_retcount(struct xc_dom_image *dom, xen_pfn_t pfn, xen_pfn_t count, xen_pfn_t *count_out) { struct xc_dom_phys *phys; xen_pfn_t offset; unsigned int page_shift = XC_DOM_PAGE_SHIFT(dom); char *mode = "unset"; *count_out = 0; offset = pfn - dom->rambase_pfn; if ( offset > dom->total_pages || /* multiple checks to avoid overflows */ count > dom->total_pages || offset > dom->total_pages - count ) { DOMPRINTF("%s: pfn %"PRI_xen_pfn" out of range (0x%" PRIpfn " > 0x%" PRIpfn ")", __FUNCTION__, pfn, offset, dom->total_pages); return NULL; } /* already allocated? */ for ( phys = dom->phys_pages; phys != NULL; phys = phys->next ) { if ( pfn >= (phys->first + phys->count) ) continue; if ( count ) { /* size given: must be completely within the already allocated block */ if ( (pfn + count) <= phys->first ) continue; if ( (pfn < phys->first) || ((pfn + count) > (phys->first + phys->count)) ) { DOMPRINTF("%s: request overlaps allocated block" " (req 0x%" PRIpfn "+0x%" PRIpfn "," " blk 0x%" PRIpfn "+0x%" PRIpfn ")", __FUNCTION__, pfn, count, phys->first, phys->count); return NULL; } *count_out = count; } else { /* no size given: block must be allocated already, just hand out a pointer to it */ if ( pfn < phys->first ) continue; if ( pfn >= phys->first + phys->count ) continue; *count_out = phys->count - (pfn - phys->first); } return phys->ptr + ((pfn - phys->first) << page_shift); } /* allocating is allowed with size specified only */ if ( count == 0 ) { DOMPRINTF("%s: no block found, no size given," " can't malloc (pfn 0x%" PRIpfn ")", __FUNCTION__, pfn); return NULL; } /* not found, no overlap => allocate */ phys = xc_dom_malloc(dom, sizeof(*phys)); if ( phys == NULL ) return NULL; memset(phys, 0, sizeof(*phys)); phys->first = pfn; phys->count = count; if ( dom->guest_domid ) { mode = "domU mapping"; phys->ptr = xc_dom_boot_domU_map(dom, phys->first, phys->count); if ( phys->ptr == NULL ) return NULL; dom->alloc_domU_map += phys->count << page_shift; } else { int err; mode = "anonymous memory"; phys->ptr = mmap(NULL, phys->count << page_shift, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if ( phys->ptr == MAP_FAILED ) { err = errno; xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY, "%s: oom: can't allocate 0x%" PRIpfn " pages" " [mmap, errno=%i (%s)]", __FUNCTION__, count, err, strerror(err)); return NULL; } dom->alloc_mem_map += phys->count << page_shift; } #if 1 DOMPRINTF("%s: %s: pfn 0x%" PRIpfn "+0x%" PRIpfn " at %p", __FUNCTION__, mode, phys->first, phys->count, phys->ptr); #endif phys->next = dom->phys_pages; dom->phys_pages = phys; return phys->ptr; } static int xc_dom_chk_alloc_pages(struct xc_dom_image *dom, char *name, xen_pfn_t pages) { unsigned int page_size = XC_DOM_PAGE_SIZE(dom); if ( pages > dom->total_pages || /* multiple test avoids overflow probs */ dom->pfn_alloc_end - dom->rambase_pfn > dom->total_pages || pages > dom->total_pages - dom->pfn_alloc_end + dom->rambase_pfn ) { xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY, "%s: segment %s too large (0x%"PRIpfn" > " "0x%"PRIpfn" - 0x%"PRIpfn" pages)", __FUNCTION__, name, pages, dom->total_pages, dom->pfn_alloc_end - dom->rambase_pfn); return -1; } dom->pfn_alloc_end += pages; dom->virt_alloc_end += pages * page_size; if ( dom->allocate ) dom->allocate(dom); return 0; } static int xc_dom_alloc_pad(struct xc_dom_image *dom, xen_vaddr_t boundary) { unsigned int page_size = XC_DOM_PAGE_SIZE(dom); xen_pfn_t pages; if ( boundary & (page_size - 1) ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: segment boundary isn't page aligned (0x%" PRIx64 ")", __FUNCTION__, boundary); return -1; } if ( boundary < dom->virt_alloc_end ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: segment boundary too low (0x%" PRIx64 " < 0x%" PRIx64 ")", __FUNCTION__, boundary, dom->virt_alloc_end); return -1; } pages = (boundary - dom->virt_alloc_end) / page_size; return xc_dom_chk_alloc_pages(dom, "padding", pages); } int xc_dom_alloc_segment(struct xc_dom_image *dom, struct xc_dom_seg *seg, char *name, xen_vaddr_t start, xen_vaddr_t size) { unsigned int page_size = XC_DOM_PAGE_SIZE(dom); xen_pfn_t pages; void *ptr; if ( start && xc_dom_alloc_pad(dom, start) ) return -1; pages = (size + page_size - 1) / page_size; start = dom->virt_alloc_end; seg->pfn = dom->pfn_alloc_end; seg->pages = pages; if ( xc_dom_chk_alloc_pages(dom, name, pages) ) return -1; /* map and clear pages */ ptr = xc_dom_seg_to_ptr(dom, seg); if ( ptr == NULL ) return -1; memset(ptr, 0, pages * page_size); seg->vstart = start; seg->vend = dom->virt_alloc_end; DOMPRINTF("%-20s: %-12s : 0x%" PRIx64 " -> 0x%" PRIx64 " (pfn 0x%" PRIpfn " + 0x%" PRIpfn " pages)", __FUNCTION__, name, seg->vstart, seg->vend, seg->pfn, pages); return 0; } xen_pfn_t xc_dom_alloc_page(struct xc_dom_image *dom, char *name) { xen_vaddr_t start; xen_pfn_t pfn; start = dom->virt_alloc_end; pfn = dom->pfn_alloc_end - dom->rambase_pfn; if ( xc_dom_chk_alloc_pages(dom, name, 1) ) return INVALID_PFN; DOMPRINTF("%-20s: %-12s : 0x%" PRIx64 " (pfn 0x%" PRIpfn ")", __FUNCTION__, name, start, pfn); return pfn; } void xc_dom_unmap_one(struct xc_dom_image *dom, xen_pfn_t pfn) { unsigned int page_shift = XC_DOM_PAGE_SHIFT(dom); struct xc_dom_phys *phys, *prev = NULL; for ( phys = dom->phys_pages; phys != NULL; phys = phys->next ) { if ( (pfn >= phys->first) && (pfn < (phys->first + phys->count)) ) break; prev = phys; } if ( !phys ) { DOMPRINTF("%s: Huh? no mapping with pfn 0x%" PRIpfn "", __FUNCTION__, pfn); return; } munmap(phys->ptr, phys->count << page_shift); if ( prev ) prev->next = phys->next; else dom->phys_pages = phys->next; xc_domain_cacheflush(dom->xch, dom->guest_domid, phys->first, phys->count); } void xc_dom_unmap_all(struct xc_dom_image *dom) { while ( dom->phys_pages ) xc_dom_unmap_one(dom, dom->phys_pages->first); } /* ------------------------------------------------------------------------ */ /* pluggable kernel loaders */ static struct xc_dom_loader *first_loader = NULL; static struct xc_dom_arch *first_hook = NULL; void xc_dom_register_loader(struct xc_dom_loader *loader) { loader->next = first_loader; first_loader = loader; } static struct xc_dom_loader *xc_dom_find_loader(struct xc_dom_image *dom) { struct xc_dom_loader *loader = first_loader; while ( loader != NULL ) { DOMPRINTF("%s: trying %s loader ... ", __FUNCTION__, loader->name); if ( loader->probe(dom) == 0 ) { DOMPRINTF("loader probe OK"); return loader; } DOMPRINTF("loader probe failed"); loader = loader->next; } xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: no loader found", __FUNCTION__); return NULL; } void xc_dom_register_arch_hooks(struct xc_dom_arch *hooks) { hooks->next = first_hook; first_hook = hooks; } int xc_dom_set_arch_hooks(struct xc_dom_image *dom) { struct xc_dom_arch *hooks = first_hook; while ( hooks != NULL ) { if ( !strcmp(hooks->guest_type, dom->guest_type) ) { if ( hooks->arch_private_size ) { dom->arch_private = malloc(hooks->arch_private_size); if ( dom->arch_private == NULL ) return -1; memset(dom->arch_private, 0, hooks->arch_private_size); dom->alloc_malloc += hooks->arch_private_size; } dom->arch_hooks = hooks; return 0; } hooks = hooks->next; } xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: not found (type %s)", __FUNCTION__, dom->guest_type); return -1; } /* ------------------------------------------------------------------------ */ /* public interface */ void xc_dom_release(struct xc_dom_image *dom) { DOMPRINTF_CALLED(dom->xch); if ( dom->phys_pages ) xc_dom_unmap_all(dom); xc_dom_free_all(dom); free(dom->arch_private); free(dom); } struct xc_dom_image *xc_dom_allocate(xc_interface *xch, const char *cmdline, const char *features) { struct xc_dom_image *dom; xc_dom_printf(xch, "%s: cmdline=\"%s\", features=\"%s\"", __FUNCTION__, cmdline, features); dom = malloc(sizeof(*dom)); if ( !dom ) goto err; memset(dom, 0, sizeof(*dom)); dom->xch = xch; dom->max_kernel_size = XC_DOM_DECOMPRESS_MAX; dom->max_ramdisk_size = XC_DOM_DECOMPRESS_MAX; dom->max_devicetree_size = XC_DOM_DECOMPRESS_MAX; if ( cmdline ) dom->cmdline = xc_dom_strdup(dom, cmdline); if ( features ) elf_xen_parse_features(features, dom->f_requested, NULL); dom->parms.virt_base = UNSET_ADDR; dom->parms.virt_entry = UNSET_ADDR; dom->parms.virt_hypercall = UNSET_ADDR; dom->parms.virt_hv_start_low = UNSET_ADDR; dom->parms.elf_paddr_offset = UNSET_ADDR; dom->parms.p2m_base = UNSET_ADDR; dom->flags = SIF_VIRT_P2M_4TOOLS; dom->alloc_malloc += sizeof(*dom); return dom; err: if ( dom ) xc_dom_release(dom); return NULL; } int xc_dom_kernel_max_size(struct xc_dom_image *dom, size_t sz) { DOMPRINTF("%s: kernel_max_size=%zx", __FUNCTION__, sz); dom->max_kernel_size = sz; return 0; } int xc_dom_ramdisk_max_size(struct xc_dom_image *dom, size_t sz) { DOMPRINTF("%s: ramdisk_max_size=%zx", __FUNCTION__, sz); dom->max_ramdisk_size = sz; return 0; } int xc_dom_devicetree_max_size(struct xc_dom_image *dom, size_t sz) { DOMPRINTF("%s: devicetree_max_size=%zx", __FUNCTION__, sz); dom->max_devicetree_size = sz; return 0; } int xc_dom_kernel_file(struct xc_dom_image *dom, const char *filename) { DOMPRINTF("%s: filename=\"%s\"", __FUNCTION__, filename); dom->kernel_blob = xc_dom_malloc_filemap(dom, filename, &dom->kernel_size, dom->max_kernel_size); if ( dom->kernel_blob == NULL ) return -1; return xc_dom_try_gunzip(dom, &dom->kernel_blob, &dom->kernel_size); } int xc_dom_ramdisk_file(struct xc_dom_image *dom, const char *filename) { DOMPRINTF("%s: filename=\"%s\"", __FUNCTION__, filename); dom->ramdisk_blob = xc_dom_malloc_filemap(dom, filename, &dom->ramdisk_size, dom->max_ramdisk_size); if ( dom->ramdisk_blob == NULL ) return -1; // return xc_dom_try_gunzip(dom, &dom->ramdisk_blob, &dom->ramdisk_size); return 0; } int xc_dom_devicetree_file(struct xc_dom_image *dom, const char *filename) { #if defined (__arm__) || defined(__aarch64__) DOMPRINTF("%s: filename=\"%s\"", __FUNCTION__, filename); dom->devicetree_blob = xc_dom_malloc_filemap(dom, filename, &dom->devicetree_size, dom->max_devicetree_size); if ( dom->devicetree_blob == NULL ) return -1; return 0; #else errno = -EINVAL; return -1; #endif } int xc_dom_kernel_mem(struct xc_dom_image *dom, const void *mem, size_t memsize) { DOMPRINTF_CALLED(dom->xch); dom->kernel_blob = (void *)mem; dom->kernel_size = memsize; return xc_dom_try_gunzip(dom, &dom->kernel_blob, &dom->kernel_size); } int xc_dom_ramdisk_mem(struct xc_dom_image *dom, const void *mem, size_t memsize) { DOMPRINTF_CALLED(dom->xch); dom->ramdisk_blob = (void *)mem; dom->ramdisk_size = memsize; // return xc_dom_try_gunzip(dom, &dom->ramdisk_blob, &dom->ramdisk_size); return 0; } int xc_dom_devicetree_mem(struct xc_dom_image *dom, const void *mem, size_t memsize) { DOMPRINTF_CALLED(dom->xch); dom->devicetree_blob = (void *)mem; dom->devicetree_size = memsize; return 0; } int xc_dom_parse_image(struct xc_dom_image *dom) { int i; DOMPRINTF_CALLED(dom->xch); /* parse kernel image */ dom->kernel_loader = xc_dom_find_loader(dom); if ( dom->kernel_loader == NULL ) goto err; if ( dom->kernel_loader->parser(dom) != 0 ) goto err; if ( dom->guest_type == NULL ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: guest_type not set", __FUNCTION__); goto err; } /* check features */ for ( i = 0; i < XENFEAT_NR_SUBMAPS; i++ ) { dom->f_active[i] |= dom->f_requested[i]; /* cmd line */ dom->f_active[i] |= dom->parms.f_required[i]; /* kernel */ if ( (dom->f_active[i] & dom->parms.f_supported[i]) != dom->f_active[i] ) { xc_dom_panic(dom->xch, XC_INVALID_PARAM, "%s: unsupported feature requested", __FUNCTION__); goto err; } } return 0; err: return -1; } int xc_dom_rambase_init(struct xc_dom_image *dom, uint64_t rambase) { dom->rambase_pfn = rambase >> XC_PAGE_SHIFT; dom->pfn_alloc_end = dom->rambase_pfn; DOMPRINTF("%s: RAM starts at %"PRI_xen_pfn, __FUNCTION__, dom->rambase_pfn); return 0; } int xc_dom_mem_init(struct xc_dom_image *dom, unsigned int mem_mb) { unsigned int page_shift; xen_pfn_t nr_pages; if ( xc_dom_set_arch_hooks(dom) ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: arch hooks not set", __FUNCTION__); return -1; } page_shift = XC_DOM_PAGE_SHIFT(dom); nr_pages = mem_mb << (20 - page_shift); DOMPRINTF("%s: mem %d MB, pages 0x%" PRIpfn " pages, %dk each", __FUNCTION__, mem_mb, nr_pages, 1 << (page_shift-10)); dom->total_pages = nr_pages; DOMPRINTF("%s: 0x%" PRIpfn " pages", __FUNCTION__, dom->total_pages); return 0; } int xc_dom_update_guest_p2m(struct xc_dom_image *dom) { uint32_t *p2m_32; uint64_t *p2m_64; xen_pfn_t i; if ( !dom->p2m_guest ) return 0; switch ( dom->arch_hooks->sizeof_pfn ) { case 4: DOMPRINTF("%s: dst 32bit, pages 0x%" PRIpfn "", __FUNCTION__, dom->p2m_size); p2m_32 = dom->p2m_guest; for ( i = 0; i < dom->p2m_size; i++ ) if ( dom->p2m_host[i] != INVALID_PFN ) p2m_32[i] = dom->p2m_host[i]; else p2m_32[i] = (uint32_t) - 1; break; case 8: DOMPRINTF("%s: dst 64bit, pages 0x%" PRIpfn "", __FUNCTION__, dom->p2m_size); p2m_64 = dom->p2m_guest; for ( i = 0; i < dom->p2m_size; i++ ) if ( dom->p2m_host[i] != INVALID_PFN ) p2m_64[i] = dom->p2m_host[i]; else p2m_64[i] = (uint64_t) - 1; break; default: xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "sizeof_pfn is invalid (is %d, can be 4 or 8)", dom->arch_hooks->sizeof_pfn); return -1; } return 0; } static int xc_dom_build_ramdisk(struct xc_dom_image *dom) { size_t unziplen, ramdisklen; void *ramdiskmap; if ( !dom->ramdisk_seg.vstart ) { unziplen = xc_dom_check_gzip(dom->xch, dom->ramdisk_blob, dom->ramdisk_size); if ( xc_dom_ramdisk_check_size(dom, unziplen) != 0 ) unziplen = 0; } else unziplen = 0; ramdisklen = unziplen ? unziplen : dom->ramdisk_size; if ( xc_dom_alloc_segment(dom, &dom->ramdisk_seg, "ramdisk", dom->ramdisk_seg.vstart, ramdisklen) != 0 ) goto err; ramdiskmap = xc_dom_seg_to_ptr(dom, &dom->ramdisk_seg); if ( ramdiskmap == NULL ) { DOMPRINTF("%s: xc_dom_seg_to_ptr(dom, &dom->ramdisk_seg) => NULL", __FUNCTION__); goto err; } if ( unziplen ) { if ( xc_dom_do_gunzip(dom->xch, dom->ramdisk_blob, dom->ramdisk_size, ramdiskmap, ramdisklen) == -1 ) goto err; } else memcpy(ramdiskmap, dom->ramdisk_blob, dom->ramdisk_size); return 0; err: return -1; } static int populate_acpi_pages(struct xc_dom_image *dom, xen_pfn_t *extents, unsigned int num_pages) { int rc; xc_interface *xch = dom->xch; uint32_t domid = dom->guest_domid; unsigned long idx; unsigned long first_high_idx = 4UL << (30 - PAGE_SHIFT); /* 4GB */ for ( ; num_pages; num_pages--, extents++ ) { if ( xc_domain_populate_physmap(xch, domid, 1, 0, 0, extents) == 1 ) continue; if ( dom->highmem_end ) { idx = --dom->highmem_end; if ( idx == first_high_idx ) dom->highmem_end = 0; } else { idx = --dom->lowmem_end; } rc = xc_domain_add_to_physmap(xch, domid, XENMAPSPACE_gmfn, idx, *extents); if ( rc ) return rc; } return 0; } static int xc_dom_load_acpi(struct xc_dom_image *dom) { int j, i = 0; unsigned num_pages; xen_pfn_t *extents, base; void *ptr; while ( (i < MAX_ACPI_MODULES) && dom->acpi_modules[i].length ) { DOMPRINTF("%s: %d bytes at address %" PRIx64 "\n", __FUNCTION__, dom->acpi_modules[i].length, dom->acpi_modules[i].guest_addr_out); num_pages = (dom->acpi_modules[i].length + (dom->acpi_modules[i].guest_addr_out & ~XC_PAGE_MASK) + (XC_PAGE_SIZE - 1)) >> XC_PAGE_SHIFT; extents = malloc(num_pages * sizeof(*extents)); if ( !extents ) { DOMPRINTF("%s: Out of memory", __FUNCTION__); goto err; } base = dom->acpi_modules[i].guest_addr_out >> XC_PAGE_SHIFT; for ( j = 0; j < num_pages; j++ ) extents[j] = base + j; if ( populate_acpi_pages(dom, extents, num_pages) ) { DOMPRINTF("%s: Can populate ACPI pages", __FUNCTION__); goto err; } ptr = xc_map_foreign_range(dom->xch, dom->guest_domid, XC_PAGE_SIZE * num_pages, PROT_READ | PROT_WRITE, base); if ( !ptr ) { DOMPRINTF("%s: Can't map %d pages at 0x%"PRI_xen_pfn, __FUNCTION__, num_pages, base); goto err; } memcpy((uint8_t *)ptr + (dom->acpi_modules[i].guest_addr_out & ~XC_PAGE_MASK), dom->acpi_modules[i].data, dom->acpi_modules[i].length); munmap(ptr, XC_PAGE_SIZE * num_pages); free(extents); i++; } return 0; err: free(extents); return -1; } int xc_dom_build_image(struct xc_dom_image *dom) { unsigned int page_size; bool unmapped_initrd; DOMPRINTF_CALLED(dom->xch); /* check for arch hooks */ if ( dom->arch_hooks == NULL ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: arch hooks not set", __FUNCTION__); goto err; } page_size = XC_DOM_PAGE_SIZE(dom); if ( dom->parms.virt_base != UNSET_ADDR ) dom->virt_alloc_end = dom->parms.virt_base; /* load kernel */ if ( xc_dom_alloc_segment(dom, &dom->kernel_seg, "kernel", dom->kernel_seg.vstart, dom->kernel_seg.vend - dom->kernel_seg.vstart) != 0 ) goto err; if ( dom->kernel_loader->loader(dom) != 0 ) goto err; /* Don't load ramdisk now if no initial mapping required. */ unmapped_initrd = dom->parms.unmapped_initrd && !dom->ramdisk_seg.vstart; if ( dom->ramdisk_blob && !unmapped_initrd ) { if ( xc_dom_build_ramdisk(dom) != 0 ) goto err; dom->initrd_start = dom->ramdisk_seg.vstart; dom->initrd_len = dom->ramdisk_seg.vend - dom->ramdisk_seg.vstart; } /* load devicetree */ if ( dom->devicetree_blob ) { void *devicetreemap; if ( xc_dom_alloc_segment(dom, &dom->devicetree_seg, "devicetree", dom->devicetree_seg.vstart, dom->devicetree_size) != 0 ) goto err; devicetreemap = xc_dom_seg_to_ptr(dom, &dom->devicetree_seg); if ( devicetreemap == NULL ) { DOMPRINTF("%s: xc_dom_seg_to_ptr(dom, &dom->devicetree_seg) => NULL", __FUNCTION__); goto err; } memcpy(devicetreemap, dom->devicetree_blob, dom->devicetree_size); } /* load ACPI tables */ if ( xc_dom_load_acpi(dom) != 0 ) goto err; /* allocate other pages */ if ( !dom->arch_hooks->p2m_base_supported || dom->parms.p2m_base >= dom->parms.virt_base || (dom->parms.p2m_base & (XC_DOM_PAGE_SIZE(dom) - 1)) ) dom->parms.p2m_base = UNSET_ADDR; if ( dom->arch_hooks->alloc_p2m_list && dom->parms.p2m_base == UNSET_ADDR && dom->arch_hooks->alloc_p2m_list(dom) != 0 ) goto err; if ( dom->arch_hooks->alloc_magic_pages(dom) != 0 ) goto err; if ( dom->arch_hooks->alloc_pgtables(dom) != 0 ) goto err; if ( dom->alloc_bootstack ) { dom->bootstack_pfn = xc_dom_alloc_page(dom, "boot stack"); if ( dom->bootstack_pfn == INVALID_PFN ) goto err; } DOMPRINTF("%-20s: virt_alloc_end : 0x%" PRIx64 "", __FUNCTION__, dom->virt_alloc_end); DOMPRINTF("%-20s: virt_pgtab_end : 0x%" PRIx64 "", __FUNCTION__, dom->virt_pgtab_end); /* Make sure all memory mapped by initial page tables is available */ if ( dom->virt_pgtab_end && xc_dom_alloc_pad(dom, dom->virt_pgtab_end) ) return -1; /* Load ramdisk if no initial mapping required. */ if ( dom->ramdisk_blob && unmapped_initrd ) { if ( xc_dom_build_ramdisk(dom) != 0 ) goto err; dom->flags |= SIF_MOD_START_PFN; dom->initrd_start = dom->ramdisk_seg.pfn; dom->initrd_len = page_size * dom->ramdisk_seg.pages; } /* Allocate p2m list if outside of initial kernel mapping. */ if ( dom->arch_hooks->alloc_p2m_list && dom->parms.p2m_base != UNSET_ADDR ) { if ( dom->arch_hooks->alloc_p2m_list(dom) != 0 ) goto err; dom->p2m_seg.vstart = dom->parms.p2m_base; } return 0; err: return -1; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_nomigrate.c0000664000175000017500000000341613256712137015545 0ustar smbsmb/****************************************************************************** * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (c) 2011, Citrix Systems */ #include #include #include #include int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iters, uint32_t max_factor, uint32_t flags, struct save_callbacks* callbacks, int hvm, xc_migration_stream_t stream_type, int recv_fd) { errno = ENOSYS; return -1; } int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom, unsigned int store_evtchn, unsigned long *store_mfn, domid_t store_domid, unsigned int console_evtchn, unsigned long *console_mfn, domid_t console_domid, unsigned int hvm, unsigned int pae, int superpages, xc_migration_stream_t stream_type, struct restore_callbacks *callbacks, int send_back_fd) { errno = ENOSYS; return -1; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_common_x86_pv.c0000664000175000017500000001153113256712137016763 0ustar smbsmb#include #include "xc_sr_common_x86_pv.h" xen_pfn_t mfn_to_pfn(struct xc_sr_context *ctx, xen_pfn_t mfn) { assert(mfn <= ctx->x86_pv.max_mfn); return ctx->x86_pv.m2p[mfn]; } bool mfn_in_pseudophysmap(struct xc_sr_context *ctx, xen_pfn_t mfn) { return ( (mfn <= ctx->x86_pv.max_mfn) && (mfn_to_pfn(ctx, mfn) <= ctx->x86_pv.max_pfn) && (xc_pfn_to_mfn(mfn_to_pfn(ctx, mfn), ctx->x86_pv.p2m, ctx->x86_pv.width) == mfn) ); } void dump_bad_pseudophysmap_entry(struct xc_sr_context *ctx, xen_pfn_t mfn) { xc_interface *xch = ctx->xch; xen_pfn_t pfn = ~0UL; ERROR("mfn %#lx, max %#lx", mfn, ctx->x86_pv.max_mfn); if ( (mfn != ~0UL) && (mfn <= ctx->x86_pv.max_mfn) ) { pfn = ctx->x86_pv.m2p[mfn]; ERROR(" m2p[%#lx] = %#lx, max_pfn %#lx", mfn, pfn, ctx->x86_pv.max_pfn); } if ( (pfn != ~0UL) && (pfn <= ctx->x86_pv.max_pfn) ) ERROR(" p2m[%#lx] = %#lx", pfn, xc_pfn_to_mfn(pfn, ctx->x86_pv.p2m, ctx->x86_pv.width)); } xen_pfn_t cr3_to_mfn(struct xc_sr_context *ctx, uint64_t cr3) { if ( ctx->x86_pv.width == 8 ) return cr3 >> 12; else { /* 32bit guests can't represent mfns wider than 32 bits */ if ( cr3 & 0xffffffff00000000UL ) return ~0UL; else return (uint32_t)((cr3 >> 12) | (cr3 << 20)); } } uint64_t mfn_to_cr3(struct xc_sr_context *ctx, xen_pfn_t _mfn) { uint64_t mfn = _mfn; if ( ctx->x86_pv.width == 8 ) return mfn << 12; else { /* 32bit guests can't represent mfns wider than 32 bits */ if ( mfn & 0xffffffff00000000UL ) return ~0UL; else return (uint32_t)((mfn << 12) | (mfn >> 20)); } } int x86_pv_domain_info(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; unsigned int guest_width, guest_levels; /* Get the domain width */ if ( xc_domain_get_guest_width(xch, ctx->domid, &guest_width) ) { PERROR("Unable to determine dom%d's width", ctx->domid); return -1; } if ( guest_width == 4 ) guest_levels = 3; else if ( guest_width == 8 ) guest_levels = 4; else { ERROR("Invalid guest width %d. Expected 32 or 64", guest_width * 8); return -1; } ctx->x86_pv.width = guest_width; ctx->x86_pv.levels = guest_levels; DPRINTF("%d bits, %d levels", guest_width * 8, guest_levels); return 0; } int x86_pv_map_m2p(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; xen_pfn_t m2p_chunks, m2p_size, max_page; privcmd_mmap_entry_t *entries = NULL; xen_pfn_t *extents_start = NULL; int rc = -1, i; if ( xc_maximum_ram_page(xch, &max_page) < 0 ) { PERROR("Failed to get maximum ram page"); goto err; } ctx->x86_pv.max_mfn = max_page; m2p_size = M2P_SIZE(ctx->x86_pv.max_mfn); m2p_chunks = M2P_CHUNKS(ctx->x86_pv.max_mfn); extents_start = malloc(m2p_chunks * sizeof(xen_pfn_t)); if ( !extents_start ) { ERROR("Unable to allocate %lu bytes for m2p mfns", m2p_chunks * sizeof(xen_pfn_t)); goto err; } if ( xc_machphys_mfn_list(xch, m2p_chunks, extents_start) ) { PERROR("Failed to get m2p mfn list"); goto err; } entries = malloc(m2p_chunks * sizeof(privcmd_mmap_entry_t)); if ( !entries ) { ERROR("Unable to allocate %lu bytes for m2p mapping mfns", m2p_chunks * sizeof(privcmd_mmap_entry_t)); goto err; } for ( i = 0; i < m2p_chunks; ++i ) entries[i].mfn = extents_start[i]; ctx->x86_pv.m2p = xc_map_foreign_ranges( xch, DOMID_XEN, m2p_size, PROT_READ, M2P_CHUNK_SIZE, entries, m2p_chunks); if ( !ctx->x86_pv.m2p ) { PERROR("Failed to mmap() m2p ranges"); goto err; } ctx->x86_pv.nr_m2p_frames = (M2P_CHUNK_SIZE >> PAGE_SHIFT) * m2p_chunks; #ifdef __i386__ /* 32 bit toolstacks automatically get the compat m2p */ ctx->x86_pv.compat_m2p_mfn0 = entries[0].mfn; #else /* 64 bit toolstacks need to ask Xen specially for it */ { struct xen_machphys_mfn_list xmml = { .max_extents = 1, .extent_start = { &ctx->x86_pv.compat_m2p_mfn0 } }; rc = do_memory_op(xch, XENMEM_machphys_compat_mfn_list, &xmml, sizeof(xmml)); if ( rc || xmml.nr_extents != 1 ) { PERROR("Failed to get compat mfn list from Xen"); rc = -1; goto err; } } #endif /* All Done */ rc = 0; DPRINTF("max_mfn %#lx", ctx->x86_pv.max_mfn); err: free(entries); free(extents_start); return rc; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_evtchn.c0000664000175000017500000000457513256712137015056 0ustar smbsmb/****************************************************************************** * xc_evtchn.c * * API for manipulating and accessing inter-domain event channels. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (c) 2004, K A Fraser. */ #include "xc_private.h" static int do_evtchn_op(xc_interface *xch, int cmd, void *arg, size_t arg_size, int silently_fail) { int ret = -1; DECLARE_HYPERCALL_BOUNCE(arg, arg_size, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); if ( xc_hypercall_bounce_pre(xch, arg) ) { PERROR("do_evtchn_op: bouncing arg failed"); goto out; } ret = xencall2(xch->xcall, __HYPERVISOR_event_channel_op, cmd, HYPERCALL_BUFFER_AS_ARG(arg)); if ( ret < 0 && !silently_fail ) ERROR("do_evtchn_op: HYPERVISOR_event_channel_op failed: %d", ret); xc_hypercall_bounce_post(xch, arg); out: return ret; } xc_evtchn_port_or_error_t xc_evtchn_alloc_unbound(xc_interface *xch, uint32_t dom, uint32_t remote_dom) { int rc; struct evtchn_alloc_unbound arg = { .dom = (domid_t)dom, .remote_dom = (domid_t)remote_dom }; rc = do_evtchn_op(xch, EVTCHNOP_alloc_unbound, &arg, sizeof(arg), 0); if ( rc == 0 ) rc = arg.port; return rc; } int xc_evtchn_reset(xc_interface *xch, uint32_t dom) { struct evtchn_reset arg = { .dom = (domid_t)dom }; return do_evtchn_op(xch, EVTCHNOP_reset, &arg, sizeof(arg), 0); } int xc_evtchn_status(xc_interface *xch, xc_evtchn_status_t *status) { return do_evtchn_op(xch, EVTCHNOP_status, status, sizeof(*status), 1); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_stream_format.h0000664000175000017500000000627413256712137017141 0ustar smbsmb#ifndef __STREAM_FORMAT__H #define __STREAM_FORMAT__H /* * C structures for the Migration v2 stream format. * See docs/specs/libxc-migration-stream.pandoc */ #include /* * Image Header */ struct xc_sr_ihdr { uint64_t marker; uint32_t id; uint32_t version; uint16_t options; uint16_t _res1; uint32_t _res2; }; #define IHDR_MARKER 0xffffffffffffffffULL #define IHDR_ID 0x58454E46U #define IHDR_VERSION 2 #define _IHDR_OPT_ENDIAN 0 #define IHDR_OPT_LITTLE_ENDIAN (0 << _IHDR_OPT_ENDIAN) #define IHDR_OPT_BIG_ENDIAN (1 << _IHDR_OPT_ENDIAN) /* * Domain Header */ struct xc_sr_dhdr { uint32_t type; uint16_t page_shift; uint16_t _res1; uint32_t xen_major; uint32_t xen_minor; }; #define DHDR_TYPE_X86_PV 0x00000001U #define DHDR_TYPE_X86_HVM 0x00000002U #define DHDR_TYPE_X86_PVH 0x00000003U #define DHDR_TYPE_ARM 0x00000004U /* * Record Header */ struct xc_sr_rhdr { uint32_t type; uint32_t length; }; /* All records must be aligned up to an 8 octet boundary */ #define REC_ALIGN_ORDER (3U) /* Somewhat arbitrary - 8MB */ #define REC_LENGTH_MAX (8U << 20) #define REC_TYPE_END 0x00000000U #define REC_TYPE_PAGE_DATA 0x00000001U #define REC_TYPE_X86_PV_INFO 0x00000002U #define REC_TYPE_X86_PV_P2M_FRAMES 0x00000003U #define REC_TYPE_X86_PV_VCPU_BASIC 0x00000004U #define REC_TYPE_X86_PV_VCPU_EXTENDED 0x00000005U #define REC_TYPE_X86_PV_VCPU_XSAVE 0x00000006U #define REC_TYPE_SHARED_INFO 0x00000007U #define REC_TYPE_TSC_INFO 0x00000008U #define REC_TYPE_HVM_CONTEXT 0x00000009U #define REC_TYPE_HVM_PARAMS 0x0000000aU #define REC_TYPE_TOOLSTACK 0x0000000bU #define REC_TYPE_X86_PV_VCPU_MSRS 0x0000000cU #define REC_TYPE_VERIFY 0x0000000dU #define REC_TYPE_CHECKPOINT 0x0000000eU #define REC_TYPE_CHECKPOINT_DIRTY_PFN_LIST 0x0000000fU #define REC_TYPE_OPTIONAL 0x80000000U /* PAGE_DATA */ struct xc_sr_rec_page_data_header { uint32_t count; uint32_t _res1; uint64_t pfn[0]; }; #define PAGE_DATA_PFN_MASK 0x000fffffffffffffULL #define PAGE_DATA_TYPE_MASK 0xf000000000000000ULL /* X86_PV_INFO */ struct xc_sr_rec_x86_pv_info { uint8_t guest_width; uint8_t pt_levels; uint8_t _res[6]; }; /* X86_PV_P2M_FRAMES */ struct xc_sr_rec_x86_pv_p2m_frames { uint32_t start_pfn; uint32_t end_pfn; uint64_t p2m_pfns[0]; }; /* X86_PV_VCPU_{BASIC,EXTENDED,XSAVE,MSRS} */ struct xc_sr_rec_x86_pv_vcpu_hdr { uint32_t vcpu_id; uint32_t _res1; uint8_t context[0]; }; /* TSC_INFO */ struct xc_sr_rec_tsc_info { uint32_t mode; uint32_t khz; uint64_t nsec; uint32_t incarnation; uint32_t _res1; }; /* HVM_PARAMS */ struct xc_sr_rec_hvm_params_entry { uint64_t index; uint64_t value; }; struct xc_sr_rec_hvm_params { uint32_t count; uint32_t _res1; struct xc_sr_rec_hvm_params_entry param[0]; }; #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_private.c0000664000175000017500000004775213256712137015245 0ustar smbsmb/****************************************************************************** * xc_private.c * * Helper functions for the rest of the library. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include "xg_private.h" #include "xc_dom.h" #include #include #include #include #include struct xc_interface_core *xc_interface_open(xentoollog_logger *logger, xentoollog_logger *dombuild_logger, unsigned open_flags) { struct xc_interface_core xch_buf = { 0 }, *xch = &xch_buf; xch->flags = open_flags; xch->dombuild_logger_file = 0; xc_clear_last_error(xch); xch->error_handler = logger; xch->error_handler_tofree = 0; xch->dombuild_logger = dombuild_logger; xch->dombuild_logger_tofree = 0; if (!xch->error_handler) { xch->error_handler = xch->error_handler_tofree = (xentoollog_logger*) xtl_createlogger_stdiostream(stderr, XTL_PROGRESS, 0); if (!xch->error_handler) goto err; } xch = malloc(sizeof(*xch)); if (!xch) { xch = &xch_buf; PERROR("Could not allocate new xc_interface struct"); goto err; } *xch = xch_buf; if (open_flags & XC_OPENFLAG_DUMMY) return xch; /* We are done */ xch->xcall = xencall_open(xch->error_handler, open_flags & XC_OPENFLAG_NON_REENTRANT ? XENCALL_OPENFLAG_NON_REENTRANT : 0U); if ( xch->xcall == NULL ) goto err; xch->fmem = xenforeignmemory_open(xch->error_handler, 0); if ( xch->fmem == NULL ) goto err; xch->dmod = xendevicemodel_open(xch->error_handler, 0); if ( xch->dmod == NULL ) goto err; return xch; err: xenforeignmemory_close(xch->fmem); xencall_close(xch->xcall); xtl_logger_destroy(xch->error_handler_tofree); if (xch != &xch_buf) free(xch); return NULL; } int xc_interface_close(xc_interface *xch) { int rc = 0; if (!xch) return 0; rc = xencall_close(xch->xcall); if (rc) PERROR("Could not close xencall interface"); rc = xenforeignmemory_close(xch->fmem); if (rc) PERROR("Could not close foreign memory interface"); rc = xendevicemodel_close(xch->dmod); if (rc) PERROR("Could not close device model interface"); xtl_logger_destroy(xch->dombuild_logger_tofree); xtl_logger_destroy(xch->error_handler_tofree); free(xch); return rc; } static pthread_key_t errbuf_pkey; static pthread_once_t errbuf_pkey_once = PTHREAD_ONCE_INIT; const xc_error *xc_get_last_error(xc_interface *xch) { return &xch->last_error; } void xc_clear_last_error(xc_interface *xch) { xch->last_error.code = XC_ERROR_NONE; xch->last_error.message[0] = '\0'; } const char *xc_error_code_to_desc(int code) { /* Sync to members of xc_error_code enumeration in xenctrl.h */ switch ( code ) { case XC_ERROR_NONE: return "No error details"; case XC_INTERNAL_ERROR: return "Internal error"; case XC_INVALID_KERNEL: return "Invalid kernel"; case XC_INVALID_PARAM: return "Invalid configuration"; case XC_OUT_OF_MEMORY: return "Out of memory"; } return "Unknown error code"; } void xc_reportv(xc_interface *xch, xentoollog_logger *lg, xentoollog_level level, int code, const char *fmt, va_list args) { int saved_errno = errno; char msgbuf[XC_MAX_ERROR_MSG_LEN]; char *msg; /* Strip newlines from messages. * XXX really the messages themselves should have the newlines removed. */ char fmt_nonewline[512]; int fmt_l; fmt_l = strlen(fmt); if (fmt_l && fmt[fmt_l-1]=='\n' && fmt_l < sizeof(fmt_nonewline)) { memcpy(fmt_nonewline, fmt, fmt_l-1); fmt_nonewline[fmt_l-1] = 0; fmt = fmt_nonewline; } if ( level >= XTL_ERROR ) { msg = xch->last_error.message; xch->last_error.code = code; } else { msg = msgbuf; } vsnprintf(msg, XC_MAX_ERROR_MSG_LEN-1, fmt, args); msg[XC_MAX_ERROR_MSG_LEN-1] = '\0'; xtl_log(lg, level, -1, "xc", "%s" "%s%s", msg, code?": ":"", code ? xc_error_code_to_desc(code) : ""); errno = saved_errno; } void xc_report(xc_interface *xch, xentoollog_logger *lg, xentoollog_level level, int code, const char *fmt, ...) { va_list args; va_start(args,fmt); xc_reportv(xch,lg,level,code,fmt,args); va_end(args); } void xc_report_error(xc_interface *xch, int code, const char *fmt, ...) { va_list args; va_start(args, fmt); xc_reportv(xch, xch->error_handler, XTL_ERROR, code, fmt, args); va_end(args); } const char *xc_set_progress_prefix(xc_interface *xch, const char *doing) { const char *old = xch->currently_progress_reporting; xch->currently_progress_reporting = doing; return old; } void xc_report_progress_single(xc_interface *xch, const char *doing) { assert(doing); xtl_progress(xch->error_handler, "xc", doing, 0, 0); } void xc_report_progress_step(xc_interface *xch, unsigned long done, unsigned long total) { assert(xch->currently_progress_reporting); xtl_progress(xch->error_handler, "xc", xch->currently_progress_reporting, done, total); } int xc_get_pfn_type_batch(xc_interface *xch, uint32_t dom, unsigned int num, xen_pfn_t *arr) { int rc; DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(arr, sizeof(*arr) * num, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); if ( xc_hypercall_bounce_pre(xch, arr) ) return -1; domctl.cmd = XEN_DOMCTL_getpageframeinfo3; domctl.domain = (domid_t)dom; domctl.u.getpageframeinfo3.num = num; set_xen_guest_handle(domctl.u.getpageframeinfo3.array, arr); rc = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, arr); return rc; } int xc_mmuext_op( xc_interface *xch, struct mmuext_op *op, unsigned int nr_ops, domid_t dom) { DECLARE_HYPERCALL_BOUNCE(op, nr_ops*sizeof(*op), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); long ret = -1; if ( xc_hypercall_bounce_pre(xch, op) ) { PERROR("Could not bounce memory for mmuext op hypercall"); goto out1; } ret = xencall4(xch->xcall, __HYPERVISOR_mmuext_op, HYPERCALL_BUFFER_AS_ARG(op), nr_ops, 0, dom); xc_hypercall_bounce_post(xch, op); out1: return ret; } static int flush_mmu_updates(xc_interface *xch, struct xc_mmu *mmu) { int rc, err = 0; DECLARE_NAMED_HYPERCALL_BOUNCE(updates, mmu->updates, mmu->idx*sizeof(*mmu->updates), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); if ( mmu->idx == 0 ) return 0; if ( xc_hypercall_bounce_pre(xch, updates) ) { PERROR("flush_mmu_updates: bounce buffer failed"); err = 1; goto out; } rc = xencall4(xch->xcall, __HYPERVISOR_mmu_update, HYPERCALL_BUFFER_AS_ARG(updates), mmu->idx, 0, mmu->subject); if ( rc < 0 ) { ERROR("Failure when submitting mmu updates"); err = 1; } mmu->idx = 0; xc_hypercall_bounce_post(xch, updates); out: return err; } struct xc_mmu *xc_alloc_mmu_updates(xc_interface *xch, unsigned int subject) { struct xc_mmu *mmu = malloc(sizeof(*mmu)); if ( mmu == NULL ) return mmu; mmu->idx = 0; mmu->subject = subject; return mmu; } int xc_add_mmu_update(xc_interface *xch, struct xc_mmu *mmu, unsigned long long ptr, unsigned long long val) { mmu->updates[mmu->idx].ptr = ptr; mmu->updates[mmu->idx].val = val; if ( ++mmu->idx == MAX_MMU_UPDATES ) return flush_mmu_updates(xch, mmu); return 0; } int xc_flush_mmu_updates(xc_interface *xch, struct xc_mmu *mmu) { return flush_mmu_updates(xch, mmu); } long do_memory_op(xc_interface *xch, int cmd, void *arg, size_t len) { DECLARE_HYPERCALL_BOUNCE(arg, len, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); long ret = -1; if ( xc_hypercall_bounce_pre(xch, arg) ) { PERROR("Could not bounce memory for XENMEM hypercall"); goto out1; } ret = xencall2(xch->xcall, __HYPERVISOR_memory_op, cmd, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_bounce_post(xch, arg); out1: return ret; } int xc_maximum_ram_page(xc_interface *xch, unsigned long *max_mfn) { long rc = do_memory_op(xch, XENMEM_maximum_ram_page, NULL, 0); if ( rc >= 0 ) { *max_mfn = rc; rc = 0; } return rc; } long long xc_domain_get_cpu_usage( xc_interface *xch, domid_t domid, int vcpu ) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_getvcpuinfo; domctl.domain = (domid_t)domid; domctl.u.getvcpuinfo.vcpu = (uint16_t)vcpu; if ( (do_domctl(xch, &domctl) < 0) ) { PERROR("Could not get info on domain"); return -1; } return domctl.u.getvcpuinfo.cpu_time; } int xc_machphys_mfn_list(xc_interface *xch, unsigned long max_extents, xen_pfn_t *extent_start) { int rc; DECLARE_HYPERCALL_BOUNCE(extent_start, max_extents * sizeof(xen_pfn_t), XC_HYPERCALL_BUFFER_BOUNCE_OUT); struct xen_machphys_mfn_list xmml = { .max_extents = max_extents, }; if ( xc_hypercall_bounce_pre(xch, extent_start) ) { PERROR("Could not bounce memory for XENMEM_machphys_mfn_list hypercall"); return -1; } set_xen_guest_handle(xmml.extent_start, extent_start); rc = do_memory_op(xch, XENMEM_machphys_mfn_list, &xmml, sizeof(xmml)); if (rc || xmml.nr_extents != max_extents) rc = -1; else rc = 0; xc_hypercall_bounce_post(xch, extent_start); return rc; } int xc_get_pfn_list(xc_interface *xch, uint32_t domid, uint64_t *pfn_buf, unsigned long max_pfns) { DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(pfn_buf, max_pfns * sizeof(*pfn_buf), XC_HYPERCALL_BUFFER_BOUNCE_OUT); int ret; if ( xc_hypercall_bounce_pre(xch, pfn_buf) ) { PERROR("xc_get_pfn_list: pfn_buf bounce failed"); return -1; } domctl.cmd = XEN_DOMCTL_getmemlist; domctl.domain = (domid_t)domid; domctl.u.getmemlist.max_pfns = max_pfns; set_xen_guest_handle(domctl.u.getmemlist.buffer, pfn_buf); ret = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, pfn_buf); return (ret < 0) ? -1 : domctl.u.getmemlist.num_pfns; } long xc_get_tot_pages(xc_interface *xch, uint32_t domid) { xc_dominfo_t info; if ( (xc_domain_getinfo(xch, domid, 1, &info) != 1) || (info.domid != domid) ) return -1; return info.nr_pages; } int xc_copy_to_domain_page(xc_interface *xch, uint32_t domid, unsigned long dst_pfn, const char *src_page) { void *vaddr = xc_map_foreign_range( xch, domid, PAGE_SIZE, PROT_WRITE, dst_pfn); if ( vaddr == NULL ) return -1; memcpy(vaddr, src_page, PAGE_SIZE); munmap(vaddr, PAGE_SIZE); xc_domain_cacheflush(xch, domid, dst_pfn, 1); return 0; } int xc_clear_domain_pages(xc_interface *xch, uint32_t domid, unsigned long dst_pfn, int num) { size_t size = num * PAGE_SIZE; void *vaddr = xc_map_foreign_range( xch, domid, size, PROT_WRITE, dst_pfn); if ( vaddr == NULL ) return -1; memset(vaddr, 0, size); munmap(vaddr, size); xc_domain_cacheflush(xch, domid, dst_pfn, num); return 0; } int xc_domctl(xc_interface *xch, struct xen_domctl *domctl) { return do_domctl(xch, domctl); } int xc_sysctl(xc_interface *xch, struct xen_sysctl *sysctl) { return do_sysctl(xch, sysctl); } int xc_version(xc_interface *xch, int cmd, void *arg) { DECLARE_HYPERCALL_BOUNCE(arg, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); /* Size unknown until cmd decoded */ size_t sz; int rc; switch ( cmd ) { case XENVER_version: sz = 0; break; case XENVER_extraversion: sz = sizeof(xen_extraversion_t); break; case XENVER_compile_info: sz = sizeof(xen_compile_info_t); break; case XENVER_capabilities: sz = sizeof(xen_capabilities_info_t); break; case XENVER_changeset: sz = sizeof(xen_changeset_info_t); break; case XENVER_platform_parameters: sz = sizeof(xen_platform_parameters_t); break; case XENVER_get_features: sz = sizeof(xen_feature_info_t); break; case XENVER_pagesize: sz = 0; break; case XENVER_guest_handle: sz = sizeof(xen_domain_handle_t); break; case XENVER_commandline: sz = sizeof(xen_commandline_t); break; case XENVER_build_id: { xen_build_id_t *build_id = (xen_build_id_t *)arg; sz = sizeof(*build_id) + build_id->len; HYPERCALL_BOUNCE_SET_DIR(arg, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); break; } default: ERROR("xc_version: unknown command %d\n", cmd); return -EINVAL; } HYPERCALL_BOUNCE_SET_SIZE(arg, sz); if ( (sz != 0) && xc_hypercall_bounce_pre(xch, arg) ) { PERROR("Could not bounce buffer for version hypercall"); return -ENOMEM; } rc = do_xen_version(xch, cmd, HYPERCALL_BUFFER(arg)); if ( sz != 0 ) xc_hypercall_bounce_post(xch, arg); return rc; } unsigned long xc_make_page_below_4G( xc_interface *xch, uint32_t domid, unsigned long mfn) { xen_pfn_t old_mfn = mfn; xen_pfn_t new_mfn; if ( xc_domain_decrease_reservation_exact( xch, domid, 1, 0, &old_mfn) != 0 ) { DPRINTF("xc_make_page_below_4G decrease failed. mfn=%lx\n",mfn); return 0; } if ( xc_domain_increase_reservation_exact( xch, domid, 1, 0, XENMEMF_address_bits(32), &new_mfn) != 0 ) { DPRINTF("xc_make_page_below_4G increase failed. mfn=%lx\n",mfn); return 0; } return new_mfn; } static void _xc_clean_errbuf(void * m) { free(m); pthread_setspecific(errbuf_pkey, NULL); } static void _xc_init_errbuf(void) { pthread_key_create(&errbuf_pkey, _xc_clean_errbuf); } const char *xc_strerror(xc_interface *xch, int errcode) { if ( xch->flags & XC_OPENFLAG_NON_REENTRANT ) { return strerror(errcode); } else { #define XS_BUFSIZE 32 char *errbuf; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; char *strerror_str; pthread_once(&errbuf_pkey_once, _xc_init_errbuf); errbuf = pthread_getspecific(errbuf_pkey); if (errbuf == NULL) { errbuf = malloc(XS_BUFSIZE); if ( errbuf == NULL ) return "(failed to allocate errbuf)"; pthread_setspecific(errbuf_pkey, errbuf); } /* * Thread-unsafe strerror() is protected by a local mutex. We copy the * string to a thread-private buffer before releasing the mutex. */ pthread_mutex_lock(&mutex); strerror_str = strerror(errcode); strncpy(errbuf, strerror_str, XS_BUFSIZE); errbuf[XS_BUFSIZE-1] = '\0'; pthread_mutex_unlock(&mutex); return errbuf; } } void bitmap_64_to_byte(uint8_t *bp, const uint64_t *lp, int nbits) { uint64_t l; int i, j, b; for (i = 0, b = 0; nbits > 0; i++, b += sizeof(l)) { l = lp[i]; for (j = 0; (j < sizeof(l)) && (nbits > 0); j++) { bp[b+j] = l; l >>= 8; nbits -= 8; } } } void bitmap_byte_to_64(uint64_t *lp, const uint8_t *bp, int nbits) { uint64_t l; int i, j, b; for (i = 0, b = 0; nbits > 0; i++, b += sizeof(l)) { l = 0; for (j = 0; (j < sizeof(l)) && (nbits > 0); j++) { l |= (uint64_t)bp[b+j] << (j*8); nbits -= 8; } lp[i] = l; } } int read_exact(int fd, void *data, size_t size) { size_t offset = 0; ssize_t len; while ( offset < size ) { len = read(fd, (char *)data + offset, size - offset); if ( (len == -1) && (errno == EINTR) ) continue; if ( len == 0 ) errno = 0; if ( len <= 0 ) return -1; offset += len; } return 0; } int write_exact(int fd, const void *data, size_t size) { size_t offset = 0; ssize_t len; while ( offset < size ) { len = write(fd, (const char *)data + offset, size - offset); if ( (len == -1) && (errno == EINTR) ) continue; if ( len <= 0 ) return -1; offset += len; } return 0; } #if defined(__MINIOS__) /* * MiniOS's libc doesn't know about writev(). Implement it as multiple write()s. */ int writev_exact(int fd, const struct iovec *iov, int iovcnt) { int rc, i; for ( i = 0; i < iovcnt; ++i ) { rc = write_exact(fd, iov[i].iov_base, iov[i].iov_len); if ( rc ) return rc; } return 0; } #else int writev_exact(int fd, const struct iovec *iov, int iovcnt) { struct iovec *local_iov = NULL; int rc = 0, iov_idx = 0, saved_errno = 0; ssize_t len; while ( iov_idx < iovcnt ) { /* * Skip over iov[] entries with 0 length. * * This is needed to cover the case where we took a partial write and * all remaining vectors are of 0 length. In such a case, the results * from writev() are indistinguishable from EOF. */ while ( iov[iov_idx].iov_len == 0 ) if ( ++iov_idx == iovcnt ) goto out; len = writev(fd, &iov[iov_idx], min(iovcnt - iov_idx, IOV_MAX)); saved_errno = errno; if ( (len == -1) && (errno == EINTR) ) continue; if ( len <= 0 ) { rc = -1; goto out; } /* Check iov[] to see whether we had a partial or complete write. */ while ( (len > 0) && (iov_idx < iovcnt) ) { if ( len >= iov[iov_idx].iov_len ) len -= iov[iov_idx++].iov_len; else { /* Partial write of iov[iov_idx]. Copy iov so we can adjust * element iov_idx and resubmit the rest. */ if ( !local_iov ) { local_iov = malloc(iovcnt * sizeof(*iov)); if ( !local_iov ) { saved_errno = ENOMEM; goto out; } iov = memcpy(local_iov, iov, iovcnt * sizeof(*iov)); } local_iov[iov_idx].iov_base += len; local_iov[iov_idx].iov_len -= len; break; } } } saved_errno = 0; out: free(local_iov); errno = saved_errno; return rc; } #endif int xc_ffs8(uint8_t x) { int i; for ( i = 0; i < 8; i++ ) if ( x & (1u << i) ) return i+1; return 0; } int xc_ffs16(uint16_t x) { uint8_t h = x>>8, l = x; return l ? xc_ffs8(l) : h ? xc_ffs8(h) + 8 : 0; } int xc_ffs32(uint32_t x) { uint16_t h = x>>16, l = x; return l ? xc_ffs16(l) : h ? xc_ffs16(h) + 16 : 0; } int xc_ffs64(uint64_t x) { uint32_t h = x>>32, l = x; return l ? xc_ffs32(l) : h ? xc_ffs32(h) + 32 : 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_tbuf.c0000664000175000017500000001100613256712137014512 0ustar smbsmb/****************************************************************************** * xc_tbuf.c * * API for manipulating and accessing trace buffer parameters * * Copyright (c) 2005, Rob Gardner * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include static int tbuf_enable(xc_interface *xch, int enable) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_tbuf_op; sysctl.interface_version = XEN_SYSCTL_INTERFACE_VERSION; if ( enable ) sysctl.u.tbuf_op.cmd = XEN_SYSCTL_TBUFOP_enable; else sysctl.u.tbuf_op.cmd = XEN_SYSCTL_TBUFOP_disable; return xc_sysctl(xch, &sysctl); } int xc_tbuf_set_size(xc_interface *xch, unsigned long size) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_tbuf_op; sysctl.interface_version = XEN_SYSCTL_INTERFACE_VERSION; sysctl.u.tbuf_op.cmd = XEN_SYSCTL_TBUFOP_set_size; sysctl.u.tbuf_op.size = size; return xc_sysctl(xch, &sysctl); } int xc_tbuf_get_size(xc_interface *xch, unsigned long *size) { struct t_info *t_info; int rc; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_tbuf_op; sysctl.interface_version = XEN_SYSCTL_INTERFACE_VERSION; sysctl.u.tbuf_op.cmd = XEN_SYSCTL_TBUFOP_get_info; rc = xc_sysctl(xch, &sysctl); if ( rc != 0 ) return rc; t_info = xc_map_foreign_range(xch, DOMID_XEN, sysctl.u.tbuf_op.size, PROT_READ | PROT_WRITE, sysctl.u.tbuf_op.buffer_mfn); if ( t_info == NULL || t_info->tbuf_size == 0 ) rc = -1; else *size = t_info->tbuf_size; xenforeignmemory_unmap(xch->fmem, t_info, sysctl.u.tbuf_op.size); return rc; } int xc_tbuf_enable(xc_interface *xch, unsigned long pages, unsigned long *mfn, unsigned long *size) { DECLARE_SYSCTL; int rc; /* * Ignore errors (at least for now) as we get an error if size is already * set (since trace buffers cannot be reallocated). If we really have no * buffers at all then tbuf_enable() will fail, so this is safe. */ (void)xc_tbuf_set_size(xch, pages); if ( tbuf_enable(xch, 1) != 0 ) return -1; sysctl.cmd = XEN_SYSCTL_tbuf_op; sysctl.interface_version = XEN_SYSCTL_INTERFACE_VERSION; sysctl.u.tbuf_op.cmd = XEN_SYSCTL_TBUFOP_get_info; rc = xc_sysctl(xch, &sysctl); if ( rc == 0 ) { *size = sysctl.u.tbuf_op.size; *mfn = sysctl.u.tbuf_op.buffer_mfn; } return 0; } int xc_tbuf_disable(xc_interface *xch) { return tbuf_enable(xch, 0); } int xc_tbuf_set_cpu_mask(xc_interface *xch, xc_cpumap_t mask) { DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(mask, 0, XC_HYPERCALL_BUFFER_BOUNCE_IN); int ret = -1; int bits, cpusize; cpusize = xc_get_cpumap_size(xch); if (cpusize <= 0) { PERROR("Could not get number of cpus"); return -1; } HYPERCALL_BOUNCE_SET_SIZE(mask, cpusize); bits = xc_get_max_cpus(xch); if (bits <= 0) { PERROR("Could not get number of bits"); return -1; } if ( xc_hypercall_bounce_pre(xch, mask) ) { PERROR("Could not allocate memory for xc_tbuf_set_cpu_mask hypercall"); goto out; } sysctl.cmd = XEN_SYSCTL_tbuf_op; sysctl.interface_version = XEN_SYSCTL_INTERFACE_VERSION; sysctl.u.tbuf_op.cmd = XEN_SYSCTL_TBUFOP_set_cpu_mask; set_xen_guest_handle(sysctl.u.tbuf_op.cpu_mask.bitmap, mask); sysctl.u.tbuf_op.cpu_mask.nr_bits = bits; ret = do_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, mask); out: return ret; } int xc_tbuf_set_evt_mask(xc_interface *xch, uint32_t mask) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_tbuf_op; sysctl.interface_version = XEN_SYSCTL_INTERFACE_VERSION; sysctl.u.tbuf_op.cmd = XEN_SYSCTL_TBUFOP_set_evt_mask; sysctl.u.tbuf_op.evt_mask = mask; return do_sysctl(xch, &sysctl); } xen-4.9.2/tools/libxc/xc_csched2.c0000664000175000017500000000560113256712137015071 0ustar smbsmb/**************************************************************************** * (C) 2006 - Emmanuel Ackaouy - XenSource Inc. **************************************************************************** * * File: xc_csched.c * Author: Emmanuel Ackaouy * * Description: XC Interface to the credit scheduler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" int xc_sched_credit2_domain_set( xc_interface *xch, uint32_t domid, struct xen_domctl_sched_credit2 *sdom) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_scheduler_op; domctl.domain = (domid_t) domid; domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_CREDIT2; domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_putinfo; domctl.u.scheduler_op.u.credit2 = *sdom; if ( do_domctl(xch, &domctl) ) return -1; return 0; } int xc_sched_credit2_domain_get( xc_interface *xch, uint32_t domid, struct xen_domctl_sched_credit2 *sdom) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_scheduler_op; domctl.domain = (domid_t) domid; domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_CREDIT2; domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_getinfo; if ( do_domctl(xch, &domctl) ) return -1; *sdom = domctl.u.scheduler_op.u.credit2; return 0; } int xc_sched_credit2_params_set( xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_credit2_schedule *schedule) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_scheduler_op; sysctl.u.scheduler_op.cpupool_id = cpupool_id; sysctl.u.scheduler_op.sched_id = XEN_SCHEDULER_CREDIT2; sysctl.u.scheduler_op.cmd = XEN_SYSCTL_SCHEDOP_putinfo; sysctl.u.scheduler_op.u.sched_credit2 = *schedule; if ( do_sysctl(xch, &sysctl) ) return -1; *schedule = sysctl.u.scheduler_op.u.sched_credit2; return 0; } int xc_sched_credit2_params_get( xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_credit2_schedule *schedule) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_scheduler_op; sysctl.u.scheduler_op.cpupool_id = cpupool_id; sysctl.u.scheduler_op.sched_id = XEN_SCHEDULER_CREDIT2; sysctl.u.scheduler_op.cmd = XEN_SYSCTL_SCHEDOP_getinfo; if ( do_sysctl(xch, &sysctl) ) return -1; *schedule = sysctl.u.scheduler_op.u.sched_credit2; return 0; } xen-4.9.2/tools/libxc/xc_sr_restore_x86_pv.c0000664000175000017500000007771613256712137017177 0ustar smbsmb#include #include "xc_sr_common_x86_pv.h" static xen_pfn_t pfn_to_mfn(const struct xc_sr_context *ctx, xen_pfn_t pfn) { assert(pfn <= ctx->x86_pv.max_pfn); return xc_pfn_to_mfn(pfn, ctx->x86_pv.p2m, ctx->x86_pv.width); } /* * Expand our local tracking information for the p2m table and domains maximum * size. Normally this will be called once to expand from 0 to max_pfn, but * is liable to expand multiple times if the domain grows on the sending side * after migration has started. */ static int expand_p2m(struct xc_sr_context *ctx, unsigned long max_pfn) { xc_interface *xch = ctx->xch; unsigned long old_max = ctx->x86_pv.max_pfn, i; unsigned int fpp = PAGE_SIZE / ctx->x86_pv.width; unsigned long end_frame = (max_pfn / fpp) + 1; unsigned long old_end_frame = (old_max / fpp) + 1; xen_pfn_t *p2m = NULL, *p2m_pfns = NULL; uint32_t *pfn_types = NULL; size_t p2msz, p2m_pfnsz, pfn_typesz; assert(max_pfn > old_max); p2msz = (max_pfn + 1) * ctx->x86_pv.width; p2m = realloc(ctx->x86_pv.p2m, p2msz); if ( !p2m ) { ERROR("Failed to (re)alloc %zu bytes for p2m", p2msz); return -1; } ctx->x86_pv.p2m = p2m; pfn_typesz = (max_pfn + 1) * sizeof(*pfn_types); pfn_types = realloc(ctx->x86_pv.restore.pfn_types, pfn_typesz); if ( !pfn_types ) { ERROR("Failed to (re)alloc %zu bytes for pfn_types", pfn_typesz); return -1; } ctx->x86_pv.restore.pfn_types = pfn_types; p2m_pfnsz = (end_frame + 1) * sizeof(*p2m_pfns); p2m_pfns = realloc(ctx->x86_pv.p2m_pfns, p2m_pfnsz); if ( !p2m_pfns ) { ERROR("Failed to (re)alloc %zu bytes for p2m frame list", p2m_pfnsz); return -1; } ctx->x86_pv.p2m_frames = end_frame; ctx->x86_pv.p2m_pfns = p2m_pfns; ctx->x86_pv.max_pfn = max_pfn; for ( i = (old_max ? old_max + 1 : 0); i <= max_pfn; ++i ) { ctx->restore.ops.set_gfn(ctx, i, INVALID_MFN); ctx->restore.ops.set_page_type(ctx, i, 0); } for ( i = (old_end_frame ? old_end_frame + 1 : 0); i <= end_frame; ++i ) ctx->x86_pv.p2m_pfns[i] = INVALID_MFN; DPRINTF("Changed max_pfn from %#lx to %#lx", old_max, max_pfn); return 0; } /* * Pin all of the pagetables. */ static int pin_pagetables(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; unsigned long i, nr_pins; struct mmuext_op pin[MAX_PIN_BATCH]; for ( i = nr_pins = 0; i <= ctx->x86_pv.max_pfn; ++i ) { if ( (ctx->x86_pv.restore.pfn_types[i] & XEN_DOMCTL_PFINFO_LPINTAB) == 0 ) continue; switch ( (ctx->x86_pv.restore.pfn_types[i] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) ) { case XEN_DOMCTL_PFINFO_L1TAB: pin[nr_pins].cmd = MMUEXT_PIN_L1_TABLE; break; case XEN_DOMCTL_PFINFO_L2TAB: pin[nr_pins].cmd = MMUEXT_PIN_L2_TABLE; break; case XEN_DOMCTL_PFINFO_L3TAB: pin[nr_pins].cmd = MMUEXT_PIN_L3_TABLE; break; case XEN_DOMCTL_PFINFO_L4TAB: pin[nr_pins].cmd = MMUEXT_PIN_L4_TABLE; break; default: continue; } pin[nr_pins].arg1.mfn = pfn_to_mfn(ctx, i); nr_pins++; if ( nr_pins == MAX_PIN_BATCH ) { if ( xc_mmuext_op(xch, pin, nr_pins, ctx->domid) != 0 ) { PERROR("Failed to pin batch of pagetables"); return -1; } nr_pins = 0; } } if ( (nr_pins > 0) && (xc_mmuext_op(xch, pin, nr_pins, ctx->domid) < 0) ) { PERROR("Failed to pin batch of pagetables"); return -1; } return 0; } /* * Update details in a guests start_info structure. */ static int process_start_info(struct xc_sr_context *ctx, vcpu_guest_context_any_t *vcpu) { xc_interface *xch = ctx->xch; xen_pfn_t pfn, mfn; start_info_any_t *guest_start_info = NULL; int rc = -1; pfn = GET_FIELD(vcpu, user_regs.edx, ctx->x86_pv.width); if ( pfn > ctx->x86_pv.max_pfn ) { ERROR("Start Info pfn %#lx out of range", pfn); goto err; } else if ( ctx->x86_pv.restore.pfn_types[pfn] != XEN_DOMCTL_PFINFO_NOTAB ) { ERROR("Start Info pfn %#lx has bad type %u", pfn, (ctx->x86_pv.restore.pfn_types[pfn] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT)); goto err; } mfn = pfn_to_mfn(ctx, pfn); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("Start Info has bad mfn"); dump_bad_pseudophysmap_entry(ctx, mfn); goto err; } SET_FIELD(vcpu, user_regs.edx, mfn, ctx->x86_pv.width); guest_start_info = xc_map_foreign_range( xch, ctx->domid, PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); if ( !guest_start_info ) { PERROR("Failed to map Start Info at mfn %#lx", mfn); goto err; } /* Deal with xenstore stuff */ pfn = GET_FIELD(guest_start_info, store_mfn, ctx->x86_pv.width); if ( pfn > ctx->x86_pv.max_pfn ) { ERROR("XenStore pfn %#lx out of range", pfn); goto err; } mfn = pfn_to_mfn(ctx, pfn); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("XenStore pfn has bad mfn"); dump_bad_pseudophysmap_entry(ctx, mfn); goto err; } ctx->restore.xenstore_gfn = mfn; SET_FIELD(guest_start_info, store_mfn, mfn, ctx->x86_pv.width); SET_FIELD(guest_start_info, store_evtchn, ctx->restore.xenstore_evtchn, ctx->x86_pv.width); /* Deal with console stuff */ pfn = GET_FIELD(guest_start_info, console.domU.mfn, ctx->x86_pv.width); if ( pfn > ctx->x86_pv.max_pfn ) { ERROR("Console pfn %#lx out of range", pfn); goto err; } mfn = pfn_to_mfn(ctx, pfn); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("Console pfn has bad mfn"); dump_bad_pseudophysmap_entry(ctx, mfn); goto err; } ctx->restore.console_gfn = mfn; SET_FIELD(guest_start_info, console.domU.mfn, mfn, ctx->x86_pv.width); SET_FIELD(guest_start_info, console.domU.evtchn, ctx->restore.console_evtchn, ctx->x86_pv.width); /* Set other information */ SET_FIELD(guest_start_info, nr_pages, ctx->x86_pv.max_pfn + 1, ctx->x86_pv.width); SET_FIELD(guest_start_info, shared_info, ctx->dominfo.shared_info_frame << PAGE_SHIFT, ctx->x86_pv.width); SET_FIELD(guest_start_info, flags, 0, ctx->x86_pv.width); rc = 0; err: if ( guest_start_info ) munmap(guest_start_info, PAGE_SIZE); return rc; } /* * Process one stashed vcpu worth of basic state and send to Xen. */ static int process_vcpu_basic(struct xc_sr_context *ctx, unsigned int vcpuid) { xc_interface *xch = ctx->xch; vcpu_guest_context_any_t vcpu; xen_pfn_t pfn, mfn; unsigned i, gdt_count; int rc = -1; memcpy(&vcpu, ctx->x86_pv.restore.vcpus[vcpuid].basic, ctx->x86_pv.restore.vcpus[vcpuid].basicsz); /* Vcpu 0 is special: Convert the suspend record to an mfn. */ if ( vcpuid == 0 ) { rc = process_start_info(ctx, &vcpu); if ( rc ) return rc; rc = -1; } SET_FIELD(&vcpu, flags, GET_FIELD(&vcpu, flags, ctx->x86_pv.width) | VGCF_online, ctx->x86_pv.width); gdt_count = GET_FIELD(&vcpu, gdt_ents, ctx->x86_pv.width); if ( gdt_count > FIRST_RESERVED_GDT_ENTRY ) { ERROR("GDT entry count (%u) out of range (max %u)", gdt_count, FIRST_RESERVED_GDT_ENTRY); errno = ERANGE; goto err; } gdt_count = (gdt_count + 511) / 512; /* gdt_count now in units of frames. */ /* Convert GDT frames to mfns. */ for ( i = 0; i < gdt_count; ++i ) { pfn = GET_FIELD(&vcpu, gdt_frames[i], ctx->x86_pv.width); if ( pfn > ctx->x86_pv.max_pfn ) { ERROR("GDT frame %u (pfn %#lx) out of range", i, pfn); goto err; } else if ( (ctx->x86_pv.restore.pfn_types[pfn] != XEN_DOMCTL_PFINFO_NOTAB) ) { ERROR("GDT frame %u (pfn %#lx) has bad type %u", i, pfn, (ctx->x86_pv.restore.pfn_types[pfn] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT)); goto err; } mfn = pfn_to_mfn(ctx, pfn); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("GDT frame %u has bad mfn", i); dump_bad_pseudophysmap_entry(ctx, mfn); goto err; } SET_FIELD(&vcpu, gdt_frames[i], mfn, ctx->x86_pv.width); } /* Convert CR3 to an mfn. */ pfn = cr3_to_mfn(ctx, GET_FIELD(&vcpu, ctrlreg[3], ctx->x86_pv.width)); if ( pfn > ctx->x86_pv.max_pfn ) { ERROR("cr3 (pfn %#lx) out of range", pfn); goto err; } else if ( (ctx->x86_pv.restore.pfn_types[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) != (((xen_pfn_t)ctx->x86_pv.levels) << XEN_DOMCTL_PFINFO_LTAB_SHIFT) ) { ERROR("cr3 (pfn %#lx) has bad type %u, expected %u", pfn, (ctx->x86_pv.restore.pfn_types[pfn] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT), ctx->x86_pv.levels); goto err; } mfn = pfn_to_mfn(ctx, pfn); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("cr3 has bad mfn"); dump_bad_pseudophysmap_entry(ctx, mfn); goto err; } SET_FIELD(&vcpu, ctrlreg[3], mfn_to_cr3(ctx, mfn), ctx->x86_pv.width); /* 64bit guests: Convert CR1 (guest pagetables) to mfn. */ if ( ctx->x86_pv.levels == 4 && (vcpu.x64.ctrlreg[1] & 1) ) { pfn = vcpu.x64.ctrlreg[1] >> PAGE_SHIFT; if ( pfn > ctx->x86_pv.max_pfn ) { ERROR("cr1 (pfn %#lx) out of range", pfn); goto err; } else if ( (ctx->x86_pv.restore.pfn_types[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) != (((xen_pfn_t)ctx->x86_pv.levels) << XEN_DOMCTL_PFINFO_LTAB_SHIFT) ) { ERROR("cr1 (pfn %#lx) has bad type %u, expected %u", pfn, (ctx->x86_pv.restore.pfn_types[pfn] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT), ctx->x86_pv.levels); goto err; } mfn = pfn_to_mfn(ctx, pfn); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("cr1 has bad mfn"); dump_bad_pseudophysmap_entry(ctx, mfn); goto err; } vcpu.x64.ctrlreg[1] = (uint64_t)mfn << PAGE_SHIFT; } if ( xc_vcpu_setcontext(xch, ctx->domid, vcpuid, &vcpu) ) { PERROR("Failed to set vcpu%u's basic info", vcpuid); goto err; } rc = 0; err: return rc; } /* * Process one stashed vcpu worth of extended state and send to Xen. */ static int process_vcpu_extended(struct xc_sr_context *ctx, unsigned int vcpuid) { xc_interface *xch = ctx->xch; struct xc_sr_x86_pv_restore_vcpu *vcpu = &ctx->x86_pv.restore.vcpus[vcpuid]; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_set_ext_vcpucontext; domctl.domain = ctx->domid; memcpy(&domctl.u.ext_vcpucontext, vcpu->extd, vcpu->extdsz); if ( xc_domctl(xch, &domctl) != 0 ) { PERROR("Failed to set vcpu%u's extended info", vcpuid); return -1; } return 0; } /* * Process one stashed vcpu worth of xsave state and send to Xen. */ static int process_vcpu_xsave(struct xc_sr_context *ctx, unsigned int vcpuid) { xc_interface *xch = ctx->xch; struct xc_sr_x86_pv_restore_vcpu *vcpu = &ctx->x86_pv.restore.vcpus[vcpuid]; int rc; DECLARE_DOMCTL; DECLARE_HYPERCALL_BUFFER(void, buffer); buffer = xc_hypercall_buffer_alloc(xch, buffer, vcpu->xsavesz); if ( !buffer ) { ERROR("Unable to allocate %zu bytes for xsave hypercall buffer", vcpu->xsavesz); return -1; } domctl.cmd = XEN_DOMCTL_setvcpuextstate; domctl.domain = ctx->domid; domctl.u.vcpuextstate.vcpu = vcpuid; domctl.u.vcpuextstate.size = vcpu->xsavesz; set_xen_guest_handle(domctl.u.vcpuextstate.buffer, buffer); memcpy(buffer, vcpu->xsave, vcpu->xsavesz); rc = xc_domctl(xch, &domctl); if ( rc ) PERROR("Failed to set vcpu%u's xsave info", vcpuid); xc_hypercall_buffer_free(xch, buffer); return rc; } /* * Process one stashed vcpu worth of msr state and send to Xen. */ static int process_vcpu_msrs(struct xc_sr_context *ctx, unsigned int vcpuid) { xc_interface *xch = ctx->xch; struct xc_sr_x86_pv_restore_vcpu *vcpu = &ctx->x86_pv.restore.vcpus[vcpuid]; int rc; DECLARE_DOMCTL; DECLARE_HYPERCALL_BUFFER(void, buffer); buffer = xc_hypercall_buffer_alloc(xch, buffer, vcpu->msrsz); if ( !buffer ) { ERROR("Unable to allocate %zu bytes for msr hypercall buffer", vcpu->msrsz); return -1; } domctl.cmd = XEN_DOMCTL_set_vcpu_msrs; domctl.domain = ctx->domid; domctl.u.vcpu_msrs.vcpu = vcpuid; domctl.u.vcpu_msrs.msr_count = vcpu->msrsz / sizeof(xen_domctl_vcpu_msr_t); set_xen_guest_handle(domctl.u.vcpu_msrs.msrs, buffer); memcpy(buffer, vcpu->msr, vcpu->msrsz); rc = xc_domctl(xch, &domctl); if ( rc ) PERROR("Failed to set vcpu%u's msrs", vcpuid); xc_hypercall_buffer_free(xch, buffer); return rc; } /* * Process all stashed vcpu context and send to Xen. */ static int update_vcpu_context(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; struct xc_sr_x86_pv_restore_vcpu *vcpu; unsigned i; int rc = 0; for ( i = 0; i < ctx->x86_pv.restore.nr_vcpus; ++i ) { vcpu = &ctx->x86_pv.restore.vcpus[i]; if ( vcpu->basic ) { rc = process_vcpu_basic(ctx, i); if ( rc ) return rc; } else if ( i == 0 ) { ERROR("Sender didn't send vcpu0's basic state"); return -1; } if ( vcpu->extd ) { rc = process_vcpu_extended(ctx, i); if ( rc ) return rc; } if ( vcpu->xsave ) { rc = process_vcpu_xsave(ctx, i); if ( rc ) return rc; } if ( vcpu->msr ) { rc = process_vcpu_msrs(ctx, i); if ( rc ) return rc; } } return rc; } /* * Copy the p2m which has been constructed locally as memory has been * allocated, over the p2m in guest, so the guest can find its memory again on * resume. */ static int update_guest_p2m(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; xen_pfn_t mfn, pfn, *guest_p2m = NULL; unsigned i; int rc = -1; for ( i = 0; i < ctx->x86_pv.p2m_frames; ++i ) { pfn = ctx->x86_pv.p2m_pfns[i]; if ( pfn > ctx->x86_pv.max_pfn ) { ERROR("pfn (%#lx) for p2m_frame_list[%u] out of range", pfn, i); goto err; } else if ( (ctx->x86_pv.restore.pfn_types[pfn] != XEN_DOMCTL_PFINFO_NOTAB) ) { ERROR("pfn (%#lx) for p2m_frame_list[%u] has bad type %u", pfn, i, (ctx->x86_pv.restore.pfn_types[pfn] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT)); goto err; } mfn = pfn_to_mfn(ctx, pfn); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("p2m_frame_list[%u] has bad mfn", i); dump_bad_pseudophysmap_entry(ctx, mfn); goto err; } ctx->x86_pv.p2m_pfns[i] = mfn; } guest_p2m = xc_map_foreign_pages(xch, ctx->domid, PROT_WRITE, ctx->x86_pv.p2m_pfns, ctx->x86_pv.p2m_frames ); if ( !guest_p2m ) { PERROR("Failed to map p2m frames"); goto err; } memcpy(guest_p2m, ctx->x86_pv.p2m, (ctx->x86_pv.max_pfn + 1) * ctx->x86_pv.width); rc = 0; err: if ( guest_p2m ) munmap(guest_p2m, ctx->x86_pv.p2m_frames * PAGE_SIZE); return rc; } /* * Process an X86_PV_INFO record. */ static int handle_x86_pv_info(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; struct xc_sr_rec_x86_pv_info *info = rec->data; if ( ctx->x86_pv.restore.seen_pv_info ) { ERROR("Already received X86_PV_INFO record"); return -1; } if ( rec->length < sizeof(*info) ) { ERROR("X86_PV_INFO record truncated: length %u, expected %zu", rec->length, sizeof(*info)); return -1; } else if ( info->guest_width != 4 && info->guest_width != 8 ) { ERROR("Unexpected guest width %u, Expected 4 or 8", info->guest_width); return -1; } else if ( info->guest_width != ctx->x86_pv.width ) { int rc; struct xen_domctl domctl; /* Try to set address size, domain is always created 64 bit. */ memset(&domctl, 0, sizeof(domctl)); domctl.domain = ctx->domid; domctl.cmd = XEN_DOMCTL_set_address_size; domctl.u.address_size.size = info->guest_width * 8; rc = do_domctl(xch, &domctl); if ( rc != 0 ) { ERROR("Width of guest in stream (%u" " bits) differs with existing domain (%u bits)", info->guest_width * 8, ctx->x86_pv.width * 8); return -1; } /* Domain's information changed, better to refresh. */ rc = x86_pv_domain_info(ctx); if ( rc != 0 ) { ERROR("Unable to refresh guest information"); return -1; } } else if ( info->pt_levels != 3 && info->pt_levels != 4 ) { ERROR("Unexpected guest levels %u, Expected 3 or 4", info->pt_levels); return -1; } else if ( info->pt_levels != ctx->x86_pv.levels ) { ERROR("Levels of guest in stream (%u" ") differs with existing domain (%u)", info->pt_levels, ctx->x86_pv.levels); return -1; } ctx->x86_pv.restore.seen_pv_info = true; return 0; } /* * Process an X86_PV_P2M_FRAMES record. Takes care of expanding the local p2m * state if needed. */ static int handle_x86_pv_p2m_frames(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; struct xc_sr_rec_x86_pv_p2m_frames *data = rec->data; unsigned start, end, x, fpp = PAGE_SIZE / ctx->x86_pv.width; int rc; if ( !ctx->x86_pv.restore.seen_pv_info ) { ERROR("Not yet received X86_PV_INFO record"); return -1; } if ( rec->length < sizeof(*data) ) { ERROR("X86_PV_P2M_FRAMES record truncated: length %u, min %zu", rec->length, sizeof(*data) + sizeof(uint64_t)); return -1; } else if ( data->start_pfn > data->end_pfn ) { ERROR("End pfn in stream (%#x) exceeds Start (%#x)", data->end_pfn, data->start_pfn); return -1; } start = data->start_pfn / fpp; end = data->end_pfn / fpp + 1; if ( rec->length != sizeof(*data) + ((end - start) * sizeof(uint64_t)) ) { ERROR("X86_PV_P2M_FRAMES record wrong size: start_pfn %#x" ", end_pfn %#x, length %u, expected %zu + (%u - %u) * %zu", data->start_pfn, data->end_pfn, rec->length, sizeof(*data), end, start, sizeof(uint64_t)); return -1; } if ( data->end_pfn > ctx->x86_pv.max_pfn ) { rc = expand_p2m(ctx, data->end_pfn); if ( rc ) return rc; } for ( x = 0; x < (end - start); ++x ) ctx->x86_pv.p2m_pfns[start + x] = data->p2m_pfns[x]; return 0; } /* * Processes X86_PV_VCPU_{BASIC,EXTENDED,XSAVE,MSRS} records from the stream. * The blobs are all stashed to one side as they need to be deferred until the * very end of the stream, rather than being send to Xen at the point they * arrive in the stream. It performs all pre-hypercall size validation. */ static int handle_x86_pv_vcpu_blob(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; struct xc_sr_rec_x86_pv_vcpu_hdr *vhdr = rec->data; struct xc_sr_x86_pv_restore_vcpu *vcpu; const char *rec_name; size_t blobsz; void *blob; int rc = -1; switch ( rec->type ) { case REC_TYPE_X86_PV_VCPU_BASIC: rec_name = "X86_PV_VCPU_BASIC"; break; case REC_TYPE_X86_PV_VCPU_EXTENDED: rec_name = "X86_PV_VCPU_EXTENDED"; break; case REC_TYPE_X86_PV_VCPU_XSAVE: rec_name = "X86_PV_VCPU_XSAVE"; break; case REC_TYPE_X86_PV_VCPU_MSRS: rec_name = "X86_PV_VCPU_MSRS"; break; default: ERROR("Unrecognised vcpu blob record %s (%u)", rec_type_to_str(rec->type), rec->type); goto out; } /* Confirm that there is a complete header. */ if ( rec->length < sizeof(*vhdr) ) { ERROR("%s record truncated: length %u, header size %zu", rec_name, rec->length, sizeof(*vhdr)); goto out; } blobsz = rec->length - sizeof(*vhdr); /* * Tolerate empty records. Older sending sides used to accidentally * generate them. */ if ( blobsz == 0 ) { DBGPRINTF("Skipping empty %s record for vcpu %u\n", rec_type_to_str(rec->type), vhdr->vcpu_id); goto out; } /* Check that the vcpu id is within range. */ if ( vhdr->vcpu_id >= ctx->x86_pv.restore.nr_vcpus ) { ERROR("%s record vcpu_id (%u) exceeds domain max (%u)", rec_name, vhdr->vcpu_id, ctx->x86_pv.restore.nr_vcpus - 1); goto out; } vcpu = &ctx->x86_pv.restore.vcpus[vhdr->vcpu_id]; /* Further per-record checks, where possible. */ switch ( rec->type ) { case REC_TYPE_X86_PV_VCPU_BASIC: { size_t vcpusz = ctx->x86_pv.width == 8 ? sizeof(vcpu_guest_context_x86_64_t) : sizeof(vcpu_guest_context_x86_32_t); if ( blobsz != vcpusz ) { ERROR("%s record wrong size: expected %zu, got %u", rec_name, sizeof(*vhdr) + vcpusz, rec->length); goto out; } break; } case REC_TYPE_X86_PV_VCPU_EXTENDED: if ( blobsz > 128 ) { ERROR("%s record too long: max %zu, got %u", rec_name, sizeof(*vhdr) + 128, rec->length); goto out; } break; case REC_TYPE_X86_PV_VCPU_XSAVE: if ( blobsz % sizeof(xen_domctl_vcpu_msr_t) != 0 ) { ERROR("%s record payload size %zu expected to be a multiple of %zu", rec_name, blobsz, sizeof(xen_domctl_vcpu_msr_t)); goto out; } break; } /* Allocate memory. */ blob = malloc(blobsz); if ( !blob ) { ERROR("Unable to allocate %zu bytes for vcpu%u %s blob", blobsz, vhdr->vcpu_id, rec_name); goto out; } memcpy(blob, &vhdr->context, blobsz); /* Stash sideways for later. */ switch ( rec->type ) { #define RECSTORE(x, y) case REC_TYPE_X86_PV_ ## x: \ free(y); (y) = blob; (y ## sz) = blobsz; break RECSTORE(VCPU_BASIC, vcpu->basic); RECSTORE(VCPU_EXTENDED, vcpu->extd); RECSTORE(VCPU_XSAVE, vcpu->xsave); RECSTORE(VCPU_MSRS, vcpu->msr); #undef RECSTORE } rc = 0; out: return rc; } /* * Process a SHARED_INFO record from the stream. */ static int handle_shared_info(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; unsigned i; int rc = -1; shared_info_any_t *guest_shinfo = NULL; const shared_info_any_t *old_shinfo = rec->data; if ( !ctx->x86_pv.restore.seen_pv_info ) { ERROR("Not yet received X86_PV_INFO record"); return -1; } if ( rec->length != PAGE_SIZE ) { ERROR("X86_PV_SHARED_INFO record wrong size: length %u" ", expected 4096", rec->length); goto err; } guest_shinfo = xc_map_foreign_range( xch, ctx->domid, PAGE_SIZE, PROT_READ | PROT_WRITE, ctx->dominfo.shared_info_frame); if ( !guest_shinfo ) { PERROR("Failed to map Shared Info at mfn %#lx", ctx->dominfo.shared_info_frame); goto err; } MEMCPY_FIELD(guest_shinfo, old_shinfo, vcpu_info, ctx->x86_pv.width); MEMCPY_FIELD(guest_shinfo, old_shinfo, arch, ctx->x86_pv.width); SET_FIELD(guest_shinfo, arch.pfn_to_mfn_frame_list_list, 0, ctx->x86_pv.width); MEMSET_ARRAY_FIELD(guest_shinfo, evtchn_pending, 0, ctx->x86_pv.width); for ( i = 0; i < XEN_LEGACY_MAX_VCPUS; i++ ) SET_FIELD(guest_shinfo, vcpu_info[i].evtchn_pending_sel, 0, ctx->x86_pv.width); MEMSET_ARRAY_FIELD(guest_shinfo, evtchn_mask, 0xff, ctx->x86_pv.width); rc = 0; err: if ( guest_shinfo ) munmap(guest_shinfo, PAGE_SIZE); return rc; } /* restore_ops function. */ static bool x86_pv_pfn_is_valid(const struct xc_sr_context *ctx, xen_pfn_t pfn) { return pfn <= ctx->x86_pv.max_pfn; } /* restore_ops function. */ static void x86_pv_set_page_type(struct xc_sr_context *ctx, xen_pfn_t pfn, unsigned long type) { assert(pfn <= ctx->x86_pv.max_pfn); ctx->x86_pv.restore.pfn_types[pfn] = type; } /* restore_ops function. */ static void x86_pv_set_gfn(struct xc_sr_context *ctx, xen_pfn_t pfn, xen_pfn_t mfn) { assert(pfn <= ctx->x86_pv.max_pfn); if ( ctx->x86_pv.width == sizeof(uint64_t) ) /* 64 bit guest. Need to expand INVALID_MFN for 32 bit toolstacks. */ ((uint64_t *)ctx->x86_pv.p2m)[pfn] = mfn == INVALID_MFN ? ~0ULL : mfn; else /* 32 bit guest. Can truncate INVALID_MFN for 64 bit toolstacks. */ ((uint32_t *)ctx->x86_pv.p2m)[pfn] = mfn; } /* * restore_ops function. Convert pfns back to mfns in pagetables. Possibly * needs to populate new frames if a PTE is found referring to a frame which * hasn't yet been seen from PAGE_DATA records. */ static int x86_pv_localise_page(struct xc_sr_context *ctx, uint32_t type, void *page) { xc_interface *xch = ctx->xch; uint64_t *table = page; uint64_t pte; unsigned i, to_populate; xen_pfn_t pfns[(PAGE_SIZE / sizeof(uint64_t))]; type &= XEN_DOMCTL_PFINFO_LTABTYPE_MASK; /* Only page tables need localisation. */ if ( type < XEN_DOMCTL_PFINFO_L1TAB || type > XEN_DOMCTL_PFINFO_L4TAB ) return 0; /* Check to see whether we need to populate any new frames. */ for ( i = 0, to_populate = 0; i < (PAGE_SIZE / sizeof(uint64_t)); ++i ) { pte = table[i]; if ( pte & _PAGE_PRESENT ) { xen_pfn_t pfn = pte_to_frame(pte); #ifdef __i386__ if ( pfn == INVALID_MFN ) { ERROR("PTE truncation detected. L%u[%u] = %016"PRIx64, type >> XEN_DOMCTL_PFINFO_LTAB_SHIFT, i, pte); errno = E2BIG; return -1; } #endif if ( pfn_to_mfn(ctx, pfn) == INVALID_MFN ) pfns[to_populate++] = pfn; } } if ( to_populate && populate_pfns(ctx, to_populate, pfns, NULL) ) return -1; for ( i = 0; i < (PAGE_SIZE / sizeof(uint64_t)); ++i ) { pte = table[i]; if ( pte & _PAGE_PRESENT ) { xen_pfn_t mfn, pfn; pfn = pte_to_frame(pte); mfn = pfn_to_mfn(ctx, pfn); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("Bad mfn for L%u[%u] - pte %"PRIx64, type >> XEN_DOMCTL_PFINFO_LTAB_SHIFT, i, pte); dump_bad_pseudophysmap_entry(ctx, mfn); errno = ERANGE; return -1; } table[i] = merge_pte(pte, mfn); } } return 0; } /* * restore_ops function. Confirm that the incoming stream matches the type of * domain we are attempting to restore into. */ static int x86_pv_setup(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc; if ( ctx->restore.guest_type != DHDR_TYPE_X86_PV ) { ERROR("Unable to restore %s domain into an x86_pv domain", dhdr_type_to_str(ctx->restore.guest_type)); return -1; } else if ( ctx->restore.guest_page_size != PAGE_SIZE ) { ERROR("Invalid page size %d for x86_pv domains", ctx->restore.guest_page_size); return -1; } rc = x86_pv_domain_info(ctx); if ( rc ) return rc; ctx->x86_pv.restore.nr_vcpus = ctx->dominfo.max_vcpu_id + 1; ctx->x86_pv.restore.vcpus = calloc(sizeof(struct xc_sr_x86_pv_restore_vcpu), ctx->x86_pv.restore.nr_vcpus); if ( !ctx->x86_pv.restore.vcpus ) { errno = ENOMEM; return -1; } rc = x86_pv_map_m2p(ctx); if ( rc ) return rc; return rc; } /* * restore_ops function. */ static int x86_pv_process_record(struct xc_sr_context *ctx, struct xc_sr_record *rec) { switch ( rec->type ) { case REC_TYPE_X86_PV_INFO: return handle_x86_pv_info(ctx, rec); case REC_TYPE_X86_PV_P2M_FRAMES: return handle_x86_pv_p2m_frames(ctx, rec); case REC_TYPE_X86_PV_VCPU_BASIC: case REC_TYPE_X86_PV_VCPU_EXTENDED: case REC_TYPE_X86_PV_VCPU_XSAVE: case REC_TYPE_X86_PV_VCPU_MSRS: return handle_x86_pv_vcpu_blob(ctx, rec); case REC_TYPE_SHARED_INFO: return handle_shared_info(ctx, rec); case REC_TYPE_TSC_INFO: return handle_tsc_info(ctx, rec); default: return RECORD_NOT_PROCESSED; } } /* * restore_ops function. Update the vcpu context in Xen, pin the pagetables, * rewrite the p2m and seed the grant table. */ static int x86_pv_stream_complete(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc; rc = update_vcpu_context(ctx); if ( rc ) return rc; rc = pin_pagetables(ctx); if ( rc ) return rc; rc = update_guest_p2m(ctx); if ( rc ) return rc; rc = xc_dom_gnttab_seed(xch, ctx->domid, ctx->restore.console_gfn, ctx->restore.xenstore_gfn, ctx->restore.console_domid, ctx->restore.xenstore_domid); if ( rc ) { PERROR("Failed to seed grant table"); return rc; } return rc; } /* * restore_ops function. */ static int x86_pv_cleanup(struct xc_sr_context *ctx) { free(ctx->x86_pv.p2m); free(ctx->x86_pv.p2m_pfns); if ( ctx->x86_pv.restore.vcpus ) { unsigned i; for ( i = 0; i < ctx->x86_pv.restore.nr_vcpus; ++i ) { struct xc_sr_x86_pv_restore_vcpu *vcpu = &ctx->x86_pv.restore.vcpus[i]; free(vcpu->basic); free(vcpu->extd); free(vcpu->xsave); free(vcpu->msr); } free(ctx->x86_pv.restore.vcpus); } free(ctx->x86_pv.restore.pfn_types); if ( ctx->x86_pv.m2p ) munmap(ctx->x86_pv.m2p, ctx->x86_pv.nr_m2p_frames * PAGE_SIZE); return 0; } struct xc_sr_restore_ops restore_ops_x86_pv = { .pfn_is_valid = x86_pv_pfn_is_valid, .pfn_to_gfn = pfn_to_mfn, .set_page_type = x86_pv_set_page_type, .set_gfn = x86_pv_set_gfn, .localise_page = x86_pv_localise_page, .setup = x86_pv_setup, .process_record = x86_pv_process_record, .stream_complete = x86_pv_stream_complete, .cleanup = x86_pv_cleanup, }; /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_suspend.c0000664000175000017500000001173113256712137015240 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include #include #include #include "xc_private.h" #include "xenguest.h" #define SUSPEND_LOCK_FILE XEN_RUN_DIR "/suspend-evtchn-%d.lock" /* * locking */ #define ERR(x) do{ \ ERROR("Can't " #x " lock file for suspend event channel %s: %s\n", \ suspend_file, strerror(errno)); \ goto err; \ }while(0) #define SUSPEND_FILE_BUFLEN (sizeof(SUSPEND_LOCK_FILE) + 10) static void get_suspend_file(char buf[], int domid) { snprintf(buf, SUSPEND_FILE_BUFLEN, SUSPEND_LOCK_FILE, domid); } static int lock_suspend_event(xc_interface *xch, int domid, int *lockfd) { int fd = -1, r; char suspend_file[SUSPEND_FILE_BUFLEN]; struct stat ours, theirs; struct flock fl; get_suspend_file(suspend_file, domid); *lockfd = -1; for (;;) { if (fd >= 0) close (fd); fd = open(suspend_file, O_CREAT | O_RDWR, 0600); if (fd < 0) ERR("create"); r = fcntl(fd, F_SETFD, FD_CLOEXEC); if (r) ERR("fcntl F_SETFD FD_CLOEXEC"); memset(&fl, 0, sizeof(fl)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_len = 1; r = fcntl(fd, F_SETLK, &fl); if (r) ERR("fcntl F_SETLK"); r = fstat(fd, &ours); if (r) ERR("fstat"); r = stat(suspend_file, &theirs); if (r) { if (errno == ENOENT) /* try again */ continue; ERR("stat"); } if (ours.st_ino != theirs.st_ino) /* someone else must have removed it while we were locking it */ continue; break; } *lockfd = fd; return 0; err: if (fd >= 0) close(fd); return -1; } static int unlock_suspend_event(xc_interface *xch, int domid, int *lockfd) { int r; char suspend_file[SUSPEND_FILE_BUFLEN]; if (*lockfd < 0) return 0; get_suspend_file(suspend_file, domid); r = unlink(suspend_file); if (r) ERR("unlink"); r = close(*lockfd); *lockfd = -1; if (r) ERR("close"); err: if (*lockfd >= 0) close(*lockfd); return -1; } int xc_await_suspend(xc_interface *xch, xenevtchn_handle *xce, int suspend_evtchn) { int rc; do { rc = xenevtchn_pending(xce); if (rc < 0) { ERROR("error polling suspend notification channel: %d", rc); return -1; } } while (rc != suspend_evtchn); /* harmless for one-off suspend */ if (xenevtchn_unmask(xce, suspend_evtchn) < 0) ERROR("failed to unmask suspend notification channel: %d", rc); return 0; } /* Internal callers are allowed to call this with suspend_evtchn<0 * but *lockfd>0. */ int xc_suspend_evtchn_release(xc_interface *xch, xenevtchn_handle *xce, int domid, int suspend_evtchn, int *lockfd) { if (suspend_evtchn >= 0) xenevtchn_unbind(xce, suspend_evtchn); return unlock_suspend_event(xch, domid, lockfd); } int xc_suspend_evtchn_init_sane(xc_interface *xch, xenevtchn_handle *xce, int domid, int port, int *lockfd) { int rc, suspend_evtchn = -1; if (lock_suspend_event(xch, domid, lockfd)) { errno = EINVAL; goto cleanup; } suspend_evtchn = xenevtchn_bind_interdomain(xce, domid, port); if (suspend_evtchn < 0) { ERROR("failed to bind suspend event channel: %d", suspend_evtchn); goto cleanup; } rc = xc_domain_subscribe_for_suspend(xch, domid, port); if (rc < 0) { ERROR("failed to subscribe to domain: %d", rc); goto cleanup; } return suspend_evtchn; cleanup: xc_suspend_evtchn_release(xch, xce, domid, suspend_evtchn, lockfd); return -1; } int xc_suspend_evtchn_init_exclusive(xc_interface *xch, xenevtchn_handle *xce, int domid, int port, int *lockfd) { int suspend_evtchn; suspend_evtchn = xc_suspend_evtchn_init_sane(xch, xce, domid, port, lockfd); if (suspend_evtchn < 0) return suspend_evtchn; /* event channel is pending immediately after binding */ xc_await_suspend(xch, xce, suspend_evtchn); return suspend_evtchn; } xen-4.9.2/tools/libxc/xg_save_restore.h0000664000175000017500000001277713256712137016304 0ustar smbsmb/* * Definitions and utilities for save / restore. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include #include /* ** We process save/restore/migrate in batches of pages; the below ** determines how many pages we (at maximum) deal with in each batch. */ #define MAX_BATCH_SIZE 1024 /* up to 1024 pages (4MB) at a time */ /* When pinning page tables at the end of restore, we also use batching. */ #define MAX_PIN_BATCH 1024 /* ** Determine various platform information required for save/restore, in ** particular: ** ** - the maximum MFN on this machine, used to compute the size of ** the M2P table; ** ** - the starting virtual address of the the hypervisor; we use this ** to determine which parts of guest address space(s) do and don't ** require canonicalization during save/restore; and ** ** - the number of page-table levels for save/ restore. This should ** be a property of the domain, but for the moment we just read it ** from the hypervisor. ** ** - The width of a guest word (unsigned long), in bytes. ** ** Returns 1 on success, 0 on failure. */ static inline int get_platform_info(xc_interface *xch, uint32_t dom, /* OUT */ unsigned long *max_mfn, /* OUT */ unsigned long *hvirt_start, /* OUT */ unsigned int *pt_levels, /* OUT */ unsigned int *guest_width) { xen_capabilities_info_t xen_caps = ""; xen_platform_parameters_t xen_params; if (xc_version(xch, XENVER_platform_parameters, &xen_params) != 0) return 0; if (xc_version(xch, XENVER_capabilities, &xen_caps) != 0) return 0; if (xc_maximum_ram_page(xch, max_mfn)) return 0; *hvirt_start = xen_params.virt_start; if ( xc_domain_get_guest_width(xch, dom, guest_width) != 0) return 0; /* 64-bit tools will see the 64-bit hvirt_start, but 32-bit guests * will be using the compat one. */ if ( *guest_width < sizeof (unsigned long) ) /* XXX need to fix up a way of extracting this value from Xen if * XXX it becomes variable for domU */ *hvirt_start = 0xf5800000; if (strstr(xen_caps, "xen-3.0-x86_64")) /* Depends on whether it's a compat 32-on-64 guest */ *pt_levels = ( (*guest_width == 8) ? 4 : 3 ); else if (strstr(xen_caps, "xen-3.0-x86_32p")) *pt_levels = 3; else return 0; return 1; } /* ** Save/restore deal with the mfn_to_pfn (M2P) and pfn_to_mfn (P2M) tables. ** The M2P simply holds the corresponding PFN, while the top bit of a P2M ** entry tell us whether or not the the PFN is currently mapped. */ #define PFN_TO_KB(_pfn) ((_pfn) << (PAGE_SHIFT - 10)) /* ** The M2P is made up of some number of 'chunks' of at least 2MB in size. ** The below definitions and utility function(s) deal with mapping the M2P ** regarldess of the underlying machine memory size or architecture. */ #define M2P_SHIFT L2_PAGETABLE_SHIFT_PAE #define M2P_CHUNK_SIZE (1 << M2P_SHIFT) #define M2P_SIZE(_m) ROUNDUP(((_m) * sizeof(xen_pfn_t)), M2P_SHIFT) #define M2P_CHUNKS(_m) (M2P_SIZE((_m)) >> M2P_SHIFT) /* Returns TRUE if the PFN is currently mapped */ #define is_mapped(pfn_type) (!((pfn_type) & 0x80000000UL)) #define GET_FIELD(_p, _f, _w) (((_w) == 8) ? ((_p)->x64._f) : ((_p)->x32._f)) #define SET_FIELD(_p, _f, _v, _w) do { \ if ((_w) == 8) \ (_p)->x64._f = (_v); \ else \ (_p)->x32._f = (_v); \ } while (0) #define UNFOLD_CR3(_c) \ ((uint64_t)((dinfo->guest_width == 8) \ ? ((_c) >> 12) \ : (((uint32_t)(_c) >> 12) | ((uint32_t)(_c) << 20)))) #define FOLD_CR3(_c) \ ((uint64_t)((dinfo->guest_width == 8) \ ? ((uint64_t)(_c)) << 12 \ : (((uint32_t)(_c) << 12) | ((uint32_t)(_c) >> 20)))) #define MEMCPY_FIELD(_d, _s, _f, _w) do { \ if ((_w) == 8) \ memcpy(&(_d)->x64._f, &(_s)->x64._f,sizeof((_d)->x64._f)); \ else \ memcpy(&(_d)->x32._f, &(_s)->x32._f,sizeof((_d)->x32._f)); \ } while (0) #define MEMSET_ARRAY_FIELD(_p, _f, _v, _w) do { \ if ((_w) == 8) \ memset(&(_p)->x64._f[0], (_v), sizeof((_p)->x64._f)); \ else \ memset(&(_p)->x32._f[0], (_v), sizeof((_p)->x32._f)); \ } while (0) xen-4.9.2/tools/libxc/xc_sr_common.h0000664000175000017500000003167713256712137015573 0ustar smbsmb#ifndef __COMMON__H #define __COMMON__H #include #include "xg_private.h" #include "xg_save_restore.h" #include "xc_dom.h" #include "xc_bitops.h" #include "xc_sr_stream_format.h" /* String representation of Domain Header types. */ const char *dhdr_type_to_str(uint32_t type); /* String representation of Record types. */ const char *rec_type_to_str(uint32_t type); struct xc_sr_context; struct xc_sr_record; /** * Save operations. To be implemented for each type of guest, for use by the * common save algorithm. * * Every function must be implemented, even if only with a no-op stub. */ struct xc_sr_save_ops { /* Convert a PFN to GFN. May return ~0UL for an invalid mapping. */ xen_pfn_t (*pfn_to_gfn)(const struct xc_sr_context *ctx, xen_pfn_t pfn); /** * Optionally transform the contents of a page from being specific to the * sending environment, to being generic for the stream. * * The page of data at the end of 'page' may be a read-only mapping of a * running guest; it must not be modified. If no transformation is * required, the callee should leave '*pages' untouched. * * If a transformation is required, the callee should allocate themselves * a local page using malloc() and return it via '*page'. * * The caller shall free() '*page' in all cases. In the case that the * callee encounters an error, it should *NOT* free() the memory it * allocated for '*page'. * * It is valid to fail with EAGAIN if the transformation is not able to be * completed at this point. The page shall be retried later. * * @returns 0 for success, -1 for failure, with errno appropriately set. */ int (*normalise_page)(struct xc_sr_context *ctx, xen_pfn_t type, void **page); /** * Set up local environment to save a domain. (Typically querying * running domain state, setting up mappings etc.) * * This is called once before any common setup has occurred, allowing for * guest-specific adjustments to be made to common state. */ int (*setup)(struct xc_sr_context *ctx); /** * Send records which need to be at the start of the stream. This is * called once, after the Image and Domain headers are written. */ int (*start_of_stream)(struct xc_sr_context *ctx); /** * Send records which need to be at the start of a checkpoint. This is * called once, or once per checkpoint in a checkpointed stream, and is * ahead of memory data. */ int (*start_of_checkpoint)(struct xc_sr_context *ctx); /** * Send records which need to be at the end of the checkpoint. This is * called once, or once per checkpoint in a checkpointed stream, and is * after the memory data. */ int (*end_of_checkpoint)(struct xc_sr_context *ctx); /** * Check state of guest to decide whether it makes sense to continue * migration. This is called in each iteration or checkpoint to check * whether all criteria for the migration are still met. If that's not * the case either migration is cancelled via a bad rc or the situation * is handled, e.g. by sending appropriate records. */ int (*check_vm_state)(struct xc_sr_context *ctx); /** * Clean up the local environment. Will be called exactly once, either * after a successful save, or upon encountering an error. */ int (*cleanup)(struct xc_sr_context *ctx); }; /** * Restore operations. To be implemented for each type of guest, for use by * the common restore algorithm. * * Every function must be implemented, even if only with a no-op stub. */ struct xc_sr_restore_ops { /* Convert a PFN to GFN. May return ~0UL for an invalid mapping. */ xen_pfn_t (*pfn_to_gfn)(const struct xc_sr_context *ctx, xen_pfn_t pfn); /* Check to see whether a PFN is valid. */ bool (*pfn_is_valid)(const struct xc_sr_context *ctx, xen_pfn_t pfn); /* Set the GFN of a PFN. */ void (*set_gfn)(struct xc_sr_context *ctx, xen_pfn_t pfn, xen_pfn_t gfn); /* Set the type of a PFN. */ void (*set_page_type)(struct xc_sr_context *ctx, xen_pfn_t pfn, xen_pfn_t type); /** * Optionally transform the contents of a page from being generic in the * stream, to being specific to the restoring environment. * * 'page' is expected to be modified in-place if a transformation is * required. * * @returns 0 for success, -1 for failure, with errno appropriately set. */ int (*localise_page)(struct xc_sr_context *ctx, uint32_t type, void *page); /** * Set up local environment to restore a domain. * * This is called once before any common setup has occurred, allowing for * guest-specific adjustments to be made to common state. */ int (*setup)(struct xc_sr_context *ctx); /** * Process an individual record from the stream. The caller shall take * care of processing common records (e.g. END, PAGE_DATA). * * @return 0 for success, -1 for failure, or the following sentinels: * - RECORD_NOT_PROCESSED * - BROKEN_CHANNEL: under Remus/COLO, this means master may be dead, and * a failover is needed. */ #define RECORD_NOT_PROCESSED 1 #define BROKEN_CHANNEL 2 int (*process_record)(struct xc_sr_context *ctx, struct xc_sr_record *rec); /** * Perform any actions required after the stream has been finished. Called * after the END record has been received. */ int (*stream_complete)(struct xc_sr_context *ctx); /** * Clean up the local environment. Will be called exactly once, either * after a successful restore, or upon encountering an error. */ int (*cleanup)(struct xc_sr_context *ctx); }; /* x86 PV per-vcpu storage structure for blobs heading Xen-wards. */ struct xc_sr_x86_pv_restore_vcpu { void *basic, *extd, *xsave, *msr; size_t basicsz, extdsz, xsavesz, msrsz; }; struct xc_sr_context { xc_interface *xch; uint32_t domid; int fd; xc_dominfo_t dominfo; union /* Common save or restore data. */ { struct /* Save data. */ { int recv_fd; struct xc_sr_save_ops ops; struct save_callbacks *callbacks; /* Live migrate vs non live suspend. */ bool live; /* Plain VM, or checkpoints over time. */ int checkpointed; /* Further debugging information in the stream. */ bool debug; /* Parameters for tweaking live migration. */ unsigned max_iterations; unsigned dirty_threshold; unsigned long p2m_size; xen_pfn_t *batch_pfns; unsigned nr_batch_pfns; unsigned long *deferred_pages; unsigned long nr_deferred_pages; xc_hypercall_buffer_t dirty_bitmap_hbuf; } save; struct /* Restore data. */ { struct xc_sr_restore_ops ops; struct restore_callbacks *callbacks; int send_back_fd; unsigned long p2m_size; xc_hypercall_buffer_t dirty_bitmap_hbuf; /* From Image Header. */ uint32_t format_version; /* From Domain Header. */ uint32_t guest_type; uint32_t guest_page_size; /* Plain VM, or checkpoints over time. */ int checkpointed; /* Currently buffering records between a checkpoint */ bool buffer_all_records; /* * With Remus/COLO, we buffer the records sent by the primary at checkpoint, * in case the primary will fail, we can recover from the last * checkpoint state. * This should be enough for most of the cases because primary only send * dirty pages at checkpoint. */ #define DEFAULT_BUF_RECORDS 1024 struct xc_sr_record *buffered_records; unsigned allocated_rec_num; unsigned buffered_rec_num; /* * Xenstore and Console parameters. * INPUT: evtchn & domid * OUTPUT: gfn */ xen_pfn_t xenstore_gfn, console_gfn; unsigned int xenstore_evtchn, console_evtchn; domid_t xenstore_domid, console_domid; /* Bitmap of currently populated PFNs during restore. */ unsigned long *populated_pfns; xen_pfn_t max_populated_pfn; /* Sender has invoked verify mode on the stream. */ bool verify; } restore; }; union /* Guest-arch specific data. */ { struct /* x86 PV guest. */ { /* 4 or 8; 32 or 64 bit domain */ unsigned int width; /* 3 or 4 pagetable levels */ unsigned int levels; /* Maximum Xen frame */ xen_pfn_t max_mfn; /* Read-only machine to phys map */ xen_pfn_t *m2p; /* first mfn of the compat m2p (Only needed for 32bit PV guests) */ xen_pfn_t compat_m2p_mfn0; /* Number of m2p frames mapped */ unsigned long nr_m2p_frames; /* Maximum guest frame */ xen_pfn_t max_pfn; /* Number of frames making up the p2m */ unsigned int p2m_frames; /* Guest's phys to machine map. Mapped read-only (save) or * allocated locally (restore). Uses guest unsigned longs. */ void *p2m; /* The guest pfns containing the p2m leaves */ xen_pfn_t *p2m_pfns; /* Read-only mapping of guests shared info page */ shared_info_any_t *shinfo; /* p2m generation count for verifying validity of local p2m. */ uint64_t p2m_generation; union { struct { /* State machine for the order of received records. */ bool seen_pv_info; /* Types for each page (bounded by max_pfn). */ uint32_t *pfn_types; /* Vcpu context blobs. */ struct xc_sr_x86_pv_restore_vcpu *vcpus; unsigned nr_vcpus; } restore; }; } x86_pv; struct /* x86 HVM guest. */ { union { struct { /* Whether qemu enabled logdirty mode, and we should * disable on cleanup. */ bool qemu_enabled_logdirty; } save; struct { /* HVM context blob. */ void *context; size_t contextsz; } restore; }; } x86_hvm; }; }; extern struct xc_sr_save_ops save_ops_x86_pv; extern struct xc_sr_save_ops save_ops_x86_hvm; extern struct xc_sr_restore_ops restore_ops_x86_pv; extern struct xc_sr_restore_ops restore_ops_x86_hvm; struct xc_sr_record { uint32_t type; uint32_t length; void *data; }; /* * Writes a split record to the stream, applying correct padding where * appropriate. It is common when sending records containing blobs from Xen * that the header and blob data are separate. This function accepts a second * buffer and length, and will merge it with the main record when sending. * * Records with a non-zero length must provide a valid data field; records * with a 0 length shall have their data field ignored. * * Returns 0 on success and non0 on failure. */ int write_split_record(struct xc_sr_context *ctx, struct xc_sr_record *rec, void *buf, size_t sz); /* * Writes a record to the stream, applying correct padding where appropriate. * Records with a non-zero length must provide a valid data field; records * with a 0 length shall have their data field ignored. * * Returns 0 on success and non0 on failure. */ static inline int write_record(struct xc_sr_context *ctx, struct xc_sr_record *rec) { return write_split_record(ctx, rec, NULL, 0); } /* * Reads a record from the stream, and fills in the record structure. * * Returns 0 on success and non-0 on failure. * * On success, the records type and size shall be valid. * - If size is 0, data shall be NULL. * - If size is non-0, data shall be a buffer allocated by malloc() which must * be passed to free() by the caller. * * On failure, the contents of the record structure are undefined. */ int read_record(struct xc_sr_context *ctx, int fd, struct xc_sr_record *rec); /* * This would ideally be private in restore.c, but is needed by * x86_pv_localise_page() if we receive pagetables frames ahead of the * contents of the frames they point at. */ int populate_pfns(struct xc_sr_context *ctx, unsigned count, const xen_pfn_t *original_pfns, const uint32_t *types); #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_binloader.c0000664000175000017500000002674113256712137016364 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Some of the field descriptions were copied from "The Multiboot * Specification", Copyright 1995, 96 Bryan Ford , * Erich Stefan Boleyn Copyright 1999, 2000, 2001, 2002 * Free Software Foundation, Inc. */ /****************************************************************************** * * Loads simple binary images. It's like a .COM file in MS-DOS. No headers are * present. The only requirement is that it must have a xen_bin_image table * somewhere in the first 8192 bytes, starting on a 32-bit aligned address. * Those familiar with the multiboot specification should recognize this, it's * (almost) the same as the multiboot header. * The layout of the xen_bin_image table is: * * Offset Type Name Note * 0 uint32_t magic required * 4 uint32_t flags required * 8 uint32_t checksum required * 12 uint32_t header_addr required * 16 uint32_t load_addr required * 20 uint32_t load_end_addr required * 24 uint32_t bss_end_addr required * 28 uint32_t entry_addr required * * - magic * Magic number identifying the table. For images to be loaded by Xen 3, the * magic value is 0x336ec578 ("xEn3" with the 0x80 bit of the "E" set). * - flags * bit 0: indicates whether the image needs to be loaded on a page boundary * bit 1: reserved, must be 0 (the multiboot spec uses this bit to indicate * that memory info should be passed to the image) * bit 2: reserved, must be 0 (the multiboot spec uses this bit to indicate * that the bootloader should pass video mode info to the image) * bit 16: reserved, must be 1 (the multiboot spec uses this bit to indicate * that the values in the fields header_addr - entry_addr are * valid) * All other bits should be set to 0. * - checksum * When added to "magic" and "flags", the resulting value should be 0. * - header_addr * Contains the virtual address corresponding to the beginning of the * table - the memory location at which the magic value is supposed to be * loaded. This field serves to synchronize the mapping between OS image * offsets and virtual memory addresses. * - load_addr * Contains the virtual address of the beginning of the text segment. The * offset in the OS image file at which to start loading is defined by the * offset at which the table was found, minus (header addr - load addr). * load addr must be less than or equal to header addr. * - load_end_addr * Contains the virtual address of the end of the data segment. * (load_end_addr - load_addr) specifies how much data to load. This implies * that the text and data segments must be consecutive in the OS image. If * this field is zero, the domain builder assumes that the text and data * segments occupy the whole OS image file. * - bss_end_addr * Contains the virtual address of the end of the bss segment. The domain * builder initializes this area to zero, and reserves the memory it occupies * to avoid placing boot modules and other data relevant to the loaded image * in that area. If this field is zero, the domain builder assumes that no bss * segment is present. * - entry_addr * The virtual address at which to start execution of the loaded image. * */ #include #include #include "xg_private.h" #include "xc_dom.h" #define round_pgup(_p) (((_p)+(PAGE_SIZE_X86-1))&PAGE_MASK_X86) #define round_pgdown(_p) ((_p)&PAGE_MASK_X86) struct xen_bin_image_table { uint32_t magic; uint32_t flags; uint32_t checksum; uint32_t header_addr; uint32_t load_addr; uint32_t load_end_addr; uint32_t bss_end_addr; uint32_t entry_addr; }; #define XEN_MULTIBOOT_MAGIC3 0x336ec578 #define XEN_MULTIBOOT_FLAG_ALIGN4K 0x00000001 #define XEN_MULTIBOOT_FLAG_NEEDMEMINFO 0x00000002 #define XEN_MULTIBOOT_FLAG_NEEDVIDINFO 0x00000004 #define XEN_MULTIBOOT_FLAG_ADDRSVALID 0x00010000 #define XEN_MULTIBOOT_FLAG_PAE_SHIFT 14 #define XEN_MULTIBOOT_FLAG_PAE_MASK (3 << XEN_MULTIBOOT_FLAG_PAE_SHIFT) /* Flags we test for */ #define FLAGS_MASK ((~ 0) & (~ XEN_MULTIBOOT_FLAG_ALIGN4K) & \ (~ XEN_MULTIBOOT_FLAG_PAE_MASK)) #define FLAGS_REQUIRED XEN_MULTIBOOT_FLAG_ADDRSVALID /* --------------------------------------------------------------------- */ static struct xen_bin_image_table *find_table(struct xc_dom_image *dom) { struct xen_bin_image_table *table; uint32_t *probe_ptr; uint32_t *probe_end; if ( dom->kernel_size < sizeof(*table) ) return NULL; probe_ptr = dom->kernel_blob; if ( dom->kernel_size > (8192 + sizeof(*table)) ) probe_end = dom->kernel_blob + 8192; else probe_end = dom->kernel_blob + dom->kernel_size - sizeof(*table); for ( table = NULL; probe_ptr < probe_end; probe_ptr++ ) { if ( *probe_ptr == XEN_MULTIBOOT_MAGIC3 ) { table = (struct xen_bin_image_table *) probe_ptr; /* Checksum correct? */ if ( (table->magic + table->flags + table->checksum) == 0 ) return table; } } return NULL; } static int xc_dom_probe_bin_kernel(struct xc_dom_image *dom) { return find_table(dom) ? 0 : -EINVAL; } static int xc_dom_parse_bin_kernel(struct xc_dom_image *dom) { struct xen_bin_image_table *image_info; char *image = dom->kernel_blob; size_t image_size = dom->kernel_size; uint32_t start_addr; uint32_t load_end_addr; uint32_t bss_end_addr; uint32_t pae_flags; image_info = find_table(dom); if ( !image_info ) return -EINVAL; DOMPRINTF("%s: multiboot header fields", __FUNCTION__); DOMPRINTF(" flags: 0x%" PRIx32 "", image_info->flags); DOMPRINTF(" header_addr: 0x%" PRIx32 "", image_info->header_addr); DOMPRINTF(" load_addr: 0x%" PRIx32 "", image_info->load_addr); DOMPRINTF(" load_end_addr: 0x%" PRIx32 "", image_info->load_end_addr); DOMPRINTF(" bss_end_addr: 0x%" PRIx32 "", image_info->bss_end_addr); DOMPRINTF(" entry_addr: 0x%" PRIx32 "", image_info->entry_addr); /* Check the flags */ if ( (image_info->flags & FLAGS_MASK) != FLAGS_REQUIRED ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: xen_bin_image_table flags required " "0x%08" PRIx32 " found 0x%08" PRIx32 "", __FUNCTION__, FLAGS_REQUIRED, image_info->flags & FLAGS_MASK); return -EINVAL; } /* Sanity check on the addresses */ if ( (image_info->header_addr < image_info->load_addr) || ((char *) image_info - image) < (image_info->header_addr - image_info->load_addr) ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: Invalid header_addr.", __FUNCTION__); return -EINVAL; } start_addr = image_info->header_addr - ((char *)image_info - image); load_end_addr = image_info->load_end_addr ?: start_addr + image_size; bss_end_addr = image_info->bss_end_addr ?: load_end_addr; DOMPRINTF("%s: calculated addresses", __FUNCTION__); DOMPRINTF(" start_addr: 0x%" PRIx32 "", start_addr); DOMPRINTF(" load_end_addr: 0x%" PRIx32 "", load_end_addr); DOMPRINTF(" bss_end_addr: 0x%" PRIx32 "", bss_end_addr); if ( (start_addr + image_size) < load_end_addr ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: Invalid load_end_addr.", __FUNCTION__); return -EINVAL; } if ( bss_end_addr < load_end_addr) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: Invalid bss_end_addr.", __FUNCTION__); return -EINVAL; } dom->kernel_seg.vstart = image_info->load_addr; dom->kernel_seg.vend = bss_end_addr; dom->parms.virt_base = start_addr; dom->parms.virt_entry = image_info->entry_addr; pae_flags = image_info->flags & XEN_MULTIBOOT_FLAG_PAE_MASK; switch (pae_flags >> XEN_MULTIBOOT_FLAG_PAE_SHIFT) { case 0: dom->guest_type = "xen-3.0-x86_32"; break; case 1: dom->guest_type = "xen-3.0-x86_32p"; break; case 2: dom->guest_type = "xen-3.0-x86_64"; break; case 3: /* Kernel detects PAE at runtime. So try to figure whenever * xen supports PAE and advertise a PAE-capable kernel in case * it does. */ dom->guest_type = "xen-3.0-x86_32"; if ( strstr(dom->xen_caps, "xen-3.0-x86_32p") ) { DOMPRINTF("%s: PAE fixup", __FUNCTION__); dom->guest_type = "xen-3.0-x86_32p"; dom->parms.pae = XEN_PAE_EXTCR3; } break; } return 0; } static int xc_dom_load_bin_kernel(struct xc_dom_image *dom) { struct xen_bin_image_table *image_info; char *image = dom->kernel_blob; char *dest; size_t image_size = dom->kernel_size; size_t dest_size; uint32_t start_addr; uint32_t load_end_addr; uint32_t bss_end_addr; uint32_t skip, text_size, bss_size; image_info = find_table(dom); if ( !image_info ) return -EINVAL; start_addr = image_info->header_addr - ((char *)image_info - image); load_end_addr = image_info->load_end_addr ?: start_addr + image_size; bss_end_addr = image_info->bss_end_addr ?: load_end_addr; /* It's possible that we need to skip the first part of the image */ skip = image_info->load_addr - start_addr; text_size = load_end_addr - image_info->load_addr; bss_size = bss_end_addr - load_end_addr; DOMPRINTF("%s: calculated sizes", __FUNCTION__); DOMPRINTF(" skip: 0x%" PRIx32 "", skip); DOMPRINTF(" text_size: 0x%" PRIx32 "", text_size); DOMPRINTF(" bss_size: 0x%" PRIx32 "", bss_size); dest = xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart, &dest_size); if ( dest == NULL ) { DOMPRINTF("%s: xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart)" " => NULL", __FUNCTION__); return -EINVAL; } if ( dest_size < text_size || dest_size - text_size < bss_size ) { DOMPRINTF("%s: mapped region is too small for image", __FUNCTION__); return -EINVAL; } if ( image_size < skip || image_size - skip < text_size ) { DOMPRINTF("%s: image is too small for declared text size", __FUNCTION__); return -EINVAL; } memcpy(dest, image + skip, text_size); memset(dest + text_size, 0, bss_size); return 0; } /* ------------------------------------------------------------------------ */ static struct xc_dom_loader bin_loader = { .name = "multiboot-binary", .probe = xc_dom_probe_bin_kernel, .parser = xc_dom_parse_bin_kernel, .loader = xc_dom_load_bin_kernel, }; static void __init register_loader(void) { xc_dom_register_loader(&bin_loader); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_decompress_unsafe_lzo1x.c0000664000175000017500000000176413256712137021265 0ustar smbsmb#include #include #include #include #include #include "xg_private.h" #include "xc_dom_decompress_unsafe.h" typedef uint8_t u8; typedef uint32_t u32; typedef uint16_t u16; typedef uint64_t u64; #define likely(a) a #define noinline #define unlikely(a) a static inline u16 be16_to_cpup(const u16 *p) { u16 v = *p; #if BYTE_ORDER == LITTLE_ENDIAN return (((v & 0x00ffU) << 8) | ((v & 0xff00U) >> 8)); #else return v; #endif } static inline u32 be32_to_cpup(const u32 *p) { u32 v = *p; #if BYTE_ORDER == LITTLE_ENDIAN return (((v & 0x000000ffUL) << 24) | ((v & 0x0000ff00UL) << 8) | ((v & 0x00ff0000UL) >> 8) | ((v & 0xff000000UL) >> 24)); #else return v; #endif } #include "../../xen/common/lzo.c" #include "../../xen/common/unlzo.c" int xc_try_lzo1x_decode( struct xc_dom_image *dom, void **blob, size_t *size) { return xc_dom_decompress_unsafe(unlzo, dom, blob, size); } xen-4.9.2/tools/libxc/xc_memshr.c0000664000175000017500000001624213256712137015054 0ustar smbsmb/****************************************************************************** * * xc_memshr.c * * Interface to low-level memory sharing functionality. * * Copyright (c) 2009 Citrix Systems, Inc. (Grzegorz Milos) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include #include int xc_memshr_control(xc_interface *xch, domid_t domid, int enable) { DECLARE_DOMCTL; struct xen_domctl_mem_sharing_op *op; domctl.cmd = XEN_DOMCTL_mem_sharing_op; domctl.interface_version = XEN_DOMCTL_INTERFACE_VERSION; domctl.domain = domid; op = &(domctl.u.mem_sharing_op); op->op = XEN_DOMCTL_MEM_SHARING_CONTROL; op->u.enable = enable; return do_domctl(xch, &domctl); } int xc_memshr_ring_enable(xc_interface *xch, domid_t domid, uint32_t *port) { if ( !port ) { errno = EINVAL; return -1; } return xc_vm_event_control(xch, domid, XEN_VM_EVENT_ENABLE, XEN_DOMCTL_VM_EVENT_OP_SHARING, port); } int xc_memshr_ring_disable(xc_interface *xch, domid_t domid) { return xc_vm_event_control(xch, domid, XEN_VM_EVENT_DISABLE, XEN_DOMCTL_VM_EVENT_OP_SHARING, NULL); } static int xc_memshr_memop(xc_interface *xch, domid_t domid, xen_mem_sharing_op_t *mso) { mso->domain = domid; return do_memory_op(xch, XENMEM_sharing_op, mso, sizeof(*mso)); } int xc_memshr_nominate_gfn(xc_interface *xch, domid_t domid, unsigned long gfn, uint64_t *handle) { int rc; xen_mem_sharing_op_t mso; memset(&mso, 0, sizeof(mso)); mso.op = XENMEM_sharing_op_nominate_gfn; mso.u.nominate.u.gfn = gfn; rc = xc_memshr_memop(xch, domid, &mso); if (!rc) *handle = mso.u.nominate.handle; return rc; } int xc_memshr_nominate_gref(xc_interface *xch, domid_t domid, grant_ref_t gref, uint64_t *handle) { int rc; xen_mem_sharing_op_t mso; memset(&mso, 0, sizeof(mso)); mso.op = XENMEM_sharing_op_nominate_gref; mso.u.nominate.u.grant_ref = gref; rc = xc_memshr_memop(xch, domid, &mso); if (!rc) *handle = mso.u.nominate.handle; return rc; } int xc_memshr_share_gfns(xc_interface *xch, domid_t source_domain, unsigned long source_gfn, uint64_t source_handle, domid_t client_domain, unsigned long client_gfn, uint64_t client_handle) { xen_mem_sharing_op_t mso; memset(&mso, 0, sizeof(mso)); mso.op = XENMEM_sharing_op_share; mso.u.share.source_handle = source_handle; mso.u.share.source_gfn = source_gfn; mso.u.share.client_domain = client_domain; mso.u.share.client_gfn = client_gfn; mso.u.share.client_handle = client_handle; return xc_memshr_memop(xch, source_domain, &mso); } int xc_memshr_share_grefs(xc_interface *xch, domid_t source_domain, grant_ref_t source_gref, uint64_t source_handle, domid_t client_domain, grant_ref_t client_gref, uint64_t client_handle) { xen_mem_sharing_op_t mso; memset(&mso, 0, sizeof(mso)); mso.op = XENMEM_sharing_op_share; mso.u.share.source_handle = source_handle; XENMEM_SHARING_OP_FIELD_MAKE_GREF(mso.u.share.source_gfn, source_gref); mso.u.share.client_domain = client_domain; XENMEM_SHARING_OP_FIELD_MAKE_GREF(mso.u.share.client_gfn, client_gref); mso.u.share.client_handle = client_handle; return xc_memshr_memop(xch, source_domain, &mso); } int xc_memshr_add_to_physmap(xc_interface *xch, domid_t source_domain, unsigned long source_gfn, uint64_t source_handle, domid_t client_domain, unsigned long client_gfn) { xen_mem_sharing_op_t mso; memset(&mso, 0, sizeof(mso)); mso.op = XENMEM_sharing_op_add_physmap; mso.u.share.source_handle = source_handle; mso.u.share.source_gfn = source_gfn; mso.u.share.client_domain = client_domain; mso.u.share.client_gfn = client_gfn; return xc_memshr_memop(xch, source_domain, &mso); } int xc_memshr_range_share(xc_interface *xch, domid_t source_domain, domid_t client_domain, uint64_t first_gfn, uint64_t last_gfn) { xen_mem_sharing_op_t mso; memset(&mso, 0, sizeof(mso)); mso.op = XENMEM_sharing_op_range_share; mso.u.range.client_domain = client_domain; mso.u.range.first_gfn = first_gfn; mso.u.range.last_gfn = last_gfn; return xc_memshr_memop(xch, source_domain, &mso); } int xc_memshr_domain_resume(xc_interface *xch, domid_t domid) { return xc_vm_event_control(xch, domid, XEN_VM_EVENT_RESUME, XEN_DOMCTL_VM_EVENT_OP_SHARING, NULL); } int xc_memshr_debug_gfn(xc_interface *xch, domid_t domid, unsigned long gfn) { xen_mem_sharing_op_t mso; memset(&mso, 0, sizeof(mso)); mso.op = XENMEM_sharing_op_debug_gfn; mso.u.debug.u.gfn = gfn; return xc_memshr_memop(xch, domid, &mso); } int xc_memshr_debug_gref(xc_interface *xch, domid_t domid, grant_ref_t gref) { xen_mem_sharing_op_t mso; memset(&mso, 0, sizeof(mso)); mso.op = XENMEM_sharing_op_debug_gref; mso.u.debug.u.gref = gref; return xc_memshr_memop(xch, domid, &mso); } int xc_memshr_audit(xc_interface *xch) { xen_mem_sharing_op_t mso; memset(&mso, 0, sizeof(mso)); mso.op = XENMEM_sharing_op_audit; return do_memory_op(xch, XENMEM_sharing_op, &mso, sizeof(mso)); } long xc_sharing_freed_pages(xc_interface *xch) { return do_memory_op(xch, XENMEM_get_sharing_freed_pages, NULL, 0); } long xc_sharing_used_frames(xc_interface *xch) { return do_memory_op(xch, XENMEM_get_sharing_shared_pages, NULL, 0); } xen-4.9.2/tools/libxc/xc_dom_boot.c0000664000175000017500000003104013256712137015354 0ustar smbsmb/* * Xen domain builder -- xen booter. * * This is the code which actually boots a fresh * prepared domain image as xen guest domain. * * ==> this is the only domain builder code piece * where xen hypercalls are allowed <== * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * written 2006 by Gerd Hoffmann . * */ #include #include #include #include #include #include "xg_private.h" #include "xc_dom.h" #include "xc_core.h" #include #include /* ------------------------------------------------------------------------ */ static int setup_hypercall_page(struct xc_dom_image *dom) { DECLARE_DOMCTL; xen_pfn_t pfn; int rc; if ( dom->parms.virt_hypercall == -1 ) return 0; pfn = (dom->parms.virt_hypercall - dom->parms.virt_base) >> XC_DOM_PAGE_SHIFT(dom); DOMPRINTF("%s: vaddr=0x%" PRIx64 " pfn=0x%" PRIpfn "", __FUNCTION__, dom->parms.virt_hypercall, pfn); domctl.cmd = XEN_DOMCTL_hypercall_init; domctl.domain = dom->guest_domid; domctl.u.hypercall_init.gmfn = xc_dom_p2m(dom, pfn); rc = do_domctl(dom->xch, &domctl); if ( rc != 0 ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: HYPERCALL_INIT failed: %d - %s)", __FUNCTION__, errno, strerror(errno)); return rc; } static int clear_page(struct xc_dom_image *dom, xen_pfn_t pfn) { xen_pfn_t dst; int rc; if ( pfn == 0 ) return 0; dst = xc_dom_p2m(dom, pfn); DOMPRINTF("%s: pfn 0x%" PRIpfn ", mfn 0x%" PRIpfn "", __FUNCTION__, pfn, dst); rc = xc_clear_domain_page(dom->xch, dom->guest_domid, dst); if ( rc != 0 ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: xc_clear_domain_page failed (pfn 0x%" PRIpfn ", rc=%d)", __FUNCTION__, pfn, rc); return rc; } /* ------------------------------------------------------------------------ */ int xc_dom_compat_check(struct xc_dom_image *dom) { xen_capabilities_info_t xen_caps; char *item, *ptr; int match, found = 0; strncpy(xen_caps, dom->xen_caps, XEN_CAPABILITIES_INFO_LEN - 1); xen_caps[XEN_CAPABILITIES_INFO_LEN - 1] = '\0'; for ( item = strtok_r(xen_caps, " ", &ptr); item != NULL ; item = strtok_r(NULL, " ", &ptr) ) { match = !strcmp(dom->guest_type, item); DOMPRINTF("%s: supported guest type: %s%s", __FUNCTION__, item, match ? " <= matches" : ""); if ( match ) found++; } if ( !found ) xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: guest type %s not supported by xen kernel, sorry", __FUNCTION__, dom->guest_type); return found; } int xc_dom_boot_xen_init(struct xc_dom_image *dom, xc_interface *xch, domid_t domid) { dom->xch = xch; dom->guest_domid = domid; dom->xen_version = xc_version(xch, XENVER_version, NULL); if ( xc_version(xch, XENVER_capabilities, &dom->xen_caps) < 0 ) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "can't get xen capabilities"); return -1; } DOMPRINTF("%s: ver %d.%d, caps %s", __FUNCTION__, dom->xen_version >> 16, dom->xen_version & 0xff, dom->xen_caps); return 0; } int xc_dom_boot_mem_init(struct xc_dom_image *dom) { long rc; DOMPRINTF_CALLED(dom->xch); rc = dom->arch_hooks->meminit(dom); if ( rc != 0 ) { xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY, "%s: can't allocate low memory for domain", __FUNCTION__); return rc; } return 0; } void *xc_dom_boot_domU_map(struct xc_dom_image *dom, xen_pfn_t pfn, xen_pfn_t count) { int page_shift = XC_DOM_PAGE_SHIFT(dom); privcmd_mmap_entry_t *entries; void *ptr; int i; int err; entries = xc_dom_malloc(dom, count * sizeof(privcmd_mmap_entry_t)); if ( entries == NULL ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: failed to mmap domU pages 0x%" PRIpfn "+0x%" PRIpfn " [malloc]", __FUNCTION__, pfn, count); return NULL; } for ( i = 0; i < count; i++ ) entries[i].mfn = xc_dom_p2m(dom, pfn + i); ptr = xc_map_foreign_ranges(dom->xch, dom->guest_domid, count << page_shift, PROT_READ | PROT_WRITE, 1 << page_shift, entries, count); if ( ptr == NULL ) { err = errno; xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: failed to mmap domU pages 0x%" PRIpfn "+0x%" PRIpfn " [mmap, errno=%i (%s)]", __FUNCTION__, pfn, count, err, strerror(err)); return NULL; } return ptr; } int xc_dom_boot_image(struct xc_dom_image *dom) { xc_dominfo_t info; int rc; DOMPRINTF_CALLED(dom->xch); /* misc stuff*/ if ( (rc = dom->arch_hooks->bootearly(dom)) != 0 ) return rc; /* collect some info */ rc = xc_domain_getinfo(dom->xch, dom->guest_domid, 1, &info); if ( rc < 0 ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: getdomaininfo failed (rc=%d)", __FUNCTION__, rc); return rc; } if ( rc == 0 || info.domid != dom->guest_domid ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: Huh? No domains found (nr_domains=%d) " "or domid mismatch (%d != %d)", __FUNCTION__, rc, info.domid, dom->guest_domid); return -1; } dom->shared_info_mfn = info.shared_info_frame; /* sanity checks */ if ( !xc_dom_compat_check(dom) ) return -1; /* initial mm setup */ if ( (rc = xc_dom_update_guest_p2m(dom)) != 0 ) return rc; if ( dom->arch_hooks->setup_pgtables ) if ( (rc = dom->arch_hooks->setup_pgtables(dom)) != 0 ) return rc; if ( (rc = clear_page(dom, dom->console_pfn)) != 0 ) return rc; if ( (rc = clear_page(dom, dom->xenstore_pfn)) != 0 ) return rc; /* start info page */ if ( dom->arch_hooks->start_info ) dom->arch_hooks->start_info(dom); /* hypercall page */ if ( (rc = setup_hypercall_page(dom)) != 0 ) return rc; xc_dom_log_memory_footprint(dom); /* misc x86 stuff */ if ( (rc = dom->arch_hooks->bootlate(dom)) != 0 ) return rc; /* let the vm run */ if ( (rc = dom->arch_hooks->vcpu(dom)) != 0 ) return rc; xc_dom_unmap_all(dom); return rc; } static xen_pfn_t xc_dom_gnttab_setup(xc_interface *xch, domid_t domid) { gnttab_setup_table_t setup; DECLARE_HYPERCALL_BUFFER(xen_pfn_t, gmfnp); int rc; xen_pfn_t gmfn; gmfnp = xc_hypercall_buffer_alloc(xch, gmfnp, sizeof(*gmfnp)); if (gmfnp == NULL) return -1; setup.dom = domid; setup.nr_frames = 1; set_xen_guest_handle(setup.frame_list, gmfnp); setup.status = 0; rc = xc_gnttab_op(xch, GNTTABOP_setup_table, &setup, sizeof(setup), 1); gmfn = *gmfnp; xc_hypercall_buffer_free(xch, gmfnp); if ( rc != 0 || setup.status != GNTST_okay ) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "%s: failed to setup domU grant table " "[errno=%d, status=%" PRId16 "]\n", __FUNCTION__, rc != 0 ? errno : 0, setup.status); return -1; } return gmfn; } int xc_dom_gnttab_seed(xc_interface *xch, domid_t domid, xen_pfn_t console_gmfn, xen_pfn_t xenstore_gmfn, domid_t console_domid, domid_t xenstore_domid) { xen_pfn_t gnttab_gmfn; grant_entry_v1_t *gnttab; gnttab_gmfn = xc_dom_gnttab_setup(xch, domid); if ( gnttab_gmfn == -1 ) return -1; gnttab = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ|PROT_WRITE, gnttab_gmfn); if ( gnttab == NULL ) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "%s: failed to map domU grant table " "[errno=%d]\n", __FUNCTION__, errno); return -1; } if ( domid != console_domid && console_gmfn != -1) { gnttab[GNTTAB_RESERVED_CONSOLE].flags = GTF_permit_access; gnttab[GNTTAB_RESERVED_CONSOLE].domid = console_domid; gnttab[GNTTAB_RESERVED_CONSOLE].frame = console_gmfn; } if ( domid != xenstore_domid && xenstore_gmfn != -1) { gnttab[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access; gnttab[GNTTAB_RESERVED_XENSTORE].domid = xenstore_domid; gnttab[GNTTAB_RESERVED_XENSTORE].frame = xenstore_gmfn; } if ( munmap(gnttab, PAGE_SIZE) == -1 ) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "%s: failed to unmap domU grant table " "[errno=%d]\n", __FUNCTION__, errno); return -1; } /* Guest shouldn't really touch its grant table until it has * enabled its caches. But lets be nice. */ xc_domain_cacheflush(xch, domid, gnttab_gmfn, 1); return 0; } int xc_dom_gnttab_hvm_seed(xc_interface *xch, domid_t domid, xen_pfn_t console_gpfn, xen_pfn_t xenstore_gpfn, domid_t console_domid, domid_t xenstore_domid) { int rc; xen_pfn_t scratch_gpfn; struct xen_add_to_physmap xatp = { .domid = domid, .space = XENMAPSPACE_grant_table, .idx = 0, }; struct xen_remove_from_physmap xrfp = { .domid = domid, }; rc = xc_core_arch_get_scratch_gpfn(xch, domid, &scratch_gpfn); if ( rc < 0 ) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "%s: failed to get a scratch gfn " "[errno=%d]\n", __FUNCTION__, errno); return -1; } xatp.gpfn = scratch_gpfn; xrfp.gpfn = scratch_gpfn; xc_dom_printf(xch, "%s: called, pfn=0x%"PRI_xen_pfn, __FUNCTION__, scratch_gpfn); rc = do_memory_op(xch, XENMEM_add_to_physmap, &xatp, sizeof(xatp)); if ( rc != 0 ) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "%s: failed to add gnttab to physmap " "[errno=%d]\n", __FUNCTION__, errno); return -1; } rc = xc_dom_gnttab_seed(xch, domid, console_gpfn, xenstore_gpfn, console_domid, xenstore_domid); if (rc != 0) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "%s: failed to seed gnttab entries\n", __FUNCTION__); (void) do_memory_op(xch, XENMEM_remove_from_physmap, &xrfp, sizeof(xrfp)); return -1; } rc = do_memory_op(xch, XENMEM_remove_from_physmap, &xrfp, sizeof(xrfp)); if (rc != 0) { xc_dom_panic(xch, XC_INTERNAL_ERROR, "%s: failed to remove gnttab from physmap " "[errno=%d]\n", __FUNCTION__, errno); return -1; } return 0; } int xc_dom_gnttab_init(struct xc_dom_image *dom) { if ( xc_dom_translated(dom) ) { return xc_dom_gnttab_hvm_seed(dom->xch, dom->guest_domid, dom->console_pfn, dom->xenstore_pfn, dom->console_domid, dom->xenstore_domid); } else { return xc_dom_gnttab_seed(dom->xch, dom->guest_domid, xc_dom_p2m(dom, dom->console_pfn), xc_dom_p2m(dom, dom->xenstore_pfn), dom->console_domid, dom->xenstore_domid); } } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_save.c0000664000175000017500000006226513256712137015231 0ustar smbsmb#include #include #include "xc_sr_common.h" /* * Writes an Image header and Domain header into the stream. */ static int write_headers(struct xc_sr_context *ctx, uint16_t guest_type) { xc_interface *xch = ctx->xch; int32_t xen_version = xc_version(xch, XENVER_version, NULL); struct xc_sr_ihdr ihdr = { .marker = IHDR_MARKER, .id = htonl(IHDR_ID), .version = htonl(IHDR_VERSION), .options = htons(IHDR_OPT_LITTLE_ENDIAN), }; struct xc_sr_dhdr dhdr = { .type = guest_type, .page_shift = XC_PAGE_SHIFT, .xen_major = (xen_version >> 16) & 0xffff, .xen_minor = (xen_version) & 0xffff, }; if ( xen_version < 0 ) { PERROR("Unable to obtain Xen Version"); return -1; } if ( write_exact(ctx->fd, &ihdr, sizeof(ihdr)) ) { PERROR("Unable to write Image Header to stream"); return -1; } if ( write_exact(ctx->fd, &dhdr, sizeof(dhdr)) ) { PERROR("Unable to write Domain Header to stream"); return -1; } return 0; } /* * Writes an END record into the stream. */ static int write_end_record(struct xc_sr_context *ctx) { struct xc_sr_record end = { REC_TYPE_END, 0, NULL }; return write_record(ctx, &end); } /* * Writes a CHECKPOINT record into the stream. */ static int write_checkpoint_record(struct xc_sr_context *ctx) { struct xc_sr_record checkpoint = { REC_TYPE_CHECKPOINT, 0, NULL }; return write_record(ctx, &checkpoint); } /* * Writes a batch of memory as a PAGE_DATA record into the stream. The batch * is constructed in ctx->save.batch_pfns. * * This function: * - gets the types for each pfn in the batch. * - for each pfn with real data: * - maps and attempts to localise the pages. * - construct and writes a PAGE_DATA record into the stream. */ static int write_batch(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; xen_pfn_t *mfns = NULL, *types = NULL; void *guest_mapping = NULL; void **guest_data = NULL; void **local_pages = NULL; int *errors = NULL, rc = -1; unsigned i, p, nr_pages = 0, nr_pages_mapped = 0; unsigned nr_pfns = ctx->save.nr_batch_pfns; void *page, *orig_page; uint64_t *rec_pfns = NULL; struct iovec *iov = NULL; int iovcnt = 0; struct xc_sr_rec_page_data_header hdr = { 0 }; struct xc_sr_record rec = { .type = REC_TYPE_PAGE_DATA, }; assert(nr_pfns != 0); /* Mfns of the batch pfns. */ mfns = malloc(nr_pfns * sizeof(*mfns)); /* Types of the batch pfns. */ types = malloc(nr_pfns * sizeof(*types)); /* Errors from attempting to map the gfns. */ errors = malloc(nr_pfns * sizeof(*errors)); /* Pointers to page data to send. Mapped gfns or local allocations. */ guest_data = calloc(nr_pfns, sizeof(*guest_data)); /* Pointers to locally allocated pages. Need freeing. */ local_pages = calloc(nr_pfns, sizeof(*local_pages)); /* iovec[] for writev(). */ iov = malloc((nr_pfns + 4) * sizeof(*iov)); if ( !mfns || !types || !errors || !guest_data || !local_pages || !iov ) { ERROR("Unable to allocate arrays for a batch of %u pages", nr_pfns); goto err; } for ( i = 0; i < nr_pfns; ++i ) { types[i] = mfns[i] = ctx->save.ops.pfn_to_gfn(ctx, ctx->save.batch_pfns[i]); /* Likely a ballooned page. */ if ( mfns[i] == INVALID_MFN ) { set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages); ++ctx->save.nr_deferred_pages; } } rc = xc_get_pfn_type_batch(xch, ctx->domid, nr_pfns, types); if ( rc ) { PERROR("Failed to get types for pfn batch"); goto err; } rc = -1; for ( i = 0; i < nr_pfns; ++i ) { switch ( types[i] ) { case XEN_DOMCTL_PFINFO_BROKEN: case XEN_DOMCTL_PFINFO_XALLOC: case XEN_DOMCTL_PFINFO_XTAB: continue; } mfns[nr_pages++] = mfns[i]; } if ( nr_pages > 0 ) { guest_mapping = xenforeignmemory_map(xch->fmem, ctx->domid, PROT_READ, nr_pages, mfns, errors); if ( !guest_mapping ) { PERROR("Failed to map guest pages"); goto err; } nr_pages_mapped = nr_pages; for ( i = 0, p = 0; i < nr_pfns; ++i ) { switch ( types[i] ) { case XEN_DOMCTL_PFINFO_BROKEN: case XEN_DOMCTL_PFINFO_XALLOC: case XEN_DOMCTL_PFINFO_XTAB: continue; } if ( errors[p] ) { ERROR("Mapping of pfn %#"PRIpfn" (mfn %#"PRIpfn") failed %d", ctx->save.batch_pfns[i], mfns[p], errors[p]); goto err; } orig_page = page = guest_mapping + (p * PAGE_SIZE); rc = ctx->save.ops.normalise_page(ctx, types[i], &page); if ( orig_page != page ) local_pages[i] = page; if ( rc ) { if ( rc == -1 && errno == EAGAIN ) { set_bit(ctx->save.batch_pfns[i], ctx->save.deferred_pages); ++ctx->save.nr_deferred_pages; types[i] = XEN_DOMCTL_PFINFO_XTAB; --nr_pages; } else goto err; } else guest_data[i] = page; rc = -1; ++p; } } rec_pfns = malloc(nr_pfns * sizeof(*rec_pfns)); if ( !rec_pfns ) { ERROR("Unable to allocate %zu bytes of memory for page data pfn list", nr_pfns * sizeof(*rec_pfns)); goto err; } hdr.count = nr_pfns; rec.length = sizeof(hdr); rec.length += nr_pfns * sizeof(*rec_pfns); rec.length += nr_pages * PAGE_SIZE; for ( i = 0; i < nr_pfns; ++i ) rec_pfns[i] = ((uint64_t)(types[i]) << 32) | ctx->save.batch_pfns[i]; iov[0].iov_base = &rec.type; iov[0].iov_len = sizeof(rec.type); iov[1].iov_base = &rec.length; iov[1].iov_len = sizeof(rec.length); iov[2].iov_base = &hdr; iov[2].iov_len = sizeof(hdr); iov[3].iov_base = rec_pfns; iov[3].iov_len = nr_pfns * sizeof(*rec_pfns); iovcnt = 4; if ( nr_pages ) { for ( i = 0; i < nr_pfns; ++i ) { if ( guest_data[i] ) { iov[iovcnt].iov_base = guest_data[i]; iov[iovcnt].iov_len = PAGE_SIZE; iovcnt++; --nr_pages; } } } if ( writev_exact(ctx->fd, iov, iovcnt) ) { PERROR("Failed to write page data to stream"); goto err; } /* Sanity check we have sent all the pages we expected to. */ assert(nr_pages == 0); rc = ctx->save.nr_batch_pfns = 0; err: free(rec_pfns); if ( guest_mapping ) xenforeignmemory_unmap(xch->fmem, guest_mapping, nr_pages_mapped); for ( i = 0; local_pages && i < nr_pfns; ++i ) free(local_pages[i]); free(iov); free(local_pages); free(guest_data); free(errors); free(types); free(mfns); return rc; } /* * Flush a batch of pfns into the stream. */ static int flush_batch(struct xc_sr_context *ctx) { int rc = 0; if ( ctx->save.nr_batch_pfns == 0 ) return rc; rc = write_batch(ctx); if ( !rc ) { VALGRIND_MAKE_MEM_UNDEFINED(ctx->save.batch_pfns, MAX_BATCH_SIZE * sizeof(*ctx->save.batch_pfns)); } return rc; } /* * Add a single pfn to the batch, flushing the batch if full. */ static int add_to_batch(struct xc_sr_context *ctx, xen_pfn_t pfn) { int rc = 0; if ( ctx->save.nr_batch_pfns == MAX_BATCH_SIZE ) rc = flush_batch(ctx); if ( rc == 0 ) ctx->save.batch_pfns[ctx->save.nr_batch_pfns++] = pfn; return rc; } /* * Pause/suspend the domain, and refresh ctx->dominfo if required. */ static int suspend_domain(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; /* TODO: Properly specify the return value from this callback. All * implementations currently appear to return 1 for success, whereas * the legacy code checks for != 0. */ int cb_rc = ctx->save.callbacks->suspend(ctx->save.callbacks->data); if ( cb_rc == 0 ) { ERROR("save callback suspend() failed: %d", cb_rc); return -1; } /* Refresh domain information. */ if ( (xc_domain_getinfo(xch, ctx->domid, 1, &ctx->dominfo) != 1) || (ctx->dominfo.domid != ctx->domid) ) { PERROR("Unable to refresh domain information"); return -1; } /* Confirm the domain has actually been paused. */ if ( !ctx->dominfo.shutdown || (ctx->dominfo.shutdown_reason != SHUTDOWN_suspend) ) { ERROR("Domain has not been suspended: shutdown %d, reason %d", ctx->dominfo.shutdown, ctx->dominfo.shutdown_reason); return -1; } xc_report_progress_single(xch, "Domain now suspended"); return 0; } /* * Send a subset of pages in the guests p2m, according to the dirty bitmap. * Used for each subsequent iteration of the live migration loop. * * Bitmap is bounded by p2m_size. */ static int send_dirty_pages(struct xc_sr_context *ctx, unsigned long entries) { xc_interface *xch = ctx->xch; xen_pfn_t p; unsigned long written; int rc; DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap, &ctx->save.dirty_bitmap_hbuf); for ( p = 0, written = 0; p < ctx->save.p2m_size; ++p ) { if ( !test_bit(p, dirty_bitmap) ) continue; rc = add_to_batch(ctx, p); if ( rc ) return rc; /* Update progress every 4MB worth of memory sent. */ if ( (written & ((1U << (22 - 12)) - 1)) == 0 ) xc_report_progress_step(xch, written, entries); ++written; } rc = flush_batch(ctx); if ( rc ) return rc; if ( written > entries ) DPRINTF("Bitmap contained more entries than expected..."); xc_report_progress_step(xch, entries, entries); return ctx->save.ops.check_vm_state(ctx); } /* * Send all pages in the guests p2m. Used as the first iteration of the live * migration loop, and for a non-live save. */ static int send_all_pages(struct xc_sr_context *ctx) { DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap, &ctx->save.dirty_bitmap_hbuf); bitmap_set(dirty_bitmap, ctx->save.p2m_size); return send_dirty_pages(ctx, ctx->save.p2m_size); } static int enable_logdirty(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int on1 = 0, off = 0, on2 = 0; int rc; /* This juggling is required if logdirty is enabled for VRAM tracking. */ rc = xc_shadow_control(xch, ctx->domid, XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY, NULL, 0, NULL, 0, NULL); if ( rc < 0 ) { on1 = errno; rc = xc_shadow_control(xch, ctx->domid, XEN_DOMCTL_SHADOW_OP_OFF, NULL, 0, NULL, 0, NULL); if ( rc < 0 ) off = errno; else { rc = xc_shadow_control(xch, ctx->domid, XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY, NULL, 0, NULL, 0, NULL); if ( rc < 0 ) on2 = errno; } if ( rc < 0 ) { PERROR("Failed to enable logdirty: %d,%d,%d", on1, off, on2); return rc; } } return 0; } static int update_progress_string(struct xc_sr_context *ctx, char **str, unsigned iter) { xc_interface *xch = ctx->xch; char *new_str = NULL; if ( asprintf(&new_str, "Frames iteration %u of %u", iter, ctx->save.max_iterations) == -1 ) { PERROR("Unable to allocate new progress string"); return -1; } free(*str); *str = new_str; xc_set_progress_prefix(xch, *str); return 0; } /* * Send memory while guest is running. */ static int send_memory_live(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; xc_shadow_op_stats_t stats = { 0, ctx->save.p2m_size }; char *progress_str = NULL; unsigned x; int rc; rc = update_progress_string(ctx, &progress_str, 0); if ( rc ) goto out; rc = send_all_pages(ctx); if ( rc ) goto out; for ( x = 1; ((x < ctx->save.max_iterations) && (stats.dirty_count > ctx->save.dirty_threshold)); ++x ) { if ( xc_shadow_control( xch, ctx->domid, XEN_DOMCTL_SHADOW_OP_CLEAN, &ctx->save.dirty_bitmap_hbuf, ctx->save.p2m_size, NULL, 0, &stats) != ctx->save.p2m_size ) { PERROR("Failed to retrieve logdirty bitmap"); rc = -1; goto out; } if ( stats.dirty_count == 0 ) break; rc = update_progress_string(ctx, &progress_str, x); if ( rc ) goto out; rc = send_dirty_pages(ctx, stats.dirty_count); if ( rc ) goto out; } out: xc_set_progress_prefix(xch, NULL); free(progress_str); return rc; } static int colo_merge_secondary_dirty_bitmap(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; struct xc_sr_record rec = { 0, 0, NULL }; uint64_t *pfns = NULL; uint64_t pfn; unsigned count, i; int rc; DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap, &ctx->save.dirty_bitmap_hbuf); rc = read_record(ctx, ctx->save.recv_fd, &rec); if ( rc ) goto err; if ( rec.type != REC_TYPE_CHECKPOINT_DIRTY_PFN_LIST ) { PERROR("Expect dirty bitmap record, but received %u", rec.type ); rc = -1; goto err; } if ( rec.length % sizeof(*pfns) ) { PERROR("Invalid dirty pfn list record length %u", rec.length ); rc = -1; goto err; } count = rec.length / sizeof(*pfns); pfns = rec.data; for ( i = 0; i < count; i++ ) { pfn = pfns[i]; if (pfn > ctx->save.p2m_size) { PERROR("Invalid pfn 0x%" PRIx64, pfn); rc = -1; goto err; } set_bit(pfn, dirty_bitmap); } rc = 0; err: free(rec.data); return rc; } /* * Suspend the domain and send dirty memory. * This is the last iteration of the live migration and the * heart of the checkpointed stream. */ static int suspend_and_send_dirty(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; xc_shadow_op_stats_t stats = { 0, ctx->save.p2m_size }; char *progress_str = NULL; int rc; DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap, &ctx->save.dirty_bitmap_hbuf); rc = suspend_domain(ctx); if ( rc ) goto out; if ( xc_shadow_control( xch, ctx->domid, XEN_DOMCTL_SHADOW_OP_CLEAN, HYPERCALL_BUFFER(dirty_bitmap), ctx->save.p2m_size, NULL, XEN_DOMCTL_SHADOW_LOGDIRTY_FINAL, &stats) != ctx->save.p2m_size ) { PERROR("Failed to retrieve logdirty bitmap"); rc = -1; goto out; } if ( ctx->save.live ) { rc = update_progress_string(ctx, &progress_str, ctx->save.max_iterations); if ( rc ) goto out; } else xc_set_progress_prefix(xch, "Checkpointed save"); bitmap_or(dirty_bitmap, ctx->save.deferred_pages, ctx->save.p2m_size); if ( !ctx->save.live && ctx->save.checkpointed == XC_MIG_STREAM_COLO ) { rc = colo_merge_secondary_dirty_bitmap(ctx); if ( rc ) { PERROR("Failed to get secondary vm's dirty pages"); goto out; } } rc = send_dirty_pages(ctx, stats.dirty_count + ctx->save.nr_deferred_pages); if ( rc ) goto out; bitmap_clear(ctx->save.deferred_pages, ctx->save.p2m_size); ctx->save.nr_deferred_pages = 0; out: xc_set_progress_prefix(xch, NULL); free(progress_str); return rc; } static int verify_frames(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; xc_shadow_op_stats_t stats = { 0, ctx->save.p2m_size }; int rc; struct xc_sr_record rec = { .type = REC_TYPE_VERIFY, .length = 0, }; DPRINTF("Enabling verify mode"); rc = write_record(ctx, &rec); if ( rc ) goto out; xc_set_progress_prefix(xch, "Frames verify"); rc = send_all_pages(ctx); if ( rc ) goto out; if ( xc_shadow_control( xch, ctx->domid, XEN_DOMCTL_SHADOW_OP_PEEK, &ctx->save.dirty_bitmap_hbuf, ctx->save.p2m_size, NULL, 0, &stats) != ctx->save.p2m_size ) { PERROR("Failed to retrieve logdirty bitmap"); rc = -1; goto out; } DPRINTF(" Further stats: faults %u, dirty %u", stats.fault_count, stats.dirty_count); out: return rc; } /* * Send all domain memory. This is the heart of the live migration loop. */ static int send_domain_memory_live(struct xc_sr_context *ctx) { int rc; rc = enable_logdirty(ctx); if ( rc ) goto out; rc = send_memory_live(ctx); if ( rc ) goto out; rc = suspend_and_send_dirty(ctx); if ( rc ) goto out; if ( ctx->save.debug && ctx->save.checkpointed != XC_MIG_STREAM_NONE ) { rc = verify_frames(ctx); if ( rc ) goto out; } out: return rc; } /* * Checkpointed save. */ static int send_domain_memory_checkpointed(struct xc_sr_context *ctx) { return suspend_and_send_dirty(ctx); } /* * Send all domain memory, pausing the domain first. Generally used for * suspend-to-file. */ static int send_domain_memory_nonlive(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc; rc = suspend_domain(ctx); if ( rc ) goto err; xc_set_progress_prefix(xch, "Frames"); rc = send_all_pages(ctx); if ( rc ) goto err; err: return rc; } static int setup(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc; DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap, &ctx->save.dirty_bitmap_hbuf); rc = ctx->save.ops.setup(ctx); if ( rc ) goto err; dirty_bitmap = xc_hypercall_buffer_alloc_pages( xch, dirty_bitmap, NRPAGES(bitmap_size(ctx->save.p2m_size))); ctx->save.batch_pfns = malloc(MAX_BATCH_SIZE * sizeof(*ctx->save.batch_pfns)); ctx->save.deferred_pages = calloc(1, bitmap_size(ctx->save.p2m_size)); if ( !ctx->save.batch_pfns || !dirty_bitmap || !ctx->save.deferred_pages ) { ERROR("Unable to allocate memory for dirty bitmaps, batch pfns and" " deferred pages"); rc = -1; errno = ENOMEM; goto err; } rc = 0; err: return rc; } static void cleanup(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap, &ctx->save.dirty_bitmap_hbuf); xc_shadow_control(xch, ctx->domid, XEN_DOMCTL_SHADOW_OP_OFF, NULL, 0, NULL, 0, NULL); if ( ctx->save.ops.cleanup(ctx) ) PERROR("Failed to clean up"); xc_hypercall_buffer_free_pages(xch, dirty_bitmap, NRPAGES(bitmap_size(ctx->save.p2m_size))); free(ctx->save.deferred_pages); free(ctx->save.batch_pfns); } /* * Save a domain. */ static int save(struct xc_sr_context *ctx, uint16_t guest_type) { xc_interface *xch = ctx->xch; int rc, saved_rc = 0, saved_errno = 0; IPRINTF("Saving domain %d, type %s", ctx->domid, dhdr_type_to_str(guest_type)); rc = setup(ctx); if ( rc ) goto err; xc_report_progress_single(xch, "Start of stream"); rc = write_headers(ctx, guest_type); if ( rc ) goto err; rc = ctx->save.ops.start_of_stream(ctx); if ( rc ) goto err; do { rc = ctx->save.ops.start_of_checkpoint(ctx); if ( rc ) goto err; rc = ctx->save.ops.check_vm_state(ctx); if ( rc ) goto err; if ( ctx->save.live ) rc = send_domain_memory_live(ctx); else if ( ctx->save.checkpointed != XC_MIG_STREAM_NONE ) rc = send_domain_memory_checkpointed(ctx); else rc = send_domain_memory_nonlive(ctx); if ( rc ) goto err; if ( !ctx->dominfo.shutdown || (ctx->dominfo.shutdown_reason != SHUTDOWN_suspend) ) { ERROR("Domain has not been suspended"); rc = -1; goto err; } rc = ctx->save.ops.end_of_checkpoint(ctx); if ( rc ) goto err; if ( ctx->save.checkpointed != XC_MIG_STREAM_NONE ) { /* * We have now completed the initial live portion of the checkpoint * process. Therefore switch into periodically sending synchronous * batches of pages. */ ctx->save.live = false; rc = write_checkpoint_record(ctx); if ( rc ) goto err; if ( ctx->save.checkpointed == XC_MIG_STREAM_COLO ) { rc = ctx->save.callbacks->checkpoint(ctx->save.callbacks->data); if ( !rc ) { rc = -1; goto err; } } rc = ctx->save.callbacks->postcopy(ctx->save.callbacks->data); if ( rc <= 0 ) goto err; if ( ctx->save.checkpointed == XC_MIG_STREAM_COLO ) { rc = ctx->save.callbacks->wait_checkpoint( ctx->save.callbacks->data); if ( rc <= 0 ) goto err; } else if ( ctx->save.checkpointed == XC_MIG_STREAM_REMUS ) { rc = ctx->save.callbacks->checkpoint(ctx->save.callbacks->data); if ( rc <= 0 ) goto err; } else { ERROR("Unknown checkpointed stream"); rc = -1; goto err; } } } while ( ctx->save.checkpointed != XC_MIG_STREAM_NONE ); xc_report_progress_single(xch, "End of stream"); rc = write_end_record(ctx); if ( rc ) goto err; xc_report_progress_single(xch, "Complete"); goto done; err: saved_errno = errno; saved_rc = rc; PERROR("Save failed"); done: cleanup(ctx); if ( saved_rc ) { rc = saved_rc; errno = saved_errno; } return rc; }; int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iters, uint32_t max_factor, uint32_t flags, struct save_callbacks* callbacks, int hvm, xc_migration_stream_t stream_type, int recv_fd) { struct xc_sr_context ctx = { .xch = xch, .fd = io_fd, }; /* GCC 4.4 (of CentOS 6.x vintage) can' t initialise anonymous unions. */ ctx.save.callbacks = callbacks; ctx.save.live = !!(flags & XCFLAGS_LIVE); ctx.save.debug = !!(flags & XCFLAGS_DEBUG); ctx.save.checkpointed = stream_type; ctx.save.recv_fd = recv_fd; /* If altering migration_stream update this assert too. */ assert(stream_type == XC_MIG_STREAM_NONE || stream_type == XC_MIG_STREAM_REMUS || stream_type == XC_MIG_STREAM_COLO); /* * TODO: Find some time to better tweak the live migration algorithm. * * These parameters are better than the legacy algorithm especially for * busy guests. */ ctx.save.max_iterations = 5; ctx.save.dirty_threshold = 50; /* Sanity checks for callbacks. */ if ( hvm ) assert(callbacks->switch_qemu_logdirty); if ( ctx.save.checkpointed ) assert(callbacks->checkpoint && callbacks->postcopy); if ( ctx.save.checkpointed == XC_MIG_STREAM_COLO ) assert(callbacks->wait_checkpoint); DPRINTF("fd %d, dom %u, max_iters %u, max_factor %u, flags %u, hvm %d", io_fd, dom, max_iters, max_factor, flags, hvm); if ( xc_domain_getinfo(xch, dom, 1, &ctx.dominfo) != 1 ) { PERROR("Failed to get domain info"); return -1; } if ( ctx.dominfo.domid != dom ) { ERROR("Domain %u does not exist", dom); return -1; } ctx.domid = dom; if ( ctx.dominfo.hvm ) { ctx.save.ops = save_ops_x86_hvm; return save(&ctx, DHDR_TYPE_X86_HVM); } else { ctx.save.ops = save_ops_x86_pv; return save(&ctx, DHDR_TYPE_X86_PV); } } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_decompress_unsafe_lzma.c0000664000175000017500000000046613256712137021151 0ustar smbsmb#include #include #include #include "xg_private.h" #include "xc_dom_decompress_unsafe.h" #include "../../xen/common/unlzma.c" int xc_try_lzma_decode( struct xc_dom_image *dom, void **blob, size_t *size) { return xc_dom_decompress_unsafe(unlzma, dom, blob, size); } xen-4.9.2/tools/libxc/xc_gnttab.c0000664000175000017500000001006013256712137015030 0ustar smbsmb/****************************************************************************** * * Copyright (c) 2007-2008, D G Murray * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" int xc_gnttab_op(xc_interface *xch, int cmd, void * op, int op_size, int count) { int ret = 0; DECLARE_HYPERCALL_BOUNCE(op, count * op_size, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); if ( xc_hypercall_bounce_pre(xch, op) ) { PERROR("Could not bounce buffer for grant table op hypercall"); goto out1; } ret = xencall3(xch->xcall, __HYPERVISOR_grant_table_op, cmd, HYPERCALL_BUFFER_AS_ARG(op), count); xc_hypercall_bounce_post(xch, op); out1: return ret; } int xc_gnttab_get_version(xc_interface *xch, int domid) { struct gnttab_get_version query; int rc; query.dom = domid; rc = xc_gnttab_op(xch, GNTTABOP_get_version, &query, sizeof(query), 1); if ( rc < 0 ) return rc; else return query.version; } static void *_gnttab_map_table(xc_interface *xch, int domid, int *gnt_num) { int rc, i; struct gnttab_query_size query; struct gnttab_setup_table setup; DECLARE_HYPERCALL_BUFFER(unsigned long, frame_list); xen_pfn_t *pfn_list = NULL; grant_entry_v1_t *gnt = NULL; if ( !gnt_num ) return NULL; query.dom = domid; rc = xc_gnttab_op(xch, GNTTABOP_query_size, &query, sizeof(query), 1); if ( rc || (query.status != GNTST_okay) ) { ERROR("Could not query dom%d's grant size\n", domid); return NULL; } *gnt_num = query.nr_frames * (PAGE_SIZE / sizeof(grant_entry_v1_t) ); frame_list = xc_hypercall_buffer_alloc(xch, frame_list, query.nr_frames * sizeof(unsigned long)); if ( !frame_list ) { ERROR("Could not allocate frame_list in xc_gnttab_map_table\n"); return NULL; } pfn_list = malloc(query.nr_frames * sizeof(xen_pfn_t)); if ( !pfn_list ) { ERROR("Could not allocate pfn_list in xc_gnttab_map_table\n"); goto err; } setup.dom = domid; setup.nr_frames = query.nr_frames; set_xen_guest_handle(setup.frame_list, frame_list); /* XXX Any race with other setup_table hypercall? */ rc = xc_gnttab_op(xch, GNTTABOP_setup_table, &setup, sizeof(setup), 1); if ( rc || (setup.status != GNTST_okay) ) { ERROR("Could not get grant table frame list\n"); goto err; } for ( i = 0; i < setup.nr_frames; i++ ) pfn_list[i] = frame_list[i]; gnt = xc_map_foreign_pages(xch, domid, PROT_READ, pfn_list, setup.nr_frames); if ( !gnt ) { ERROR("Could not map grant table\n"); goto err; } err: if ( frame_list ) xc_hypercall_buffer_free(xch, frame_list); free(pfn_list); return gnt; } grant_entry_v1_t *xc_gnttab_map_table_v1(xc_interface *xch, int domid, int *gnt_num) { if (xc_gnttab_get_version(xch, domid) == 2) return NULL; return _gnttab_map_table(xch, domid, gnt_num); } grant_entry_v2_t *xc_gnttab_map_table_v2(xc_interface *xch, int domid, int *gnt_num) { if (xc_gnttab_get_version(xch, domid) != 2) return NULL; return _gnttab_map_table(xch, domid, gnt_num); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_save_x86_hvm.c0000664000175000017500000001322713256712137016602 0ustar smbsmb#include #include "xc_sr_common_x86.h" #include /* * Query for the HVM context and write an HVM_CONTEXT record into the stream. */ static int write_hvm_context(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc, hvm_buf_size; struct xc_sr_record hvm_rec = { .type = REC_TYPE_HVM_CONTEXT, }; hvm_buf_size = xc_domain_hvm_getcontext(xch, ctx->domid, 0, 0); if ( hvm_buf_size < 0 ) { PERROR("Couldn't get HVM context size from Xen"); rc = -1; goto out; } hvm_rec.data = malloc(hvm_buf_size); if ( !hvm_rec.data ) { PERROR("Couldn't allocate memory"); rc = -1; goto out; } hvm_buf_size = xc_domain_hvm_getcontext(xch, ctx->domid, hvm_rec.data, hvm_buf_size); if ( hvm_buf_size < 0 ) { PERROR("Couldn't get HVM context from Xen"); rc = -1; goto out; } hvm_rec.length = hvm_buf_size; rc = write_record(ctx, &hvm_rec); if ( rc < 0 ) { PERROR("error write HVM_CONTEXT record"); goto out; } out: free(hvm_rec.data); return rc; } /* * Query for a range of HVM parameters and write an HVM_PARAMS record into the * stream. */ static int write_hvm_params(struct xc_sr_context *ctx) { static const unsigned int params[] = { HVM_PARAM_STORE_PFN, HVM_PARAM_IOREQ_PFN, HVM_PARAM_BUFIOREQ_PFN, HVM_PARAM_PAGING_RING_PFN, HVM_PARAM_MONITOR_RING_PFN, HVM_PARAM_SHARING_RING_PFN, HVM_PARAM_VM86_TSS_SIZED, HVM_PARAM_CONSOLE_PFN, HVM_PARAM_ACPI_IOPORTS_LOCATION, HVM_PARAM_VIRIDIAN, HVM_PARAM_IDENT_PT, HVM_PARAM_PAE_ENABLED, HVM_PARAM_VM_GENERATION_ID_ADDR, HVM_PARAM_IOREQ_SERVER_PFN, HVM_PARAM_NR_IOREQ_SERVER_PAGES, HVM_PARAM_X87_FIP_WIDTH, }; xc_interface *xch = ctx->xch; struct xc_sr_rec_hvm_params_entry entries[ARRAY_SIZE(params)]; struct xc_sr_rec_hvm_params hdr = { .count = 0, }; struct xc_sr_record rec = { .type = REC_TYPE_HVM_PARAMS, .length = sizeof(hdr), .data = &hdr, }; unsigned int i; int rc; for ( i = 0; i < ARRAY_SIZE(params); i++ ) { uint32_t index = params[i]; uint64_t value; rc = xc_hvm_param_get(xch, ctx->domid, index, &value); if ( rc ) { PERROR("Failed to get HVMPARAM at index %u", index); return rc; } if ( value != 0 ) { entries[hdr.count].index = index; entries[hdr.count].value = value; hdr.count++; } } /* No params? Skip this record. */ if ( hdr.count == 0 ) return 0; rc = write_split_record(ctx, &rec, entries, hdr.count * sizeof(*entries)); if ( rc ) PERROR("Failed to write HVM_PARAMS record"); return rc; } static xen_pfn_t x86_hvm_pfn_to_gfn(const struct xc_sr_context *ctx, xen_pfn_t pfn) { /* identity map */ return pfn; } static int x86_hvm_normalise_page(struct xc_sr_context *ctx, xen_pfn_t type, void **page) { /* no-op */ return 0; } static int x86_hvm_setup(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; xen_pfn_t nr_pfns; if ( xc_domain_nr_gpfns(xch, ctx->domid, &nr_pfns) < 0 ) { PERROR("Unable to obtain the guest p2m size"); return -1; } if ( nr_pfns > ~XEN_DOMCTL_PFINFO_LTAB_MASK ) { errno = E2BIG; PERROR("Cannot save this big a guest"); return -1; } ctx->save.p2m_size = nr_pfns; if ( ctx->save.callbacks->switch_qemu_logdirty( ctx->domid, 1, ctx->save.callbacks->data) ) { PERROR("Couldn't enable qemu log-dirty mode"); return -1; } ctx->x86_hvm.save.qemu_enabled_logdirty = true; return 0; } static int x86_hvm_start_of_stream(struct xc_sr_context *ctx) { /* no-op */ return 0; } static int x86_hvm_start_of_checkpoint(struct xc_sr_context *ctx) { /* no-op */ return 0; } static int x86_hvm_check_vm_state(struct xc_sr_context *ctx) { /* no-op */ return 0; } static int x86_hvm_end_of_checkpoint(struct xc_sr_context *ctx) { int rc; /* Write the TSC record. */ rc = write_tsc_info(ctx); if ( rc ) return rc; /* Write the HVM_CONTEXT record. */ rc = write_hvm_context(ctx); if ( rc ) return rc; /* Write HVM_PARAMS record contains applicable HVM params. */ rc = write_hvm_params(ctx); if ( rc ) return rc; return 0; } static int x86_hvm_cleanup(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; /* If qemu successfully enabled logdirty mode, attempt to disable. */ if ( ctx->x86_hvm.save.qemu_enabled_logdirty && ctx->save.callbacks->switch_qemu_logdirty( ctx->domid, 0, ctx->save.callbacks->data) ) { PERROR("Couldn't disable qemu log-dirty mode"); return -1; } return 0; } struct xc_sr_save_ops save_ops_x86_hvm = { .pfn_to_gfn = x86_hvm_pfn_to_gfn, .normalise_page = x86_hvm_normalise_page, .setup = x86_hvm_setup, .start_of_stream = x86_hvm_start_of_stream, .start_of_checkpoint = x86_hvm_start_of_checkpoint, .end_of_checkpoint = x86_hvm_end_of_checkpoint, .check_vm_state = x86_hvm_check_vm_state, .cleanup = x86_hvm_cleanup, }; /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_offline_page.c0000664000175000017500000004503113256712137016175 0ustar smbsmb/****************************************************************************** * xc_offline_page.c * * Helper functions to offline/online one page * * Copyright (c) 2003, K A Fraser. * Copyright (c) 2009, Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include #include #include #include #include #include #include "xc_private.h" #include "xc_dom.h" #include "xg_private.h" #include "xg_save_restore.h" struct pte_backup_entry { xen_pfn_t table_mfn; int offset; }; #define DEFAULT_BACKUP_COUNT 1024 struct pte_backup { struct pte_backup_entry *entries; int max; int cur; }; static struct domain_info_context _dinfo; static struct domain_info_context *dinfo = &_dinfo; int xc_mark_page_online(xc_interface *xch, unsigned long start, unsigned long end, uint32_t *status) { DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(status, sizeof(uint32_t)*(end - start + 1), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); int ret = -1; if ( !status || (end < start) ) { errno = EINVAL; return -1; } if ( xc_hypercall_bounce_pre(xch, status) ) { ERROR("Could not bounce memory for xc_mark_page_online\n"); return -1; } sysctl.cmd = XEN_SYSCTL_page_offline_op; sysctl.u.page_offline.start = start; sysctl.u.page_offline.cmd = sysctl_page_online; sysctl.u.page_offline.end = end; set_xen_guest_handle(sysctl.u.page_offline.status, status); ret = xc_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, status); return ret; } int xc_mark_page_offline(xc_interface *xch, unsigned long start, unsigned long end, uint32_t *status) { DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(status, sizeof(uint32_t)*(end - start + 1), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); int ret = -1; if ( !status || (end < start) ) { errno = EINVAL; return -1; } if ( xc_hypercall_bounce_pre(xch, status) ) { ERROR("Could not bounce memory for xc_mark_page_offline"); return -1; } sysctl.cmd = XEN_SYSCTL_page_offline_op; sysctl.u.page_offline.start = start; sysctl.u.page_offline.cmd = sysctl_page_offline; sysctl.u.page_offline.end = end; set_xen_guest_handle(sysctl.u.page_offline.status, status); ret = xc_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, status); return ret; } int xc_query_page_offline_status(xc_interface *xch, unsigned long start, unsigned long end, uint32_t *status) { DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(status, sizeof(uint32_t)*(end - start + 1), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); int ret = -1; if ( !status || (end < start) ) { errno = EINVAL; return -1; } if ( xc_hypercall_bounce_pre(xch, status) ) { ERROR("Could not bounce memory for xc_query_page_offline_status\n"); return -1; } sysctl.cmd = XEN_SYSCTL_page_offline_op; sysctl.u.page_offline.start = start; sysctl.u.page_offline.cmd = sysctl_query_page_offline; sysctl.u.page_offline.end = end; set_xen_guest_handle(sysctl.u.page_offline.status, status); ret = xc_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, status); return ret; } /* * There should no update to the grant when domain paused */ static int xc_is_page_granted_v1(xc_interface *xch, xen_pfn_t gpfn, grant_entry_v1_t *gnttab, int gnt_num) { int i = 0; if (!gnttab) return 0; for (i = 0; i < gnt_num; i++) if ( ((gnttab[i].flags & GTF_type_mask) != GTF_invalid) && (gnttab[i].frame == gpfn) ) break; return (i != gnt_num); } static int xc_is_page_granted_v2(xc_interface *xch, xen_pfn_t gpfn, grant_entry_v2_t *gnttab, int gnt_num) { int i = 0; if (!gnttab) return 0; for (i = 0; i < gnt_num; i++) if ( ((gnttab[i].hdr.flags & GTF_type_mask) != GTF_invalid) && (gnttab[i].full_page.frame == gpfn) ) break; return (i != gnt_num); } static int backup_ptes(xen_pfn_t table_mfn, int offset, struct pte_backup *backup) { if (!backup) return -EINVAL; if (backup->max == backup->cur) { backup->entries = realloc(backup->entries, backup->max * 2 * sizeof(struct pte_backup_entry)); if (backup->entries == NULL) return -1; else backup->max *= 2; } backup->entries[backup->cur].table_mfn = table_mfn; backup->entries[backup->cur++].offset = offset; return 0; } /* * return: * 1 when MMU update is required * 0 when no changes * <0 when error happen */ typedef int (*pte_func)(xc_interface *xch, uint64_t pte, uint64_t *new_pte, unsigned long table_mfn, int table_offset, struct pte_backup *backup, unsigned long no_use); static int __clear_pte(xc_interface *xch, uint64_t pte, uint64_t *new_pte, unsigned long table_mfn, int table_offset, struct pte_backup *backup, unsigned long mfn) { /* If no new_pte pointer, same as no changes needed */ if (!new_pte || !backup) return -EINVAL; if ( !(pte & _PAGE_PRESENT)) return 0; /* XXX Check for PSE bit here */ /* Hit one entry */ if ( ((pte >> PAGE_SHIFT_X86) & MFN_MASK_X86) == mfn) { *new_pte = pte & ~_PAGE_PRESENT; if (!backup_ptes(table_mfn, table_offset, backup)) return 1; } return 0; } static int __update_pte(xc_interface *xch, uint64_t pte, uint64_t *new_pte, unsigned long table_mfn, int table_offset, struct pte_backup *backup, unsigned long new_mfn) { int index; if (!new_pte) return 0; for (index = 0; index < backup->cur; index ++) if ( (backup->entries[index].table_mfn == table_mfn) && (backup->entries[index].offset == table_offset) ) break; if (index != backup->cur) { if (pte & _PAGE_PRESENT) ERROR("Page present while in backup ptes\n"); pte &= ~MFN_MASK_X86; pte |= (new_mfn << PAGE_SHIFT_X86) | _PAGE_PRESENT; *new_pte = pte; return 1; } return 0; } static int change_pte(xc_interface *xch, int domid, struct xc_domain_meminfo *minfo, struct pte_backup *backup, struct xc_mmu *mmu, pte_func func, unsigned long data) { int pte_num, rc; uint64_t i; void *content = NULL; pte_num = PAGE_SIZE / ((minfo->pt_levels == 2) ? 4 : 8); for (i = 0; i < minfo->p2m_size; i++) { xen_pfn_t table_mfn = xc_pfn_to_mfn(i, minfo->p2m_table, minfo->guest_width); uint64_t pte, new_pte; int j; if ( (table_mfn == INVALID_PFN) || ((minfo->pfn_type[i] & XEN_DOMCTL_PFINFO_LTAB_MASK) == XEN_DOMCTL_PFINFO_XTAB) ) continue; if ( minfo->pfn_type[i] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK ) { content = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ, table_mfn); if (!content) goto failed; for (j = 0; j < pte_num; j++) { if ( minfo->pt_levels == 2 ) pte = ((const uint32_t*)content)[j]; else pte = ((const uint64_t*)content)[j]; rc = func(xch, pte, &new_pte, table_mfn, j, backup, data); switch (rc) { case 1: if ( xc_add_mmu_update(xch, mmu, table_mfn << PAGE_SHIFT | j * ( (minfo->pt_levels == 2) ? sizeof(uint32_t): sizeof(uint64_t)) | MMU_PT_UPDATE_PRESERVE_AD, new_pte) ) goto failed; break; case 0: break; default: goto failed; } } munmap(content, PAGE_SIZE); content = NULL; } } if ( xc_flush_mmu_updates(xch, mmu) ) goto failed; return 0; failed: /* XXX Shall we take action if we have fail to swap? */ if (content) munmap(content, PAGE_SIZE); return -1; } static int update_pte(xc_interface *xch, int domid, struct xc_domain_meminfo *minfo, struct pte_backup *backup, struct xc_mmu *mmu, unsigned long new_mfn) { return change_pte(xch, domid, minfo, backup, mmu, __update_pte, new_mfn); } static int clear_pte(xc_interface *xch, int domid, struct xc_domain_meminfo *minfo, struct pte_backup *backup, struct xc_mmu *mmu, xen_pfn_t mfn) { return change_pte(xch, domid, minfo, backup, mmu, __clear_pte, mfn); } /* * Check if a page can be exchanged successfully */ static int is_page_exchangable(xc_interface *xch, int domid, xen_pfn_t mfn, xc_dominfo_t *info) { uint32_t status; int rc; /* domain checking */ if ( !domid || (domid > DOMID_FIRST_RESERVED) ) { DPRINTF("Dom0's page can't be LM"); return 0; } if (info->hvm) { DPRINTF("Currently we can only live change PV guest's page\n"); return 0; } /* Check if pages are offline pending or not */ rc = xc_query_page_offline_status(xch, mfn, mfn, &status); if ( rc || !(status & PG_OFFLINE_STATUS_OFFLINE_PENDING) ) { ERROR("Page %lx is not offline pending %x\n", mfn, status); return 0; } return 1; } xen_pfn_t *xc_map_m2p(xc_interface *xch, unsigned long max_mfn, int prot, unsigned long *mfn0) { privcmd_mmap_entry_t *entries; unsigned long m2p_chunks, m2p_size; xen_pfn_t *m2p; xen_pfn_t *extent_start; int i; m2p = NULL; m2p_size = M2P_SIZE(max_mfn); m2p_chunks = M2P_CHUNKS(max_mfn); extent_start = calloc(m2p_chunks, sizeof(xen_pfn_t)); if ( !extent_start ) { ERROR("failed to allocate space for m2p mfns"); goto err0; } if ( xc_machphys_mfn_list(xch, m2p_chunks, extent_start) ) { PERROR("xc_get_m2p_mfns"); goto err1; } entries = calloc(m2p_chunks, sizeof(privcmd_mmap_entry_t)); if (entries == NULL) { ERROR("failed to allocate space for mmap entries"); goto err1; } for ( i = 0; i < m2p_chunks; i++ ) entries[i].mfn = extent_start[i]; m2p = xc_map_foreign_ranges(xch, DOMID_XEN, m2p_size, prot, M2P_CHUNK_SIZE, entries, m2p_chunks); if (m2p == NULL) { PERROR("xc_mmap_foreign_ranges failed"); goto err2; } if (mfn0) *mfn0 = entries[0].mfn; err2: free(entries); err1: free(extent_start); err0: return m2p; } /* The domain should be suspended when called here */ int xc_exchange_page(xc_interface *xch, int domid, xen_pfn_t mfn) { xc_dominfo_t info; struct xc_domain_meminfo minfo; struct xc_mmu *mmu = NULL; struct pte_backup old_ptes = {NULL, 0, 0}; grant_entry_v1_t *gnttab_v1 = NULL; grant_entry_v2_t *gnttab_v2 = NULL; struct mmuext_op mops; int gnt_num, unpined = 0; void *old_p, *backup = NULL; int rc, result = -1; uint32_t status; xen_pfn_t new_mfn, gpfn; xen_pfn_t *m2p_table; unsigned long max_mfn; if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 ) { ERROR("Could not get domain info"); return -1; } if (!info.shutdown || info.shutdown_reason != SHUTDOWN_suspend) { errno = EINVAL; ERROR("Can't exchange page unless domain is suspended\n"); return -1; } if (!is_page_exchangable(xch, domid, mfn, &info)) { ERROR("Could not exchange page\n"); return -1; } /* Map M2P and obtain gpfn */ rc = xc_maximum_ram_page(xch, &max_mfn); if ( rc || !(m2p_table = xc_map_m2p(xch, max_mfn, PROT_READ, NULL)) ) { PERROR("Failed to map live M2P table"); return -1; } gpfn = m2p_table[mfn]; /* Map domain's memory information */ memset(&minfo, 0, sizeof(minfo)); if ( xc_map_domain_meminfo(xch, domid, &minfo) ) { PERROR("Could not map domain's memory information\n"); goto failed; } /* For translation macros */ dinfo->guest_width = minfo.guest_width; dinfo->p2m_size = minfo.p2m_size; /* Don't exchange CR3 for PAE guest in PAE host environment */ if (minfo.guest_width > sizeof(long)) { if ( (minfo.pfn_type[gpfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) == XEN_DOMCTL_PFINFO_L3TAB ) goto failed; } gnttab_v2 = xc_gnttab_map_table_v2(xch, domid, &gnt_num); if (!gnttab_v2) { gnttab_v1 = xc_gnttab_map_table_v1(xch, domid, &gnt_num); if (!gnttab_v1) { ERROR("Failed to map grant table\n"); goto failed; } } if (gnttab_v1 ? xc_is_page_granted_v1(xch, mfn, gnttab_v1, gnt_num) : xc_is_page_granted_v2(xch, mfn, gnttab_v2, gnt_num)) { ERROR("Page %lx is granted now\n", mfn); goto failed; } /* allocate required data structure */ backup = malloc(PAGE_SIZE); if (!backup) { ERROR("Failed to allocate backup pages pointer\n"); goto failed; } old_ptes.max = DEFAULT_BACKUP_COUNT; old_ptes.entries = malloc(sizeof(struct pte_backup_entry) * DEFAULT_BACKUP_COUNT); if (!old_ptes.entries) { ERROR("Faield to allocate backup\n"); goto failed; } old_ptes.cur = 0; /* Unpin the page if it is pined */ if (minfo.pfn_type[gpfn] & XEN_DOMCTL_PFINFO_LPINTAB) { mops.cmd = MMUEXT_UNPIN_TABLE; mops.arg1.mfn = mfn; if ( xc_mmuext_op(xch, &mops, 1, domid) < 0 ) { ERROR("Failed to unpin page %lx", mfn); goto failed; } mops.arg1.mfn = mfn; unpined = 1; } /* backup the content */ old_p = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ, mfn); if (!old_p) { ERROR("Failed to map foreign page %lx\n", mfn); goto failed; } memcpy(backup, old_p, PAGE_SIZE); munmap(old_p, PAGE_SIZE); mmu = xc_alloc_mmu_updates(xch, domid); if ( mmu == NULL ) { ERROR("%s: failed at %d\n", __FUNCTION__, __LINE__); goto failed; } /* Firstly update all pte to be invalid to remove the reference */ rc = clear_pte(xch, domid, &minfo, &old_ptes, mmu, mfn); if (rc) { ERROR("clear pte failed\n"); goto failed; } rc = xc_domain_memory_exchange_pages(xch, domid, 1, 0, &mfn, 1, 0, &new_mfn); if (rc) { ERROR("Exchange the page failed\n"); /* Exchange fail means there are refere to the page still */ rc = update_pte(xch, domid, &minfo, &old_ptes, mmu, mfn); if (rc) result = -2; goto failed; } rc = update_pte(xch, domid, &minfo, &old_ptes, mmu, new_mfn); if (rc) { ERROR("update pte failed guest may be broken now\n"); /* No recover action now for swap fail */ result = -2; goto failed; } /* Check if pages are offlined already */ rc = xc_query_page_offline_status(xch, mfn, mfn, &status); if (rc) { ERROR("Fail to query offline status\n"); }else if ( !(status & PG_OFFLINE_STATUS_OFFLINED) ) { ERROR("page is still online or pending\n"); goto failed; } else { void *new_p; IPRINTF("Now page is offlined %lx\n", mfn); /* Update the p2m table */ minfo.p2m_table[gpfn] = new_mfn; new_p = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ|PROT_WRITE, new_mfn); if ( new_p == NULL ) { ERROR("failed to map new_p for copy, guest may be broken?"); goto failed; } memcpy(new_p, backup, PAGE_SIZE); munmap(new_p, PAGE_SIZE); mops.arg1.mfn = new_mfn; result = 0; } failed: if (unpined && (minfo.pfn_type[mfn] & XEN_DOMCTL_PFINFO_LPINTAB)) { switch ( minfo.pfn_type[mfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK ) { case XEN_DOMCTL_PFINFO_L1TAB: mops.cmd = MMUEXT_PIN_L1_TABLE; break; case XEN_DOMCTL_PFINFO_L2TAB: mops.cmd = MMUEXT_PIN_L2_TABLE; break; case XEN_DOMCTL_PFINFO_L3TAB: mops.cmd = MMUEXT_PIN_L3_TABLE; break; case XEN_DOMCTL_PFINFO_L4TAB: mops.cmd = MMUEXT_PIN_L4_TABLE; break; default: ERROR("Unpined for non pate table page\n"); break; } if ( xc_mmuext_op(xch, &mops, 1, domid) < 0 ) { ERROR("failed to pin the mfn again\n"); result = -2; } } free(mmu); free(old_ptes.entries); free(backup); if (gnttab_v1) munmap(gnttab_v1, gnt_num / (PAGE_SIZE/sizeof(grant_entry_v1_t))); if (gnttab_v2) munmap(gnttab_v2, gnt_num / (PAGE_SIZE/sizeof(grant_entry_v2_t))); xc_unmap_domain_meminfo(xch, &minfo); munmap(m2p_table, M2P_SIZE(max_mfn)); return result; } xen-4.9.2/tools/libxc/COPYING0000664000175000017500000006050213256712137013754 0ustar smbsmbNote that the only valid version of the LGPL as far as the files in this directory (and its subdirectories) are concerned is _this_ particular version of the license (i.e., *only* v2.1, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. Where clause 3 is invoked in order to relicense under the GPL then this shall be considered to be GPL v2 only for files which have specified LGPL v2.1 only. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS xen-4.9.2/tools/libxc/xc_sr_save_x86_pv.c0000664000175000017500000007655013256712137016445 0ustar smbsmb#include #include #include "xc_sr_common_x86_pv.h" /* Check a 64 bit virtual address for being canonical. */ static inline bool is_canonical_address(xen_vaddr_t vaddr) { return ((int64_t)vaddr >> 47) == ((int64_t)vaddr >> 63); } /* * Maps the guests shared info page. */ static int map_shinfo(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; ctx->x86_pv.shinfo = xc_map_foreign_range( xch, ctx->domid, PAGE_SIZE, PROT_READ, ctx->dominfo.shared_info_frame); if ( !ctx->x86_pv.shinfo ) { PERROR("Failed to map shared info frame at mfn %#lx", ctx->dominfo.shared_info_frame); return -1; } return 0; } /* * Copy a list of mfns from a guest, accounting for differences between guest * and toolstack width. Can fail if truncation would occur. */ static int copy_mfns_from_guest(const struct xc_sr_context *ctx, xen_pfn_t *dst, const void *src, size_t count) { size_t x; if ( ctx->x86_pv.width == sizeof(unsigned long) ) memcpy(dst, src, count * sizeof(*dst)); else { for ( x = 0; x < count; ++x ) { #ifdef __x86_64__ /* 64bit toolstack, 32bit guest. Expand any INVALID_MFN. */ uint32_t s = ((uint32_t *)src)[x]; dst[x] = s == ~0U ? INVALID_MFN : s; #else /* * 32bit toolstack, 64bit guest. Truncate INVALID_MFN, but bail * if any other truncation would occur. * * This will only occur on hosts where a PV guest has ram above * the 16TB boundary. A 32bit dom0 is unlikely to have * successfully booted on a system this large. */ uint64_t s = ((uint64_t *)src)[x]; if ( (s != ~0ULL) && ((s >> 32) != 0) ) { errno = E2BIG; return -1; } dst[x] = s; #endif } } return 0; } /* * Map the p2m leave pages and build an array of their pfns. */ static int map_p2m_leaves(struct xc_sr_context *ctx, xen_pfn_t *mfns, size_t n_mfns) { xc_interface *xch = ctx->xch; unsigned x; ctx->x86_pv.p2m = xc_map_foreign_pages(xch, ctx->domid, PROT_READ, mfns, n_mfns); if ( !ctx->x86_pv.p2m ) { PERROR("Failed to map p2m frames"); return -1; } ctx->save.p2m_size = ctx->x86_pv.max_pfn + 1; ctx->x86_pv.p2m_frames = n_mfns; ctx->x86_pv.p2m_pfns = malloc(n_mfns * sizeof(*mfns)); if ( !ctx->x86_pv.p2m_pfns ) { ERROR("Cannot allocate %zu bytes for p2m pfns list", n_mfns * sizeof(*mfns)); return -1; } /* Convert leaf frames from mfns to pfns. */ for ( x = 0; x < n_mfns; ++x ) { if ( !mfn_in_pseudophysmap(ctx, mfns[x]) ) { ERROR("Bad mfn in p2m_frame_list[%u]", x); dump_bad_pseudophysmap_entry(ctx, mfns[x]); errno = ERANGE; return -1; } ctx->x86_pv.p2m_pfns[x] = mfn_to_pfn(ctx, mfns[x]); } return 0; } /* * Walk the guests frame list list and frame list to identify and map the * frames making up the guests p2m table. Construct a list of pfns making up * the table. */ static int map_p2m_tree(struct xc_sr_context *ctx) { /* Terminology: * * fll - frame list list, top level p2m, list of fl mfns * fl - frame list, mid level p2m, list of leaf mfns * local - own allocated buffers, adjusted for bitness * guest - mappings into the domain */ xc_interface *xch = ctx->xch; int rc = -1; unsigned x, saved_x, fpp, fll_entries, fl_entries; xen_pfn_t fll_mfn, saved_mfn, max_pfn; xen_pfn_t *local_fll = NULL; void *guest_fll = NULL; size_t local_fll_size; xen_pfn_t *local_fl = NULL; void *guest_fl = NULL; size_t local_fl_size; fpp = PAGE_SIZE / ctx->x86_pv.width; fll_entries = (ctx->x86_pv.max_pfn / (fpp * fpp)) + 1; if ( fll_entries > fpp ) { ERROR("max_pfn %#lx too large for p2m tree", ctx->x86_pv.max_pfn); goto err; } fll_mfn = GET_FIELD(ctx->x86_pv.shinfo, arch.pfn_to_mfn_frame_list_list, ctx->x86_pv.width); if ( fll_mfn == 0 || fll_mfn > ctx->x86_pv.max_mfn ) { ERROR("Bad mfn %#lx for p2m frame list list", fll_mfn); goto err; } /* Map the guest top p2m. */ guest_fll = xc_map_foreign_range(xch, ctx->domid, PAGE_SIZE, PROT_READ, fll_mfn); if ( !guest_fll ) { PERROR("Failed to map p2m frame list list at %#lx", fll_mfn); goto err; } local_fll_size = fll_entries * sizeof(*local_fll); local_fll = malloc(local_fll_size); if ( !local_fll ) { ERROR("Cannot allocate %zu bytes for local p2m frame list list", local_fll_size); goto err; } if ( copy_mfns_from_guest(ctx, local_fll, guest_fll, fll_entries) ) { ERROR("Truncation detected copying p2m frame list list"); goto err; } /* Check for bad mfns in frame list list. */ saved_mfn = 0; saved_x = 0; for ( x = 0; x < fll_entries; ++x ) { if ( local_fll[x] == 0 || local_fll[x] > ctx->x86_pv.max_mfn ) { ERROR("Bad mfn %#lx at index %u (of %u) in p2m frame list list", local_fll[x], x, fll_entries); goto err; } if ( local_fll[x] != saved_mfn ) { saved_mfn = local_fll[x]; saved_x = x; } } /* * Check for actual lower max_pfn: * If the trailing entries of the frame list list were all the same we can * assume they all reference mid pages all referencing p2m pages with all * invalid entries. Otherwise there would be multiple pfns referencing all * the same mfn which can't work across migration, as this sharing would be * broken by the migration process. * Adjust max_pfn if possible to avoid allocating much larger areas as * needed for p2m and logdirty map. */ max_pfn = (saved_x + 1) * fpp * fpp - 1; if ( max_pfn < ctx->x86_pv.max_pfn ) { ctx->x86_pv.max_pfn = max_pfn; fll_entries = (ctx->x86_pv.max_pfn / (fpp * fpp)) + 1; } ctx->x86_pv.p2m_frames = (ctx->x86_pv.max_pfn + fpp) / fpp; DPRINTF("max_pfn %#lx, p2m_frames %d", ctx->x86_pv.max_pfn, ctx->x86_pv.p2m_frames); fl_entries = (ctx->x86_pv.max_pfn / fpp) + 1; /* Map the guest mid p2m frames. */ guest_fl = xc_map_foreign_pages(xch, ctx->domid, PROT_READ, local_fll, fll_entries); if ( !guest_fl ) { PERROR("Failed to map p2m frame list"); goto err; } local_fl_size = fl_entries * sizeof(*local_fl); local_fl = malloc(local_fl_size); if ( !local_fl ) { ERROR("Cannot allocate %zu bytes for local p2m frame list", local_fl_size); goto err; } if ( copy_mfns_from_guest(ctx, local_fl, guest_fl, fl_entries) ) { ERROR("Truncation detected copying p2m frame list"); goto err; } for ( x = 0; x < fl_entries; ++x ) { if ( local_fl[x] == 0 || local_fl[x] > ctx->x86_pv.max_mfn ) { ERROR("Bad mfn %#lx at index %u (of %u) in p2m frame list", local_fl[x], x, fl_entries); goto err; } } /* Map the p2m leaves themselves. */ rc = map_p2m_leaves(ctx, local_fl, fl_entries); err: free(local_fl); if ( guest_fl ) munmap(guest_fl, fll_entries * PAGE_SIZE); free(local_fll); if ( guest_fll ) munmap(guest_fll, PAGE_SIZE); return rc; } /* * Get p2m_generation count. * Returns an error if the generation count has changed since the last call. */ static int get_p2m_generation(struct xc_sr_context *ctx) { uint64_t p2m_generation; int rc; p2m_generation = GET_FIELD(ctx->x86_pv.shinfo, arch.p2m_generation, ctx->x86_pv.width); rc = (p2m_generation == ctx->x86_pv.p2m_generation) ? 0 : -1; ctx->x86_pv.p2m_generation = p2m_generation; return rc; } static int x86_pv_check_vm_state_p2m_list(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc; if ( !ctx->save.live ) return 0; rc = get_p2m_generation(ctx); if ( rc ) ERROR("p2m generation count changed. Migration aborted."); return rc; } /* * Map the guest p2m frames specified via a cr3 value, a virtual address, and * the maximum pfn. PTE entries are 64 bits for both, 32 and 64 bit guests as * in 32 bit case we support PAE guests only. */ static int map_p2m_list(struct xc_sr_context *ctx, uint64_t p2m_cr3) { xc_interface *xch = ctx->xch; xen_vaddr_t p2m_vaddr, p2m_end, mask, off; xen_pfn_t p2m_mfn, mfn, saved_mfn, max_pfn; uint64_t *ptes = NULL; xen_pfn_t *mfns = NULL; unsigned fpp, n_pages, level, shift, idx_start, idx_end, idx, saved_idx; int rc = -1; p2m_mfn = cr3_to_mfn(ctx, p2m_cr3); assert(p2m_mfn != 0); if ( p2m_mfn > ctx->x86_pv.max_mfn ) { ERROR("Bad p2m_cr3 value %#" PRIx64, p2m_cr3); errno = ERANGE; goto err; } get_p2m_generation(ctx); p2m_vaddr = GET_FIELD(ctx->x86_pv.shinfo, arch.p2m_vaddr, ctx->x86_pv.width); fpp = PAGE_SIZE / ctx->x86_pv.width; ctx->x86_pv.p2m_frames = ctx->x86_pv.max_pfn / fpp + 1; p2m_end = p2m_vaddr + ctx->x86_pv.p2m_frames * PAGE_SIZE - 1; if ( ctx->x86_pv.width == 8 ) { mask = 0x0000ffffffffffffULL; if ( !is_canonical_address(p2m_vaddr) || !is_canonical_address(p2m_end) || p2m_end < p2m_vaddr || (p2m_vaddr <= HYPERVISOR_VIRT_END_X86_64 && p2m_end > HYPERVISOR_VIRT_START_X86_64) ) { ERROR("Bad virtual p2m address range %#" PRIx64 "-%#" PRIx64, p2m_vaddr, p2m_end); errno = ERANGE; goto err; } } else { mask = 0x00000000ffffffffULL; if ( p2m_vaddr > mask || p2m_end > mask || p2m_end < p2m_vaddr || (p2m_vaddr <= HYPERVISOR_VIRT_END_X86_32 && p2m_end > HYPERVISOR_VIRT_START_X86_32) ) { ERROR("Bad virtual p2m address range %#" PRIx64 "-%#" PRIx64, p2m_vaddr, p2m_end); errno = ERANGE; goto err; } } DPRINTF("p2m list from %#" PRIx64 " to %#" PRIx64 ", root at %#lx", p2m_vaddr, p2m_end, p2m_mfn); DPRINTF("max_pfn %#lx, p2m_frames %d", ctx->x86_pv.max_pfn, ctx->x86_pv.p2m_frames); mfns = malloc(sizeof(*mfns)); if ( !mfns ) { ERROR("Cannot allocate memory for array of %u mfns", 1); goto err; } mfns[0] = p2m_mfn; off = 0; saved_mfn = 0; idx_start = idx_end = saved_idx = 0; for ( level = ctx->x86_pv.levels; level > 0; level-- ) { n_pages = idx_end - idx_start + 1; ptes = xc_map_foreign_pages(xch, ctx->domid, PROT_READ, mfns, n_pages); if ( !ptes ) { PERROR("Failed to map %u page table pages for p2m list", n_pages); goto err; } free(mfns); shift = level * 9 + 3; idx_start = ((p2m_vaddr - off) & mask) >> shift; idx_end = ((p2m_end - off) & mask) >> shift; idx = idx_end - idx_start + 1; mfns = malloc(sizeof(*mfns) * idx); if ( !mfns ) { ERROR("Cannot allocate memory for array of %u mfns", idx); goto err; } for ( idx = idx_start; idx <= idx_end; idx++ ) { mfn = pte_to_frame(ptes[idx]); if ( mfn == 0 || mfn > ctx->x86_pv.max_mfn ) { ERROR("Bad mfn %#lx during page table walk for vaddr %#" PRIx64 " at level %d of p2m list", mfn, off + ((xen_vaddr_t)idx << shift), level); errno = ERANGE; goto err; } mfns[idx - idx_start] = mfn; /* Maximum pfn check at level 2. Same reasoning as for p2m tree. */ if ( level == 2 ) { if ( mfn != saved_mfn ) { saved_mfn = mfn; saved_idx = idx - idx_start; } } } if ( level == 2 ) { if ( saved_idx == idx_end ) saved_idx++; max_pfn = ((xen_pfn_t)saved_idx << 9) * fpp - 1; if ( max_pfn < ctx->x86_pv.max_pfn ) { ctx->x86_pv.max_pfn = max_pfn; ctx->x86_pv.p2m_frames = (ctx->x86_pv.max_pfn + fpp) / fpp; p2m_end = p2m_vaddr + ctx->x86_pv.p2m_frames * PAGE_SIZE - 1; idx_end = idx_start + saved_idx; } } munmap(ptes, n_pages * PAGE_SIZE); ptes = NULL; off = p2m_vaddr & ((mask >> shift) << shift); } /* Map the p2m leaves themselves. */ rc = map_p2m_leaves(ctx, mfns, idx_end - idx_start + 1); err: free(mfns); if ( ptes ) munmap(ptes, n_pages * PAGE_SIZE); return rc; } /* * Map the guest p2m frames. * Depending on guest support this might either be a virtual mapped linear * list (preferred format) or a 3 level tree linked via mfns. */ static int map_p2m(struct xc_sr_context *ctx) { uint64_t p2m_cr3; ctx->x86_pv.p2m_generation = ~0ULL; ctx->x86_pv.max_pfn = GET_FIELD(ctx->x86_pv.shinfo, arch.max_pfn, ctx->x86_pv.width) - 1; p2m_cr3 = GET_FIELD(ctx->x86_pv.shinfo, arch.p2m_cr3, ctx->x86_pv.width); return p2m_cr3 ? map_p2m_list(ctx, p2m_cr3) : map_p2m_tree(ctx); } /* * Obtain a specific vcpus basic state and write an X86_PV_VCPU_BASIC record * into the stream. Performs mfn->pfn conversion on architectural state. */ static int write_one_vcpu_basic(struct xc_sr_context *ctx, uint32_t id) { xc_interface *xch = ctx->xch; xen_pfn_t mfn, pfn; unsigned i, gdt_count; int rc = -1; vcpu_guest_context_any_t vcpu; struct xc_sr_rec_x86_pv_vcpu_hdr vhdr = { .vcpu_id = id, }; struct xc_sr_record rec = { .type = REC_TYPE_X86_PV_VCPU_BASIC, .length = sizeof(vhdr), .data = &vhdr, }; if ( xc_vcpu_getcontext(xch, ctx->domid, id, &vcpu) ) { PERROR("Failed to get vcpu%u context", id); goto err; } /* Vcpu0 is special: Convert the suspend record to a pfn. */ if ( id == 0 ) { mfn = GET_FIELD(&vcpu, user_regs.edx, ctx->x86_pv.width); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("Bad mfn for suspend record"); dump_bad_pseudophysmap_entry(ctx, mfn); errno = ERANGE; goto err; } SET_FIELD(&vcpu, user_regs.edx, mfn_to_pfn(ctx, mfn), ctx->x86_pv.width); } gdt_count = GET_FIELD(&vcpu, gdt_ents, ctx->x86_pv.width); if ( gdt_count > FIRST_RESERVED_GDT_ENTRY ) { ERROR("GDT entry count (%u) out of range (max %u)", gdt_count, FIRST_RESERVED_GDT_ENTRY); errno = ERANGE; goto err; } gdt_count = (gdt_count + 511) / 512; /* gdt_count now in units of frames. */ /* Convert GDT frames to pfns. */ for ( i = 0; i < gdt_count; ++i ) { mfn = GET_FIELD(&vcpu, gdt_frames[i], ctx->x86_pv.width); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("Bad mfn for frame %u of vcpu%u's GDT", i, id); dump_bad_pseudophysmap_entry(ctx, mfn); errno = ERANGE; goto err; } SET_FIELD(&vcpu, gdt_frames[i], mfn_to_pfn(ctx, mfn), ctx->x86_pv.width); } /* Convert CR3 to a pfn. */ mfn = cr3_to_mfn(ctx, GET_FIELD(&vcpu, ctrlreg[3], ctx->x86_pv.width)); if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("Bad mfn for vcpu%u's cr3", id); dump_bad_pseudophysmap_entry(ctx, mfn); errno = ERANGE; goto err; } pfn = mfn_to_pfn(ctx, mfn); SET_FIELD(&vcpu, ctrlreg[3], mfn_to_cr3(ctx, pfn), ctx->x86_pv.width); /* 64bit guests: Convert CR1 (guest pagetables) to pfn. */ if ( ctx->x86_pv.levels == 4 && vcpu.x64.ctrlreg[1] ) { mfn = vcpu.x64.ctrlreg[1] >> PAGE_SHIFT; if ( !mfn_in_pseudophysmap(ctx, mfn) ) { ERROR("Bad mfn for vcpu%u's cr1", id); dump_bad_pseudophysmap_entry(ctx, mfn); errno = ERANGE; goto err; } pfn = mfn_to_pfn(ctx, mfn); vcpu.x64.ctrlreg[1] = 1 | ((uint64_t)pfn << PAGE_SHIFT); } if ( ctx->x86_pv.width == 8 ) rc = write_split_record(ctx, &rec, &vcpu, sizeof(vcpu.x64)); else rc = write_split_record(ctx, &rec, &vcpu, sizeof(vcpu.x32)); err: return rc; } /* * Obtain a specific vcpus extended state and write an X86_PV_VCPU_EXTENDED * record into the stream. */ static int write_one_vcpu_extended(struct xc_sr_context *ctx, uint32_t id) { xc_interface *xch = ctx->xch; struct xc_sr_rec_x86_pv_vcpu_hdr vhdr = { .vcpu_id = id, }; struct xc_sr_record rec = { .type = REC_TYPE_X86_PV_VCPU_EXTENDED, .length = sizeof(vhdr), .data = &vhdr, }; struct xen_domctl domctl = { .cmd = XEN_DOMCTL_get_ext_vcpucontext, .domain = ctx->domid, .u.ext_vcpucontext.vcpu = id, }; if ( xc_domctl(xch, &domctl) < 0 ) { PERROR("Unable to get vcpu%u extended context", id); return -1; } /* No content? Skip the record. */ if ( domctl.u.ext_vcpucontext.size == 0 ) return 0; return write_split_record(ctx, &rec, &domctl.u.ext_vcpucontext, domctl.u.ext_vcpucontext.size); } /* * Query to see whether a specific vcpu has xsave state and if so, write an * X86_PV_VCPU_XSAVE record into the stream. */ static int write_one_vcpu_xsave(struct xc_sr_context *ctx, uint32_t id) { xc_interface *xch = ctx->xch; int rc = -1; DECLARE_HYPERCALL_BUFFER(void, buffer); struct xc_sr_rec_x86_pv_vcpu_hdr vhdr = { .vcpu_id = id, }; struct xc_sr_record rec = { .type = REC_TYPE_X86_PV_VCPU_XSAVE, .length = sizeof(vhdr), .data = &vhdr, }; struct xen_domctl domctl = { .cmd = XEN_DOMCTL_getvcpuextstate, .domain = ctx->domid, .u.vcpuextstate.vcpu = id, }; if ( xc_domctl(xch, &domctl) < 0 ) { PERROR("Unable to get vcpu%u's xsave context", id); goto err; } /* No xsave state? skip this record. */ if ( !domctl.u.vcpuextstate.xfeature_mask ) goto out; buffer = xc_hypercall_buffer_alloc(xch, buffer, domctl.u.vcpuextstate.size); if ( !buffer ) { ERROR("Unable to allocate %"PRIx64" bytes for vcpu%u's xsave context", domctl.u.vcpuextstate.size, id); goto err; } set_xen_guest_handle(domctl.u.vcpuextstate.buffer, buffer); if ( xc_domctl(xch, &domctl) < 0 ) { PERROR("Unable to get vcpu%u's xsave context", id); goto err; } /* No xsave state? Skip this record. */ if ( domctl.u.vcpuextstate.size == 0 ) goto out; rc = write_split_record(ctx, &rec, buffer, domctl.u.vcpuextstate.size); if ( rc ) goto err; out: rc = 0; err: xc_hypercall_buffer_free(xch, buffer); return rc; } /* * Query to see whether a specific vcpu has msr state and if so, write an * X86_PV_VCPU_MSRS record into the stream. */ static int write_one_vcpu_msrs(struct xc_sr_context *ctx, uint32_t id) { xc_interface *xch = ctx->xch; int rc = -1; size_t buffersz; DECLARE_HYPERCALL_BUFFER(void, buffer); struct xc_sr_rec_x86_pv_vcpu_hdr vhdr = { .vcpu_id = id, }; struct xc_sr_record rec = { .type = REC_TYPE_X86_PV_VCPU_MSRS, .length = sizeof(vhdr), .data = &vhdr, }; struct xen_domctl domctl = { .cmd = XEN_DOMCTL_get_vcpu_msrs, .domain = ctx->domid, .u.vcpu_msrs.vcpu = id, }; if ( xc_domctl(xch, &domctl) < 0 ) { PERROR("Unable to get vcpu%u's msrs", id); goto err; } /* No MSRs? skip this record. */ if ( !domctl.u.vcpu_msrs.msr_count ) goto out; buffersz = domctl.u.vcpu_msrs.msr_count * sizeof(xen_domctl_vcpu_msr_t); buffer = xc_hypercall_buffer_alloc(xch, buffer, buffersz); if ( !buffer ) { ERROR("Unable to allocate %zu bytes for vcpu%u's msrs", buffersz, id); goto err; } set_xen_guest_handle(domctl.u.vcpu_msrs.msrs, buffer); if ( xc_domctl(xch, &domctl) < 0 ) { PERROR("Unable to get vcpu%u's msrs", id); goto err; } /* No MSRs? Skip this record. */ if ( domctl.u.vcpu_msrs.msr_count == 0 ) goto out; rc = write_split_record(ctx, &rec, buffer, domctl.u.vcpu_msrs.msr_count * sizeof(xen_domctl_vcpu_msr_t)); if ( rc ) goto err; out: rc = 0; err: xc_hypercall_buffer_free(xch, buffer); return rc; } /* * For each vcpu, if it is online, write its state into the stream. */ static int write_all_vcpu_information(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; xc_vcpuinfo_t vinfo; unsigned int i; int rc; for ( i = 0; i <= ctx->dominfo.max_vcpu_id; ++i ) { rc = xc_vcpu_getinfo(xch, ctx->domid, i, &vinfo); if ( rc ) { PERROR("Failed to get vcpu%u information", i); return rc; } /* Vcpu offline? skip all these records. */ if ( !vinfo.online ) continue; rc = write_one_vcpu_basic(ctx, i); if ( rc ) return rc; rc = write_one_vcpu_extended(ctx, i); if ( rc ) return rc; rc = write_one_vcpu_xsave(ctx, i); if ( rc ) return rc; rc = write_one_vcpu_msrs(ctx, i); if ( rc ) return rc; } return 0; } /* * Writes an X86_PV_INFO record into the stream. */ static int write_x86_pv_info(struct xc_sr_context *ctx) { struct xc_sr_rec_x86_pv_info info = { .guest_width = ctx->x86_pv.width, .pt_levels = ctx->x86_pv.levels, }; struct xc_sr_record rec = { .type = REC_TYPE_X86_PV_INFO, .length = sizeof(info), .data = &info }; return write_record(ctx, &rec); } /* * Writes an X86_PV_P2M_FRAMES record into the stream. This contains the list * of pfns making up the p2m table. */ static int write_x86_pv_p2m_frames(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc; unsigned i; size_t datasz = ctx->x86_pv.p2m_frames * sizeof(uint64_t); uint64_t *data = NULL; struct xc_sr_rec_x86_pv_p2m_frames hdr = { .start_pfn = 0, .end_pfn = ctx->x86_pv.max_pfn, }; struct xc_sr_record rec = { .type = REC_TYPE_X86_PV_P2M_FRAMES, .length = sizeof(hdr), .data = &hdr, }; /* No need to translate if sizeof(uint64_t) == sizeof(xen_pfn_t). */ if ( sizeof(uint64_t) != sizeof(*ctx->x86_pv.p2m_pfns) ) { if ( !(data = malloc(datasz)) ) { ERROR("Cannot allocate %zu bytes for X86_PV_P2M_FRAMES data", datasz); return -1; } for ( i = 0; i < ctx->x86_pv.p2m_frames; ++i ) data[i] = ctx->x86_pv.p2m_pfns[i]; } else data = (uint64_t *)ctx->x86_pv.p2m_pfns; rc = write_split_record(ctx, &rec, data, datasz); if ( data != (uint64_t *)ctx->x86_pv.p2m_pfns ) free(data); return rc; } /* * Writes an SHARED_INFO record into the stream. */ static int write_shared_info(struct xc_sr_context *ctx) { struct xc_sr_record rec = { .type = REC_TYPE_SHARED_INFO, .length = PAGE_SIZE, .data = ctx->x86_pv.shinfo, }; return write_record(ctx, &rec); } /* * Normalise a pagetable for the migration stream. Performs pfn->mfn * conversions on the ptes. */ static int normalise_pagetable(struct xc_sr_context *ctx, const uint64_t *src, uint64_t *dst, unsigned long type) { xc_interface *xch = ctx->xch; uint64_t pte; unsigned i, xen_first = -1, xen_last = -1; /* Indices of Xen mappings. */ type &= XEN_DOMCTL_PFINFO_LTABTYPE_MASK; if ( ctx->x86_pv.levels == 4 ) { /* 64bit guests only have Xen mappings in their L4 tables. */ if ( type == XEN_DOMCTL_PFINFO_L4TAB ) { xen_first = (HYPERVISOR_VIRT_START_X86_64 >> L4_PAGETABLE_SHIFT_X86_64) & 511; xen_last = (HYPERVISOR_VIRT_END_X86_64 >> L4_PAGETABLE_SHIFT_X86_64) & 511; } } else { switch ( type ) { case XEN_DOMCTL_PFINFO_L4TAB: ERROR("??? Found L4 table for 32bit guest"); errno = EINVAL; return -1; case XEN_DOMCTL_PFINFO_L3TAB: /* 32bit guests can only use the first 4 entries of their L3 tables. * All other are potentially used by Xen. */ xen_first = 4; xen_last = 511; break; case XEN_DOMCTL_PFINFO_L2TAB: /* It is hard to spot Xen mappings in a 32bit guest's L2. Most * are normal but only a few will have Xen mappings. */ i = (HYPERVISOR_VIRT_START_X86_32 >> L2_PAGETABLE_SHIFT_PAE) & 511; if ( pte_to_frame(src[i]) == ctx->x86_pv.compat_m2p_mfn0 ) { xen_first = i; xen_last = (HYPERVISOR_VIRT_END_X86_32 >> L2_PAGETABLE_SHIFT_PAE) & 511; } break; } } for ( i = 0; i < (PAGE_SIZE / sizeof(uint64_t)); ++i ) { xen_pfn_t mfn; pte = src[i]; /* Remove Xen mappings: Xen will reconstruct on the other side. */ if ( i >= xen_first && i <= xen_last ) pte = 0; /* * Errors during the live part of migration are expected as a result * of split pagetable updates, page type changes, active grant * mappings etc. The pagetable will need to be resent after pausing. * In such cases we fail with EAGAIN. * * For domains which are already paused, errors are fatal. */ if ( pte & _PAGE_PRESENT ) { mfn = pte_to_frame(pte); #ifdef __i386__ if ( mfn == INVALID_MFN ) { if ( !ctx->dominfo.paused ) errno = EAGAIN; else { ERROR("PTE truncation detected. L%lu[%u] = %016"PRIx64, type >> XEN_DOMCTL_PFINFO_LTAB_SHIFT, i, pte); errno = E2BIG; } return -1; } #endif if ( (type > XEN_DOMCTL_PFINFO_L1TAB) && (pte & _PAGE_PSE) ) { if ( !ctx->dominfo.paused ) errno = EAGAIN; else { ERROR("Cannot migrate superpage (L%lu[%u]: 0x%016"PRIx64")", type >> XEN_DOMCTL_PFINFO_LTAB_SHIFT, i, pte); errno = E2BIG; } return -1; } if ( !mfn_in_pseudophysmap(ctx, mfn) ) { if ( !ctx->dominfo.paused ) errno = EAGAIN; else { ERROR("Bad mfn for L%lu[%u]", type >> XEN_DOMCTL_PFINFO_LTAB_SHIFT, i); dump_bad_pseudophysmap_entry(ctx, mfn); errno = ERANGE; } return -1; } pte = merge_pte(pte, mfn_to_pfn(ctx, mfn)); } dst[i] = pte; } return 0; } /* save_ops function. */ static xen_pfn_t x86_pv_pfn_to_gfn(const struct xc_sr_context *ctx, xen_pfn_t pfn) { assert(pfn <= ctx->x86_pv.max_pfn); return xc_pfn_to_mfn(pfn, ctx->x86_pv.p2m, ctx->x86_pv.width); } /* * save_ops function. Performs pagetable normalisation on appropriate pages. */ static int x86_pv_normalise_page(struct xc_sr_context *ctx, xen_pfn_t type, void **page) { xc_interface *xch = ctx->xch; void *local_page; int rc; type &= XEN_DOMCTL_PFINFO_LTABTYPE_MASK; if ( type < XEN_DOMCTL_PFINFO_L1TAB || type > XEN_DOMCTL_PFINFO_L4TAB ) return 0; local_page = malloc(PAGE_SIZE); if ( !local_page ) { ERROR("Unable to allocate scratch page"); rc = -1; goto out; } rc = normalise_pagetable(ctx, *page, local_page, type); *page = local_page; out: return rc; } /* * save_ops function. Queries domain information and maps the Xen m2p and the * guests shinfo and p2m table. */ static int x86_pv_setup(struct xc_sr_context *ctx) { int rc; rc = x86_pv_domain_info(ctx); if ( rc ) return rc; rc = x86_pv_map_m2p(ctx); if ( rc ) return rc; rc = map_shinfo(ctx); if ( rc ) return rc; rc = map_p2m(ctx); if ( rc ) return rc; return 0; } /* * save_ops function. Writes PV header records into the stream. */ static int x86_pv_start_of_stream(struct xc_sr_context *ctx) { int rc; rc = write_x86_pv_info(ctx); if ( rc ) return rc; /* * Ideally should be able to change during migration. Currently * corruption will occur if the contents or location of the P2M changes * during the live migration loop. If one is very lucky, the breakage * will not be subtle. */ rc = write_x86_pv_p2m_frames(ctx); if ( rc ) return rc; return 0; } static int x86_pv_start_of_checkpoint(struct xc_sr_context *ctx) { return 0; } static int x86_pv_end_of_checkpoint(struct xc_sr_context *ctx) { int rc; rc = write_tsc_info(ctx); if ( rc ) return rc; rc = write_shared_info(ctx); if ( rc ) return rc; rc = write_all_vcpu_information(ctx); if ( rc ) return rc; return 0; } static int x86_pv_check_vm_state(struct xc_sr_context *ctx) { if ( ctx->x86_pv.p2m_generation == ~0ULL ) return 0; return x86_pv_check_vm_state_p2m_list(ctx); } /* * save_ops function. Cleanup. */ static int x86_pv_cleanup(struct xc_sr_context *ctx) { free(ctx->x86_pv.p2m_pfns); if ( ctx->x86_pv.p2m ) munmap(ctx->x86_pv.p2m, ctx->x86_pv.p2m_frames * PAGE_SIZE); if ( ctx->x86_pv.shinfo ) munmap(ctx->x86_pv.shinfo, PAGE_SIZE); if ( ctx->x86_pv.m2p ) munmap(ctx->x86_pv.m2p, ctx->x86_pv.nr_m2p_frames * PAGE_SIZE); return 0; } struct xc_sr_save_ops save_ops_x86_pv = { .pfn_to_gfn = x86_pv_pfn_to_gfn, .normalise_page = x86_pv_normalise_page, .setup = x86_pv_setup, .start_of_stream = x86_pv_start_of_stream, .start_of_checkpoint = x86_pv_start_of_checkpoint, .end_of_checkpoint = x86_pv_end_of_checkpoint, .check_vm_state = x86_pv_check_vm_state, .cleanup = x86_pv_cleanup, }; /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_core.h0000664000175000017500000001343513256712137014517 0ustar smbsmb/* * Copyright (c) 2006 Isaku Yamahata * VA Linux Systems Japan K.K. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * */ #ifndef XC_CORE_H #define XC_CORE_H #include "xen/version.h" #include "xg_private.h" #include "xen/libelf/elfstructs.h" /* section names */ #define XEN_DUMPCORE_SEC_NOTE ".note.Xen" #define XEN_DUMPCORE_SEC_PRSTATUS ".xen_prstatus" #define XEN_DUMPCORE_SEC_SHARED_INFO ".xen_shared_info" #define XEN_DUMPCORE_SEC_P2M ".xen_p2m" #define XEN_DUMPCORE_SEC_PFN ".xen_pfn" #define XEN_DUMPCORE_SEC_PAGES ".xen_pages" /* elf note name */ #define XEN_DUMPCORE_ELFNOTE_NAME "Xen" /* note numbers are defined in xen/elfnote.h */ struct elfnote { uint32_t namesz; /* Elf_Note note; */ uint32_t descsz; uint32_t type; char name[4]; /* sizeof("Xen") = 4 * Fotunately this is 64bit aligned so that * we can use same structore for both 32/64bit */ }; struct xen_dumpcore_elfnote_none_desc { /* nothing */ }; struct xen_dumpcore_elfnote_header_desc { uint64_t xch_magic; uint64_t xch_nr_vcpus; uint64_t xch_nr_pages; uint64_t xch_page_size; }; struct xen_dumpcore_elfnote_xen_version_desc { uint64_t major_version; uint64_t minor_version; xen_extraversion_t extra_version; xen_compile_info_t compile_info; xen_capabilities_info_t capabilities; xen_changeset_info_t changeset; xen_platform_parameters_t platform_parameters; uint64_t pagesize; }; #define XEN_DUMPCORE_FORMAT_VERSION(major, minor) \ ((major) << 32) | ((minor) & 0xffffffff) #define XEN_DUMPCORE_FORMAT_MAJOR(version) ((major) >> 32) #define XEN_DUMPCORE_FORMAT_MINOR(version) ((minor) & 0xffffffff) #define XEN_DUMPCORE_FORMAT_MAJOR_CURRENT ((uint64_t)0) #define XEN_DUMPCORE_FORMAT_MINOR_CURRENT ((uint64_t)1) #define XEN_DUMPCORE_FORMAT_VERSION_CURRENT \ XEN_DUMPCORE_FORMAT_VERSION(XEN_DUMPCORE_FORMAT_MAJOR_CURRENT, \ XEN_DUMPCORE_FORMAT_MINOR_CURRENT) struct xen_dumpcore_elfnote_format_version_desc { uint64_t version; }; struct xen_dumpcore_elfnote_none { struct elfnote elfnote; struct xen_dumpcore_elfnote_none_desc none; }; struct xen_dumpcore_elfnote_header { struct elfnote elfnote; struct xen_dumpcore_elfnote_header_desc header; }; struct xen_dumpcore_elfnote_xen_version { struct elfnote elfnote; struct xen_dumpcore_elfnote_xen_version_desc xen_version; }; struct xen_dumpcore_elfnote_format_version { struct elfnote elfnote; struct xen_dumpcore_elfnote_format_version_desc format_version; }; #define XC_CORE_INVALID_PFN (~(uint64_t)0) #define XC_CORE_INVALID_GMFN (~(uint64_t)0) struct xen_dumpcore_p2m { uint64_t pfn; uint64_t gmfn; }; struct xc_core_strtab; struct xc_core_section_headers; Elf64_Shdr* xc_core_shdr_get(xc_interface *xch, struct xc_core_section_headers *sheaders); int xc_core_shdr_set(xc_interface *xch, Elf64_Shdr *shdr, struct xc_core_strtab *strtab, const char *name, uint32_t type, uint64_t offset, uint64_t size, uint64_t addralign, uint64_t entsize); struct xc_core_memory_map { uint64_t addr; uint64_t size; }; typedef struct xc_core_memory_map xc_core_memory_map_t; int xc_core_arch_auto_translated_physmap(const xc_dominfo_t *info); struct xc_core_arch_context; int xc_core_arch_memory_map_get(xc_interface *xch, struct xc_core_arch_context *arch_ctxt, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xc_core_memory_map_t **mapp, unsigned int *nr_entries); int xc_core_arch_map_p2m(xc_interface *xch, unsigned int guest_width, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xen_pfn_t **live_p2m, unsigned long *pfnp); int xc_core_arch_map_p2m_writable(xc_interface *xch, unsigned int guest_width, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xen_pfn_t **live_p2m, unsigned long *pfnp); int xc_core_arch_get_scratch_gpfn(xc_interface *xch, domid_t domid, xen_pfn_t *gpfn); #if defined (__i386__) || defined (__x86_64__) # include "xc_core_x86.h" #elif defined (__arm__) || defined(__aarch64__) # include "xc_core_arm.h" #else # error "unsupported architecture" #endif #ifndef ELF_CORE_EFLAGS # define ELF_CORE_EFLAGS 0 #endif #endif /* XC_CORE_H */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_restore.c0000664000175000017500000006165213256712137015755 0ustar smbsmb#include #include #include "xc_sr_common.h" /* * Read and validate the Image and Domain headers. */ static int read_headers(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; struct xc_sr_ihdr ihdr; struct xc_sr_dhdr dhdr; if ( read_exact(ctx->fd, &ihdr, sizeof(ihdr)) ) { PERROR("Failed to read Image Header from stream"); return -1; } ihdr.id = ntohl(ihdr.id); ihdr.version = ntohl(ihdr.version); ihdr.options = ntohs(ihdr.options); if ( ihdr.marker != IHDR_MARKER ) { ERROR("Invalid marker: Got 0x%016"PRIx64, ihdr.marker); return -1; } else if ( ihdr.id != IHDR_ID ) { ERROR("Invalid ID: Expected 0x%08x, Got 0x%08x", IHDR_ID, ihdr.id); return -1; } else if ( ihdr.version != IHDR_VERSION ) { ERROR("Invalid Version: Expected %d, Got %d", ihdr.version, IHDR_VERSION); return -1; } else if ( ihdr.options & IHDR_OPT_BIG_ENDIAN ) { ERROR("Unable to handle big endian streams"); return -1; } ctx->restore.format_version = ihdr.version; if ( read_exact(ctx->fd, &dhdr, sizeof(dhdr)) ) { PERROR("Failed to read Domain Header from stream"); return -1; } ctx->restore.guest_type = dhdr.type; ctx->restore.guest_page_size = (1U << dhdr.page_shift); if ( dhdr.xen_major == 0 ) { IPRINTF("Found %s domain, converted from legacy stream format", dhdr_type_to_str(dhdr.type)); DPRINTF(" Legacy conversion script version %u", dhdr.xen_minor); } else IPRINTF("Found %s domain from Xen %u.%u", dhdr_type_to_str(dhdr.type), dhdr.xen_major, dhdr.xen_minor); return 0; } /* * Is a pfn populated? */ static bool pfn_is_populated(const struct xc_sr_context *ctx, xen_pfn_t pfn) { if ( pfn > ctx->restore.max_populated_pfn ) return false; return test_bit(pfn, ctx->restore.populated_pfns); } /* * Set a pfn as populated, expanding the tracking structures if needed. To * avoid realloc()ing too excessively, the size increased to the nearest power * of two large enough to contain the required pfn. */ static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn) { xc_interface *xch = ctx->xch; if ( pfn > ctx->restore.max_populated_pfn ) { xen_pfn_t new_max; size_t old_sz, new_sz; unsigned long *p; /* Round up to the nearest power of two larger than pfn, less 1. */ new_max = pfn; new_max |= new_max >> 1; new_max |= new_max >> 2; new_max |= new_max >> 4; new_max |= new_max >> 8; new_max |= new_max >> 16; #ifdef __x86_64__ new_max |= new_max >> 32; #endif old_sz = bitmap_size(ctx->restore.max_populated_pfn + 1); new_sz = bitmap_size(new_max + 1); p = realloc(ctx->restore.populated_pfns, new_sz); if ( !p ) { ERROR("Failed to realloc populated bitmap"); errno = ENOMEM; return -1; } memset((uint8_t *)p + old_sz, 0x00, new_sz - old_sz); ctx->restore.populated_pfns = p; ctx->restore.max_populated_pfn = new_max; } assert(!test_bit(pfn, ctx->restore.populated_pfns)); set_bit(pfn, ctx->restore.populated_pfns); return 0; } /* * Given a set of pfns, obtain memory from Xen to fill the physmap for the * unpopulated subset. If types is NULL, no page type checking is performed * and all unpopulated pfns are populated. */ int populate_pfns(struct xc_sr_context *ctx, unsigned count, const xen_pfn_t *original_pfns, const uint32_t *types) { xc_interface *xch = ctx->xch; xen_pfn_t *mfns = malloc(count * sizeof(*mfns)), *pfns = malloc(count * sizeof(*pfns)); unsigned i, nr_pfns = 0; int rc = -1; if ( !mfns || !pfns ) { ERROR("Failed to allocate %zu bytes for populating the physmap", 2 * count * sizeof(*mfns)); goto err; } for ( i = 0; i < count; ++i ) { if ( (!types || (types && (types[i] != XEN_DOMCTL_PFINFO_XTAB && types[i] != XEN_DOMCTL_PFINFO_BROKEN))) && !pfn_is_populated(ctx, original_pfns[i]) ) { rc = pfn_set_populated(ctx, original_pfns[i]); if ( rc ) goto err; pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i]; ++nr_pfns; } } if ( nr_pfns ) { rc = xc_domain_populate_physmap_exact( xch, ctx->domid, nr_pfns, 0, 0, mfns); if ( rc ) { PERROR("Failed to populate physmap"); goto err; } for ( i = 0; i < nr_pfns; ++i ) { if ( mfns[i] == INVALID_MFN ) { ERROR("Populate physmap failed for pfn %u", i); rc = -1; goto err; } ctx->restore.ops.set_gfn(ctx, pfns[i], mfns[i]); } } rc = 0; err: free(pfns); free(mfns); return rc; } /* * Given a list of pfns, their types, and a block of page data from the * stream, populate and record their types, map the relevant subset and copy * the data into the guest. */ static int process_page_data(struct xc_sr_context *ctx, unsigned count, xen_pfn_t *pfns, uint32_t *types, void *page_data) { xc_interface *xch = ctx->xch; xen_pfn_t *mfns = malloc(count * sizeof(*mfns)); int *map_errs = malloc(count * sizeof(*map_errs)); int rc; void *mapping = NULL, *guest_page = NULL; unsigned i, /* i indexes the pfns from the record. */ j, /* j indexes the subset of pfns we decide to map. */ nr_pages = 0; if ( !mfns || !map_errs ) { rc = -1; ERROR("Failed to allocate %zu bytes to process page data", count * (sizeof(*mfns) + sizeof(*map_errs))); goto err; } rc = populate_pfns(ctx, count, pfns, types); if ( rc ) { ERROR("Failed to populate pfns for batch of %u pages", count); goto err; } for ( i = 0; i < count; ++i ) { ctx->restore.ops.set_page_type(ctx, pfns[i], types[i]); switch ( types[i] ) { case XEN_DOMCTL_PFINFO_NOTAB: case XEN_DOMCTL_PFINFO_L1TAB: case XEN_DOMCTL_PFINFO_L1TAB | XEN_DOMCTL_PFINFO_LPINTAB: case XEN_DOMCTL_PFINFO_L2TAB: case XEN_DOMCTL_PFINFO_L2TAB | XEN_DOMCTL_PFINFO_LPINTAB: case XEN_DOMCTL_PFINFO_L3TAB: case XEN_DOMCTL_PFINFO_L3TAB | XEN_DOMCTL_PFINFO_LPINTAB: case XEN_DOMCTL_PFINFO_L4TAB: case XEN_DOMCTL_PFINFO_L4TAB | XEN_DOMCTL_PFINFO_LPINTAB: mfns[nr_pages++] = ctx->restore.ops.pfn_to_gfn(ctx, pfns[i]); break; } } /* Nothing to do? */ if ( nr_pages == 0 ) goto done; mapping = guest_page = xenforeignmemory_map(xch->fmem, ctx->domid, PROT_READ | PROT_WRITE, nr_pages, mfns, map_errs); if ( !mapping ) { rc = -1; PERROR("Unable to map %u mfns for %u pages of data", nr_pages, count); goto err; } for ( i = 0, j = 0; i < count; ++i ) { switch ( types[i] ) { case XEN_DOMCTL_PFINFO_XTAB: case XEN_DOMCTL_PFINFO_BROKEN: case XEN_DOMCTL_PFINFO_XALLOC: /* No page data to deal with. */ continue; } if ( map_errs[j] ) { rc = -1; ERROR("Mapping pfn %#"PRIpfn" (mfn %#"PRIpfn", type %#"PRIx32") failed with %d", pfns[i], mfns[j], types[i], map_errs[j]); goto err; } /* Undo page normalisation done by the saver. */ rc = ctx->restore.ops.localise_page(ctx, types[i], page_data); if ( rc ) { ERROR("Failed to localise pfn %#"PRIpfn" (type %#"PRIx32")", pfns[i], types[i] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT); goto err; } if ( ctx->restore.verify ) { /* Verify mode - compare incoming data to what we already have. */ if ( memcmp(guest_page, page_data, PAGE_SIZE) ) ERROR("verify pfn %#"PRIpfn" failed (type %#"PRIx32")", pfns[i], types[i] >> XEN_DOMCTL_PFINFO_LTAB_SHIFT); } else { /* Regular mode - copy incoming data into place. */ memcpy(guest_page, page_data, PAGE_SIZE); } ++j; guest_page += PAGE_SIZE; page_data += PAGE_SIZE; } done: rc = 0; err: if ( mapping ) xenforeignmemory_unmap(xch->fmem, mapping, nr_pages); free(map_errs); free(mfns); return rc; } /* * Validate a PAGE_DATA record from the stream, and pass the results to * process_page_data() to actually perform the legwork. */ static int handle_page_data(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; struct xc_sr_rec_page_data_header *pages = rec->data; unsigned i, pages_of_data = 0; int rc = -1; xen_pfn_t *pfns = NULL, pfn; uint32_t *types = NULL, type; if ( rec->length < sizeof(*pages) ) { ERROR("PAGE_DATA record truncated: length %u, min %zu", rec->length, sizeof(*pages)); goto err; } else if ( pages->count < 1 ) { ERROR("Expected at least 1 pfn in PAGE_DATA record"); goto err; } else if ( rec->length < sizeof(*pages) + (pages->count * sizeof(uint64_t)) ) { ERROR("PAGE_DATA record (length %u) too short to contain %u" " pfns worth of information", rec->length, pages->count); goto err; } pfns = malloc(pages->count * sizeof(*pfns)); types = malloc(pages->count * sizeof(*types)); if ( !pfns || !types ) { ERROR("Unable to allocate enough memory for %u pfns", pages->count); goto err; } for ( i = 0; i < pages->count; ++i ) { pfn = pages->pfn[i] & PAGE_DATA_PFN_MASK; if ( !ctx->restore.ops.pfn_is_valid(ctx, pfn) ) { ERROR("pfn %#"PRIpfn" (index %u) outside domain maximum", pfn, i); goto err; } type = (pages->pfn[i] & PAGE_DATA_TYPE_MASK) >> 32; if ( ((type >> XEN_DOMCTL_PFINFO_LTAB_SHIFT) >= 5) && ((type >> XEN_DOMCTL_PFINFO_LTAB_SHIFT) <= 8) ) { ERROR("Invalid type %#"PRIx32" for pfn %#"PRIpfn" (index %u)", type, pfn, i); goto err; } else if ( type < XEN_DOMCTL_PFINFO_BROKEN ) /* NOTAB and all L1 through L4 tables (including pinned) should * have a page worth of data in the record. */ pages_of_data++; pfns[i] = pfn; types[i] = type; } if ( rec->length != (sizeof(*pages) + (sizeof(uint64_t) * pages->count) + (PAGE_SIZE * pages_of_data)) ) { ERROR("PAGE_DATA record wrong size: length %u, expected " "%zu + %zu + %lu", rec->length, sizeof(*pages), (sizeof(uint64_t) * pages->count), (PAGE_SIZE * pages_of_data)); goto err; } rc = process_page_data(ctx, pages->count, pfns, types, &pages->pfn[pages->count]); err: free(types); free(pfns); return rc; } /* * Send checkpoint dirty pfn list to primary. */ static int send_checkpoint_dirty_pfn_list(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc = -1; unsigned count, written; uint64_t i, *pfns = NULL; struct iovec *iov = NULL; xc_shadow_op_stats_t stats = { 0, ctx->restore.p2m_size }; struct xc_sr_record rec = { .type = REC_TYPE_CHECKPOINT_DIRTY_PFN_LIST, }; DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap, &ctx->restore.dirty_bitmap_hbuf); if ( xc_shadow_control( xch, ctx->domid, XEN_DOMCTL_SHADOW_OP_CLEAN, HYPERCALL_BUFFER(dirty_bitmap), ctx->restore.p2m_size, NULL, 0, &stats) != ctx->restore.p2m_size ) { PERROR("Failed to retrieve logdirty bitmap"); goto err; } for ( i = 0, count = 0; i < ctx->restore.p2m_size; i++ ) { if ( test_bit(i, dirty_bitmap) ) count++; } pfns = malloc(count * sizeof(*pfns)); if ( !pfns ) { ERROR("Unable to allocate %zu bytes of memory for dirty pfn list", count * sizeof(*pfns)); goto err; } for ( i = 0, written = 0; i < ctx->restore.p2m_size; ++i ) { if ( !test_bit(i, dirty_bitmap) ) continue; if ( written > count ) { ERROR("Dirty pfn list exceed"); goto err; } pfns[written++] = i; } /* iovec[] for writev(). */ iov = malloc(3 * sizeof(*iov)); if ( !iov ) { ERROR("Unable to allocate memory for sending dirty bitmap"); goto err; } rec.length = count * sizeof(*pfns); iov[0].iov_base = &rec.type; iov[0].iov_len = sizeof(rec.type); iov[1].iov_base = &rec.length; iov[1].iov_len = sizeof(rec.length); iov[2].iov_base = pfns; iov[2].iov_len = count * sizeof(*pfns); if ( writev_exact(ctx->restore.send_back_fd, iov, 3) ) { PERROR("Failed to write dirty bitmap to stream"); goto err; } rc = 0; err: free(pfns); free(iov); return rc; } static int process_record(struct xc_sr_context *ctx, struct xc_sr_record *rec); static int handle_checkpoint(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc = 0, ret; unsigned i; if ( !ctx->restore.checkpointed ) { ERROR("Found checkpoint in non-checkpointed stream"); rc = -1; goto err; } ret = ctx->restore.callbacks->checkpoint(ctx->restore.callbacks->data); switch ( ret ) { case XGR_CHECKPOINT_SUCCESS: break; case XGR_CHECKPOINT_FAILOVER: if ( ctx->restore.buffer_all_records ) rc = BROKEN_CHANNEL; else /* We don't have a consistent state */ rc = -1; goto err; default: /* Other fatal error */ rc = -1; goto err; } if ( ctx->restore.buffer_all_records ) { IPRINTF("All records buffered"); for ( i = 0; i < ctx->restore.buffered_rec_num; i++ ) { rc = process_record(ctx, &ctx->restore.buffered_records[i]); if ( rc ) goto err; } ctx->restore.buffered_rec_num = 0; IPRINTF("All records processed"); } else ctx->restore.buffer_all_records = true; if ( ctx->restore.checkpointed == XC_MIG_STREAM_COLO ) { #define HANDLE_CALLBACK_RETURN_VALUE(ret) \ do { \ if ( ret == 1 ) \ rc = 0; /* Success */ \ else \ { \ if ( ret == 2 ) \ rc = BROKEN_CHANNEL; \ else \ rc = -1; /* Some unspecified error */ \ goto err; \ } \ } while (0) /* COLO */ /* We need to resume guest */ rc = ctx->restore.ops.stream_complete(ctx); if ( rc ) goto err; ctx->restore.callbacks->restore_results(ctx->restore.xenstore_gfn, ctx->restore.console_gfn, ctx->restore.callbacks->data); /* Resume secondary vm */ ret = ctx->restore.callbacks->postcopy(ctx->restore.callbacks->data); HANDLE_CALLBACK_RETURN_VALUE(ret); /* Wait for a new checkpoint */ ret = ctx->restore.callbacks->wait_checkpoint( ctx->restore.callbacks->data); HANDLE_CALLBACK_RETURN_VALUE(ret); /* suspend secondary vm */ ret = ctx->restore.callbacks->suspend(ctx->restore.callbacks->data); HANDLE_CALLBACK_RETURN_VALUE(ret); #undef HANDLE_CALLBACK_RETURN_VALUE rc = send_checkpoint_dirty_pfn_list(ctx); if ( rc ) goto err; } err: return rc; } static int buffer_record(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; unsigned new_alloc_num; struct xc_sr_record *p; if ( ctx->restore.buffered_rec_num >= ctx->restore.allocated_rec_num ) { new_alloc_num = ctx->restore.allocated_rec_num + DEFAULT_BUF_RECORDS; p = realloc(ctx->restore.buffered_records, new_alloc_num * sizeof(struct xc_sr_record)); if ( !p ) { ERROR("Failed to realloc memory for buffered records"); return -1; } ctx->restore.buffered_records = p; ctx->restore.allocated_rec_num = new_alloc_num; } memcpy(&ctx->restore.buffered_records[ctx->restore.buffered_rec_num++], rec, sizeof(*rec)); return 0; } static int process_record(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; int rc = 0; switch ( rec->type ) { case REC_TYPE_END: break; case REC_TYPE_PAGE_DATA: rc = handle_page_data(ctx, rec); break; case REC_TYPE_VERIFY: DPRINTF("Verify mode enabled"); ctx->restore.verify = true; break; case REC_TYPE_CHECKPOINT: rc = handle_checkpoint(ctx); break; default: rc = ctx->restore.ops.process_record(ctx, rec); break; } free(rec->data); rec->data = NULL; return rc; } static int setup(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc; DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap, &ctx->restore.dirty_bitmap_hbuf); if ( ctx->restore.checkpointed == XC_MIG_STREAM_COLO ) { dirty_bitmap = xc_hypercall_buffer_alloc_pages(xch, dirty_bitmap, NRPAGES(bitmap_size(ctx->restore.p2m_size))); if ( !dirty_bitmap ) { ERROR("Unable to allocate memory for dirty bitmap"); rc = -1; goto err; } } rc = ctx->restore.ops.setup(ctx); if ( rc ) goto err; ctx->restore.max_populated_pfn = (32 * 1024 / 4) - 1; ctx->restore.populated_pfns = bitmap_alloc( ctx->restore.max_populated_pfn + 1); if ( !ctx->restore.populated_pfns ) { ERROR("Unable to allocate memory for populated_pfns bitmap"); rc = -1; goto err; } ctx->restore.buffered_records = malloc( DEFAULT_BUF_RECORDS * sizeof(struct xc_sr_record)); if ( !ctx->restore.buffered_records ) { ERROR("Unable to allocate memory for buffered records"); rc = -1; goto err; } ctx->restore.allocated_rec_num = DEFAULT_BUF_RECORDS; err: return rc; } static void cleanup(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; unsigned i; DECLARE_HYPERCALL_BUFFER_SHADOW(unsigned long, dirty_bitmap, &ctx->restore.dirty_bitmap_hbuf); for ( i = 0; i < ctx->restore.buffered_rec_num; i++ ) free(ctx->restore.buffered_records[i].data); if ( ctx->restore.checkpointed == XC_MIG_STREAM_COLO ) xc_hypercall_buffer_free_pages(xch, dirty_bitmap, NRPAGES(bitmap_size(ctx->restore.p2m_size))); free(ctx->restore.buffered_records); free(ctx->restore.populated_pfns); if ( ctx->restore.ops.cleanup(ctx) ) PERROR("Failed to clean up"); } /* * Restore a domain. */ static int restore(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; struct xc_sr_record rec; int rc, saved_rc = 0, saved_errno = 0; IPRINTF("Restoring domain"); rc = setup(ctx); if ( rc ) goto err; do { rc = read_record(ctx, ctx->fd, &rec); if ( rc ) { if ( ctx->restore.buffer_all_records ) goto remus_failover; else goto err; } if ( ctx->restore.buffer_all_records && rec.type != REC_TYPE_END && rec.type != REC_TYPE_CHECKPOINT ) { rc = buffer_record(ctx, &rec); if ( rc ) goto err; } else { rc = process_record(ctx, &rec); if ( rc == RECORD_NOT_PROCESSED ) { if ( rec.type & REC_TYPE_OPTIONAL ) DPRINTF("Ignoring optional record %#x (%s)", rec.type, rec_type_to_str(rec.type)); else { ERROR("Mandatory record %#x (%s) not handled", rec.type, rec_type_to_str(rec.type)); rc = -1; goto err; } } else if ( rc == BROKEN_CHANNEL ) goto remus_failover; else if ( rc ) goto err; } } while ( rec.type != REC_TYPE_END ); remus_failover: if ( ctx->restore.checkpointed == XC_MIG_STREAM_COLO ) { /* With COLO, we have already called stream_complete */ rc = 0; IPRINTF("COLO Failover"); goto done; } /* * With Remus, if we reach here, there must be some error on primary, * failover from the last checkpoint state. */ rc = ctx->restore.ops.stream_complete(ctx); if ( rc ) goto err; IPRINTF("Restore successful"); goto done; err: saved_errno = errno; saved_rc = rc; PERROR("Restore failed"); done: cleanup(ctx); if ( saved_rc ) { rc = saved_rc; errno = saved_errno; } return rc; } int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom, unsigned int store_evtchn, unsigned long *store_mfn, domid_t store_domid, unsigned int console_evtchn, unsigned long *console_gfn, domid_t console_domid, unsigned int hvm, unsigned int pae, int superpages, xc_migration_stream_t stream_type, struct restore_callbacks *callbacks, int send_back_fd) { xen_pfn_t nr_pfns; struct xc_sr_context ctx = { .xch = xch, .fd = io_fd, }; /* GCC 4.4 (of CentOS 6.x vintage) can' t initialise anonymous unions. */ ctx.restore.console_evtchn = console_evtchn; ctx.restore.console_domid = console_domid; ctx.restore.xenstore_evtchn = store_evtchn; ctx.restore.xenstore_domid = store_domid; ctx.restore.checkpointed = stream_type; ctx.restore.callbacks = callbacks; ctx.restore.send_back_fd = send_back_fd; /* Sanity checks for callbacks. */ if ( stream_type ) assert(callbacks->checkpoint); if ( ctx.restore.checkpointed == XC_MIG_STREAM_COLO ) { /* this is COLO restore */ assert(callbacks->suspend && callbacks->postcopy && callbacks->wait_checkpoint && callbacks->restore_results); } DPRINTF("fd %d, dom %u, hvm %u, pae %u, superpages %d" ", stream_type %d", io_fd, dom, hvm, pae, superpages, stream_type); if ( xc_domain_getinfo(xch, dom, 1, &ctx.dominfo) != 1 ) { PERROR("Failed to get domain info"); return -1; } if ( ctx.dominfo.domid != dom ) { ERROR("Domain %u does not exist", dom); return -1; } ctx.domid = dom; if ( read_headers(&ctx) ) return -1; if ( xc_domain_nr_gpfns(xch, dom, &nr_pfns) < 0 ) { PERROR("Unable to obtain the guest p2m size"); return -1; } ctx.restore.p2m_size = nr_pfns; if ( ctx.dominfo.hvm ) { ctx.restore.ops = restore_ops_x86_hvm; if ( restore(&ctx) ) return -1; } else { ctx.restore.ops = restore_ops_x86_pv; if ( restore(&ctx) ) return -1; } IPRINTF("XenStore: mfn %#"PRIpfn", dom %d, evt %u", ctx.restore.xenstore_gfn, ctx.restore.xenstore_domid, ctx.restore.xenstore_evtchn); IPRINTF("Console: mfn %#"PRIpfn", dom %d, evt %u", ctx.restore.console_gfn, ctx.restore.console_domid, ctx.restore.console_evtchn); *console_gfn = ctx.restore.console_gfn; *store_mfn = ctx.restore.xenstore_gfn; return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xg_private.c0000664000175000017500000001155313256712137015237 0ustar smbsmb/****************************************************************************** * xg_private.c * * Helper functions for the rest of the library. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include #include #include #include "xg_private.h" char *xc_read_image(xc_interface *xch, const char *filename, unsigned long *size) { int kernel_fd = -1; gzFile kernel_gfd = NULL; char *image = NULL, *tmp; unsigned int bytes; if ( (filename == NULL) || (size == NULL) ) return NULL; if ( (kernel_fd = open(filename, O_RDONLY)) < 0 ) { PERROR("Could not open kernel image '%s'", filename); goto out; } if ( (kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL ) { PERROR("Could not allocate decompression state for state file"); goto out; } *size = 0; #define CHUNK 1*1024*1024 while(1) { if ( (tmp = realloc(image, *size + CHUNK)) == NULL ) { PERROR("Could not allocate memory for kernel image"); free(image); image = NULL; goto out; } image = tmp; bytes = gzread(kernel_gfd, image + *size, CHUNK); switch (bytes) { case -1: PERROR("Error reading kernel image"); free(image); image = NULL; goto out; case 0: /* EOF */ if ( *size == 0 ) { PERROR("Could not read kernel image"); free(image); image = NULL; } goto out; default: *size += bytes; break; } } #undef CHUNK out: if ( image ) { /* Shrink allocation to fit image. */ tmp = realloc(image, *size); if ( tmp ) image = tmp; } if ( kernel_gfd != NULL ) gzclose(kernel_gfd); else if ( kernel_fd >= 0 ) close(kernel_fd); return image; } char *xc_inflate_buffer(xc_interface *xch, const char *in_buf, unsigned long in_size, unsigned long *out_size) { int sts; z_stream zStream; unsigned long out_len; char *out_buf; /* Not compressed? Then return the original buffer. */ if ( ((unsigned char)in_buf[0] != 0x1F) || ((unsigned char)in_buf[1] != 0x8B) ) { if ( out_size != NULL ) *out_size = in_size; return (char *)in_buf; } out_len = (unsigned char)in_buf[in_size-4] + (256 * ((unsigned char)in_buf[in_size-3] + (256 * ((unsigned char)in_buf[in_size-2] + (256 * (unsigned char)in_buf[in_size-1]))))); memset(&zStream, 0, sizeof(zStream)); out_buf = malloc(out_len + 16); /* Leave a little extra space */ if ( out_buf == NULL ) { ERROR("Error mallocing buffer\n"); return NULL; } zStream.next_in = (unsigned char *)in_buf; zStream.avail_in = in_size; zStream.next_out = (unsigned char *)out_buf; zStream.avail_out = out_len+16; sts = inflateInit2(&zStream, (MAX_WBITS+32)); /* +32 means "handle gzip" */ if ( sts != Z_OK ) { ERROR("inflateInit failed, sts %d\n", sts); free(out_buf); return NULL; } /* Inflate in one pass/call */ sts = inflate(&zStream, Z_FINISH); inflateEnd(&zStream); if ( sts != Z_STREAM_END ) { ERROR("inflate failed, sts %d\n", sts); free(out_buf); return NULL; } if ( out_size != NULL ) *out_size = out_len; return out_buf; } /*******************/ int pin_table( xc_interface *xch, unsigned int type, unsigned long mfn, domid_t dom) { struct mmuext_op op; op.cmd = type; op.arg1.mfn = mfn; if ( xc_mmuext_op(xch, &op, 1, dom) < 0 ) return 1; return 0; } /* This is shared between save and restore, and may generally be useful. */ unsigned long csum_page(void *page) { int i; unsigned long *p = page; unsigned long long sum=0; for ( i = 0; i < (PAGE_SIZE/sizeof(unsigned long)); i++ ) sum += p[i]; return sum ^ (sum>>32); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_pm.c0000664000175000017500000003256313256712137014201 0ustar smbsmb/****************************************************************************** * xc_pm.c - Libxc API for Xen Power Management (Px/Cx/Tx, etc.) statistic * * Copyright (c) 2008, Liu Jinsong * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * */ #include #include "xc_private.h" #include /* * Get PM statistic info */ int xc_pm_get_max_px(xc_interface *xch, int cpuid, int *max_px) { DECLARE_SYSCTL; int ret; sysctl.cmd = XEN_SYSCTL_get_pmstat; sysctl.u.get_pmstat.type = PMSTAT_get_max_px; sysctl.u.get_pmstat.cpuid = cpuid; ret = xc_sysctl(xch, &sysctl); if ( ret ) return ret; *max_px = sysctl.u.get_pmstat.u.getpx.total; return ret; } int xc_pm_get_pxstat(xc_interface *xch, int cpuid, struct xc_px_stat *pxpt) { DECLARE_SYSCTL; /* Sizes unknown until xc_pm_get_max_px */ DECLARE_NAMED_HYPERCALL_BOUNCE(trans, pxpt->trans_pt, 0, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); DECLARE_NAMED_HYPERCALL_BOUNCE(pt, pxpt->pt, 0, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); int max_px, ret; if ( !pxpt->trans_pt || !pxpt->pt ) { errno = EINVAL; return -1; } if ( (ret = xc_pm_get_max_px(xch, cpuid, &max_px)) != 0) return ret; HYPERCALL_BOUNCE_SET_SIZE(trans, max_px * max_px * sizeof(uint64_t)); HYPERCALL_BOUNCE_SET_SIZE(pt, max_px * sizeof(struct xc_px_val)); if ( xc_hypercall_bounce_pre(xch, trans) ) return ret; if ( xc_hypercall_bounce_pre(xch, pt) ) { xc_hypercall_bounce_post(xch, trans); return ret; } sysctl.cmd = XEN_SYSCTL_get_pmstat; sysctl.u.get_pmstat.type = PMSTAT_get_pxstat; sysctl.u.get_pmstat.cpuid = cpuid; sysctl.u.get_pmstat.u.getpx.total = max_px; set_xen_guest_handle(sysctl.u.get_pmstat.u.getpx.trans_pt, trans); set_xen_guest_handle(sysctl.u.get_pmstat.u.getpx.pt, pt); ret = xc_sysctl(xch, &sysctl); if ( ret ) { xc_hypercall_bounce_post(xch, trans); xc_hypercall_bounce_post(xch, pt); return ret; } pxpt->total = sysctl.u.get_pmstat.u.getpx.total; pxpt->usable = sysctl.u.get_pmstat.u.getpx.usable; pxpt->last = sysctl.u.get_pmstat.u.getpx.last; pxpt->cur = sysctl.u.get_pmstat.u.getpx.cur; xc_hypercall_bounce_post(xch, trans); xc_hypercall_bounce_post(xch, pt); return ret; } int xc_pm_reset_pxstat(xc_interface *xch, int cpuid) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_get_pmstat; sysctl.u.get_pmstat.type = PMSTAT_reset_pxstat; sysctl.u.get_pmstat.cpuid = cpuid; return xc_sysctl(xch, &sysctl); } int xc_pm_get_max_cx(xc_interface *xch, int cpuid, int *max_cx) { DECLARE_SYSCTL; int ret = 0; sysctl.cmd = XEN_SYSCTL_get_pmstat; sysctl.u.get_pmstat.type = PMSTAT_get_max_cx; sysctl.u.get_pmstat.cpuid = cpuid; if ( (ret = xc_sysctl(xch, &sysctl)) != 0 ) return ret; *max_cx = sysctl.u.get_pmstat.u.getcx.nr; return ret; } int xc_pm_get_cxstat(xc_interface *xch, int cpuid, struct xc_cx_stat *cxpt) { DECLARE_SYSCTL; DECLARE_NAMED_HYPERCALL_BOUNCE(triggers, cxpt->triggers, cxpt->nr * sizeof(*cxpt->triggers), XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_NAMED_HYPERCALL_BOUNCE(residencies, cxpt->residencies, cxpt->nr * sizeof(*cxpt->residencies), XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_NAMED_HYPERCALL_BOUNCE(pc, cxpt->pc, cxpt->nr_pc * sizeof(*cxpt->pc), XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_NAMED_HYPERCALL_BOUNCE(cc, cxpt->cc, cxpt->nr_cc * sizeof(*cxpt->cc), XC_HYPERCALL_BUFFER_BOUNCE_OUT); int ret = -1; if ( xc_hypercall_bounce_pre(xch, triggers) ) goto unlock_0; if ( xc_hypercall_bounce_pre(xch, residencies) ) goto unlock_1; if ( xc_hypercall_bounce_pre(xch, pc) ) goto unlock_2; if ( xc_hypercall_bounce_pre(xch, cc) ) goto unlock_3; sysctl.cmd = XEN_SYSCTL_get_pmstat; sysctl.u.get_pmstat.type = PMSTAT_get_cxstat; sysctl.u.get_pmstat.cpuid = cpuid; sysctl.u.get_pmstat.u.getcx.nr = cxpt->nr; sysctl.u.get_pmstat.u.getcx.nr_pc = cxpt->nr_pc; sysctl.u.get_pmstat.u.getcx.nr_cc = cxpt->nr_cc; set_xen_guest_handle(sysctl.u.get_pmstat.u.getcx.triggers, triggers); set_xen_guest_handle(sysctl.u.get_pmstat.u.getcx.residencies, residencies); set_xen_guest_handle(sysctl.u.get_pmstat.u.getcx.pc, pc); set_xen_guest_handle(sysctl.u.get_pmstat.u.getcx.cc, cc); if ( (ret = xc_sysctl(xch, &sysctl)) ) goto unlock_4; cxpt->nr = sysctl.u.get_pmstat.u.getcx.nr; cxpt->last = sysctl.u.get_pmstat.u.getcx.last; cxpt->idle_time = sysctl.u.get_pmstat.u.getcx.idle_time; cxpt->nr_pc = sysctl.u.get_pmstat.u.getcx.nr_pc; cxpt->nr_cc = sysctl.u.get_pmstat.u.getcx.nr_cc; unlock_4: xc_hypercall_bounce_post(xch, cc); unlock_3: xc_hypercall_bounce_post(xch, pc); unlock_2: xc_hypercall_bounce_post(xch, residencies); unlock_1: xc_hypercall_bounce_post(xch, triggers); unlock_0: return ret; } int xc_pm_reset_cxstat(xc_interface *xch, int cpuid) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_get_pmstat; sysctl.u.get_pmstat.type = PMSTAT_reset_cxstat; sysctl.u.get_pmstat.cpuid = cpuid; return xc_sysctl(xch, &sysctl); } /* * 1. Get PM parameter * 2. Provide user PM control */ int xc_get_cpufreq_para(xc_interface *xch, int cpuid, struct xc_get_cpufreq_para *user_para) { DECLARE_SYSCTL; int ret = 0; struct xen_get_cpufreq_para *sys_para = &sysctl.u.pm_op.u.get_para; DECLARE_NAMED_HYPERCALL_BOUNCE(affected_cpus, user_para->affected_cpus, user_para->cpu_num * sizeof(uint32_t), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); DECLARE_NAMED_HYPERCALL_BOUNCE(scaling_available_frequencies, user_para->scaling_available_frequencies, user_para->freq_num * sizeof(uint32_t), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); DECLARE_NAMED_HYPERCALL_BOUNCE(scaling_available_governors, user_para->scaling_available_governors, user_para->gov_num * CPUFREQ_NAME_LEN * sizeof(char), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); bool has_num = user_para->cpu_num && user_para->freq_num && user_para->gov_num; if ( has_num ) { if ( (!user_para->affected_cpus) || (!user_para->scaling_available_frequencies) || (!user_para->scaling_available_governors) ) { errno = EINVAL; return -1; } if ( xc_hypercall_bounce_pre(xch, affected_cpus) ) goto unlock_1; if ( xc_hypercall_bounce_pre(xch, scaling_available_frequencies) ) goto unlock_2; if ( xc_hypercall_bounce_pre(xch, scaling_available_governors) ) goto unlock_3; set_xen_guest_handle(sys_para->affected_cpus, affected_cpus); set_xen_guest_handle(sys_para->scaling_available_frequencies, scaling_available_frequencies); set_xen_guest_handle(sys_para->scaling_available_governors, scaling_available_governors); } sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = GET_CPUFREQ_PARA; sysctl.u.pm_op.cpuid = cpuid; sys_para->cpu_num = user_para->cpu_num; sys_para->freq_num = user_para->freq_num; sys_para->gov_num = user_para->gov_num; ret = xc_sysctl(xch, &sysctl); if ( ret ) { if ( errno == EAGAIN ) { user_para->cpu_num = sys_para->cpu_num; user_para->freq_num = sys_para->freq_num; user_para->gov_num = sys_para->gov_num; ret = -errno; } if ( has_num ) goto unlock_4; goto unlock_1; } else { user_para->cpuinfo_cur_freq = sys_para->cpuinfo_cur_freq; user_para->cpuinfo_max_freq = sys_para->cpuinfo_max_freq; user_para->cpuinfo_min_freq = sys_para->cpuinfo_min_freq; user_para->scaling_cur_freq = sys_para->scaling_cur_freq; user_para->scaling_max_freq = sys_para->scaling_max_freq; user_para->scaling_min_freq = sys_para->scaling_min_freq; user_para->turbo_enabled = sys_para->turbo_enabled; memcpy(user_para->scaling_driver, sys_para->scaling_driver, CPUFREQ_NAME_LEN); memcpy(user_para->scaling_governor, sys_para->scaling_governor, CPUFREQ_NAME_LEN); /* copy to user_para no matter what cpufreq governor */ BUILD_BUG_ON(sizeof(((struct xc_get_cpufreq_para *)0)->u) != sizeof(((struct xen_get_cpufreq_para *)0)->u)); memcpy(&user_para->u, &sys_para->u, sizeof(sys_para->u)); } unlock_4: xc_hypercall_bounce_post(xch, scaling_available_governors); unlock_3: xc_hypercall_bounce_post(xch, scaling_available_frequencies); unlock_2: xc_hypercall_bounce_post(xch, affected_cpus); unlock_1: return ret; } int xc_set_cpufreq_gov(xc_interface *xch, int cpuid, char *govname) { DECLARE_SYSCTL; char *scaling_governor = sysctl.u.pm_op.u.set_gov.scaling_governor; if ( !xch || !govname ) { errno = EINVAL; return -1; } sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = SET_CPUFREQ_GOV; sysctl.u.pm_op.cpuid = cpuid; strncpy(scaling_governor, govname, CPUFREQ_NAME_LEN); scaling_governor[CPUFREQ_NAME_LEN - 1] = '\0'; return xc_sysctl(xch, &sysctl); } int xc_set_cpufreq_para(xc_interface *xch, int cpuid, int ctrl_type, int ctrl_value) { DECLARE_SYSCTL; if ( !xch ) { errno = EINVAL; return -1; } sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = SET_CPUFREQ_PARA; sysctl.u.pm_op.cpuid = cpuid; sysctl.u.pm_op.u.set_para.ctrl_type = ctrl_type; sysctl.u.pm_op.u.set_para.ctrl_value = ctrl_value; return xc_sysctl(xch, &sysctl); } int xc_get_cpufreq_avgfreq(xc_interface *xch, int cpuid, int *avg_freq) { int ret = 0; DECLARE_SYSCTL; if ( !xch || !avg_freq ) { errno = EINVAL; return -1; } sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = GET_CPUFREQ_AVGFREQ; sysctl.u.pm_op.cpuid = cpuid; ret = xc_sysctl(xch, &sysctl); *avg_freq = sysctl.u.pm_op.u.get_avgfreq; return ret; } /* value: 0 - disable sched_smt_power_savings 1 - enable sched_smt_power_savings */ int xc_set_sched_opt_smt(xc_interface *xch, uint32_t value) { int rc; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_set_sched_opt_smt; sysctl.u.pm_op.cpuid = 0; sysctl.u.pm_op.u.set_sched_opt_smt = value; rc = do_sysctl(xch, &sysctl); return rc; } int xc_set_vcpu_migration_delay(xc_interface *xch, uint32_t value) { int rc; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_set_vcpu_migration_delay; sysctl.u.pm_op.cpuid = 0; sysctl.u.pm_op.u.set_vcpu_migration_delay = value; rc = do_sysctl(xch, &sysctl); return rc; } int xc_get_vcpu_migration_delay(xc_interface *xch, uint32_t *value) { int rc; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_get_vcpu_migration_delay; sysctl.u.pm_op.cpuid = 0; rc = do_sysctl(xch, &sysctl); if (!rc && value) *value = sysctl.u.pm_op.u.get_vcpu_migration_delay; return rc; } int xc_get_cpuidle_max_cstate(xc_interface *xch, uint32_t *value) { int rc; DECLARE_SYSCTL; if ( !xch || !value ) { errno = EINVAL; return -1; } sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_get_max_cstate; sysctl.u.pm_op.cpuid = 0; sysctl.u.pm_op.u.get_max_cstate = 0; rc = do_sysctl(xch, &sysctl); *value = sysctl.u.pm_op.u.get_max_cstate; return rc; } int xc_set_cpuidle_max_cstate(xc_interface *xch, uint32_t value) { DECLARE_SYSCTL; if ( !xch ) { errno = EINVAL; return -1; } sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_set_max_cstate; sysctl.u.pm_op.cpuid = 0; sysctl.u.pm_op.u.set_max_cstate = value; return do_sysctl(xch, &sysctl); } int xc_enable_turbo(xc_interface *xch, int cpuid) { DECLARE_SYSCTL; if ( !xch ) { errno = EINVAL; return -1; } sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_enable_turbo; sysctl.u.pm_op.cpuid = cpuid; return do_sysctl(xch, &sysctl); } int xc_disable_turbo(xc_interface *xch, int cpuid) { DECLARE_SYSCTL; if ( !xch ) { errno = EINVAL; return -1; } sysctl.cmd = XEN_SYSCTL_pm_op; sysctl.u.pm_op.cmd = XEN_SYSCTL_pm_op_disable_turbo; sysctl.u.pm_op.cpuid = cpuid; return do_sysctl(xch, &sysctl); } xen-4.9.2/tools/libxc/xc_msr_x86.h0000664000175000017500000000173313256712137015073 0ustar smbsmb/* * xc_msr_x86.h * * MSR definition macros * * Copyright (C) 2014 Intel Corporation * Author Dongxiao Xu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef XC_MSR_X86_H #define XC_MSR_X86_H #define MSR_IA32_TSC 0x00000010 #define MSR_IA32_CMT_EVTSEL 0x00000c8d #define MSR_IA32_CMT_CTR 0x00000c8e #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_vm_event.c0000664000175000017500000001046413256712137015404 0ustar smbsmb/****************************************************************************** * * xc_vm_event.c * * Interface to low-level memory event functionality. * * Copyright (c) 2009 Citrix Systems, Inc. (Patrick Colp) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" int xc_vm_event_control(xc_interface *xch, domid_t domain_id, unsigned int op, unsigned int mode, uint32_t *port) { DECLARE_DOMCTL; int rc; domctl.cmd = XEN_DOMCTL_vm_event_op; domctl.domain = domain_id; domctl.u.vm_event_op.op = op; domctl.u.vm_event_op.mode = mode; rc = do_domctl(xch, &domctl); if ( !rc && port ) *port = domctl.u.vm_event_op.port; return rc; } void *xc_vm_event_enable(xc_interface *xch, domid_t domain_id, int param, uint32_t *port) { void *ring_page = NULL; uint64_t pfn; xen_pfn_t ring_pfn, mmap_pfn; unsigned int op, mode; int rc1, rc2, saved_errno; if ( !port ) { errno = EINVAL; return NULL; } /* Pause the domain for ring page setup */ rc1 = xc_domain_pause(xch, domain_id); if ( rc1 != 0 ) { PERROR("Unable to pause domain\n"); return NULL; } /* Get the pfn of the ring page */ rc1 = xc_hvm_param_get(xch, domain_id, param, &pfn); if ( rc1 != 0 ) { PERROR("Failed to get pfn of ring page\n"); goto out; } ring_pfn = pfn; mmap_pfn = pfn; rc1 = xc_get_pfn_type_batch(xch, domain_id, 1, &mmap_pfn); if ( rc1 || mmap_pfn & XEN_DOMCTL_PFINFO_XTAB ) { /* Page not in the physmap, try to populate it */ rc1 = xc_domain_populate_physmap_exact(xch, domain_id, 1, 0, 0, &ring_pfn); if ( rc1 != 0 ) { PERROR("Failed to populate ring pfn\n"); goto out; } } mmap_pfn = ring_pfn; ring_page = xc_map_foreign_pages(xch, domain_id, PROT_READ | PROT_WRITE, &mmap_pfn, 1); if ( !ring_page ) { PERROR("Could not map the ring page\n"); goto out; } switch ( param ) { case HVM_PARAM_PAGING_RING_PFN: op = XEN_VM_EVENT_ENABLE; mode = XEN_DOMCTL_VM_EVENT_OP_PAGING; break; case HVM_PARAM_MONITOR_RING_PFN: op = XEN_VM_EVENT_ENABLE; mode = XEN_DOMCTL_VM_EVENT_OP_MONITOR; break; case HVM_PARAM_SHARING_RING_PFN: op = XEN_VM_EVENT_ENABLE; mode = XEN_DOMCTL_VM_EVENT_OP_SHARING; break; /* * This is for the outside chance that the HVM_PARAM is valid but is invalid * as far as vm_event goes. */ default: errno = EINVAL; rc1 = -1; goto out; } rc1 = xc_vm_event_control(xch, domain_id, op, mode, port); if ( rc1 != 0 ) { PERROR("Failed to enable vm_event\n"); goto out; } /* Remove the ring_pfn from the guest's physmap */ rc1 = xc_domain_decrease_reservation_exact(xch, domain_id, 1, 0, &ring_pfn); if ( rc1 != 0 ) PERROR("Failed to remove ring page from guest physmap"); out: saved_errno = errno; rc2 = xc_domain_unpause(xch, domain_id); if ( rc1 != 0 || rc2 != 0 ) { if ( rc2 != 0 ) { if ( rc1 == 0 ) saved_errno = errno; PERROR("Unable to unpause domain"); } if ( ring_page ) xenforeignmemory_unmap(xch->fmem, ring_page, 1); ring_page = NULL; errno = saved_errno; } return ring_page; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_tmem.c0000664000175000017500000003570313256712137014526 0ustar smbsmb/****************************************************************************** * xc_tmem.c * * Copyright (C) 2008 Oracle Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include #include #include int xc_tmem_control(xc_interface *xch, int32_t pool_id, uint32_t cmd, uint32_t cli_id, uint32_t len, uint32_t arg, void *buf) { DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(buf, len, XC_HYPERCALL_BUFFER_BOUNCE_OUT); int rc; sysctl.cmd = XEN_SYSCTL_tmem_op; sysctl.u.tmem_op.pool_id = pool_id; sysctl.u.tmem_op.cmd = cmd; sysctl.u.tmem_op.cli_id = cli_id; sysctl.u.tmem_op.len = len; sysctl.u.tmem_op.arg = arg; sysctl.u.tmem_op.pad = 0; sysctl.u.tmem_op.oid.oid[0] = 0; sysctl.u.tmem_op.oid.oid[1] = 0; sysctl.u.tmem_op.oid.oid[2] = 0; if ( cmd == XEN_SYSCTL_TMEM_OP_SET_CLIENT_INFO || cmd == XEN_SYSCTL_TMEM_OP_SET_AUTH ) HYPERCALL_BOUNCE_SET_DIR(buf, XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( len ) { if ( buf == NULL ) { errno = EINVAL; return -1; } if ( xc_hypercall_bounce_pre(xch, buf) ) { PERROR("Could not bounce buffer for tmem control hypercall"); return -1; } } set_xen_guest_handle(sysctl.u.tmem_op.u.buf, buf); rc = do_sysctl(xch, &sysctl); if ( len ) xc_hypercall_bounce_post(xch, buf); return rc; } int xc_tmem_control_oid(xc_interface *xch, int32_t pool_id, uint32_t cmd, uint32_t cli_id, uint32_t len, uint32_t arg, struct xen_tmem_oid oid, void *buf) { DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(buf, len, XC_HYPERCALL_BUFFER_BOUNCE_OUT); int rc; sysctl.cmd = XEN_SYSCTL_tmem_op; sysctl.u.tmem_op.pool_id = pool_id; sysctl.u.tmem_op.cmd = cmd; sysctl.u.tmem_op.cli_id = cli_id; sysctl.u.tmem_op.len = len; sysctl.u.tmem_op.arg = arg; sysctl.u.tmem_op.pad = 0; sysctl.u.tmem_op.oid = oid; if ( len ) { if ( buf == NULL ) { errno = EINVAL; return -1; } if ( xc_hypercall_bounce_pre(xch, buf) ) { PERROR("Could not bounce buffer for tmem control (OID) hypercall"); return -1; } } set_xen_guest_handle(sysctl.u.tmem_op.u.buf, buf); rc = do_sysctl(xch, &sysctl); if ( len ) xc_hypercall_bounce_post(xch, buf); return rc; } static int xc_tmem_uuid_parse(char *uuid_str, uint64_t *uuid_lo, uint64_t *uuid_hi) { char *p = uuid_str; uint64_t *x = uuid_hi; int i = 0, digit; *uuid_lo = 0; *uuid_hi = 0; for ( p = uuid_str, i = 0; i != 36 && *p != '\0'; p++, i++ ) { if ( (i == 8 || i == 13 || i == 18 || i == 23) ) { if ( *p != '-' ) return -1; if ( i == 18 ) x = uuid_lo; continue; } else if ( *p >= '0' && *p <= '9' ) digit = *p - '0'; else if ( *p >= 'A' && *p <= 'F' ) digit = *p - 'A' + 10; else if ( *p >= 'a' && *p <= 'f' ) digit = *p - 'a' + 10; else return -1; *x = (*x << 4) | digit; } if ( (i != 1 && i != 36) || *p != '\0' ) return -1; return 0; } int xc_tmem_auth(xc_interface *xch, int cli_id, char *uuid_str, int enable) { xen_tmem_pool_info_t pool = { .flags.u.auth = enable, .id = 0, .n_pages = 0, .uuid[0] = 0, .uuid[1] = 0, }; if ( xc_tmem_uuid_parse(uuid_str, &pool.uuid[0], &pool.uuid[1]) < 0 ) { PERROR("Can't parse uuid, use xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"); return -1; } return xc_tmem_control(xch, 0 /* pool_id */, XEN_SYSCTL_TMEM_OP_SET_AUTH, cli_id, sizeof(pool), 0 /* arg */, &pool); } /* Save/restore/live migrate */ /* Note that live migration complicates the save/restore format in multiple ways: Though saving/migration can only occur when all tmem pools belonging to the domain-being-saved are frozen and this ensures that new pools can't be created or existing pools grown (in number of pages), it is possible during a live migration that pools may be destroyed and pages invalidated while the migration is in process. As a result, (1) it is not safe to pre-specify counts for these values precisely, but only as a "max", and (2) a "invalidation" list (of pools, objects, pages) must be appended when the domain is truly suspended. */ /* returns 0 if nothing to save, -1 if error saving, 1 if saved successfully */ int xc_tmem_save(xc_interface *xch, int dom, int io_fd, int live, int field_marker) { int marker = field_marker; int i, j, rc; uint32_t minusone = -1; struct tmem_handle *h; xen_tmem_client_t info; xen_tmem_pool_info_t *pools; char *buf = NULL; rc = xc_tmem_control(xch, 0, XEN_SYSCTL_TMEM_OP_SAVE_BEGIN, dom, 0 /* len*/ , live, NULL); if ( rc ) { /* Nothing to save - no tmem enabled. */ if ( errno == ENOENT ) return 0; return rc; } if ( xc_tmem_control(xch, 0 /* pool_id */, XEN_SYSCTL_TMEM_OP_GET_CLIENT_INFO, dom /* cli_id */, sizeof(info), 0 /* arg */, &info) < 0 ) return -1; /* Nothing to do. */ if ( !info.nr_pools ) return 0; pools = calloc(info.nr_pools, sizeof(*pools)); if ( !pools ) return -1; rc = xc_tmem_control(xch, 0 /* pool_id is ignored. */, XEN_SYSCTL_TMEM_OP_GET_POOLS, dom /* cli_id */, sizeof(*pools) * info.nr_pools, 0 /* arg */, pools); if ( rc < 0 || (uint32_t)rc > info.nr_pools ) goto out_memory; /* Update it - as we have less pools between the two hypercalls. */ info.nr_pools = (uint32_t)rc; if ( write_exact(io_fd, &marker, sizeof(marker)) ) goto out_memory; if ( write_exact(io_fd, &info, sizeof(info)) ) goto out_memory; if ( write_exact(io_fd, &minusone, sizeof(minusone)) ) goto out_memory; for ( i = 0; i < info.nr_pools; i++ ) { uint32_t pagesize; int bufsize = 0; int checksum = 0; xen_tmem_pool_info_t *pool = &pools[i]; if ( pool->flags.raw != -1 ) { if ( !pool->flags.u.persist ) pool->n_pages = 0; if ( write_exact(io_fd, pool, sizeof(*pool)) ) goto out_memory; if ( !pool->flags.u.persist ) continue; pagesize = 1 << (pool->flags.u.pagebits + 12); if ( pagesize > bufsize ) { bufsize = pagesize + sizeof(struct tmem_handle); if ( (buf = realloc(buf,bufsize)) == NULL ) goto out_memory; } for ( j = pool->n_pages; j > 0; j-- ) { int ret; if ( (ret = xc_tmem_control(xch, pool->id, XEN_SYSCTL_TMEM_OP_SAVE_GET_NEXT_PAGE, dom, bufsize, 0, buf)) > 0 ) { h = (struct tmem_handle *)buf; if ( write_exact(io_fd, &h->oid, sizeof(h->oid)) ) goto out_memory; if ( write_exact(io_fd, &h->index, sizeof(h->index)) ) goto out_memory; h++; checksum += *(char *)h; if ( write_exact(io_fd, h, pagesize) ) goto out_memory; } else if ( ret == 0 ) { continue; } else { /* page list terminator */ h = (struct tmem_handle *)buf; h->oid.oid[0] = h->oid.oid[1] = h->oid.oid[2] = -1L; if ( write_exact(io_fd, &h->oid, sizeof(h->oid)) ) { out_memory: free(pools); free(buf); return -1; } break; } } DPRINTF("saved %"PRId64" tmem pages for dom=%d pool=%d, checksum=%x\n", pool->n_pages - j, dom, pool->id, checksum); } } free(pools); free(buf); /* pool list terminator */ minusone = -1; if ( write_exact(io_fd, &minusone, sizeof(minusone)) ) return -1; return 1; } /* only called for live migration */ int xc_tmem_save_extra(xc_interface *xch, int dom, int io_fd, int field_marker) { struct tmem_handle handle; int marker = field_marker; uint32_t minusone; int count = 0, checksum = 0; if ( write_exact(io_fd, &marker, sizeof(marker)) ) return -1; while ( xc_tmem_control(xch, 0, XEN_SYSCTL_TMEM_OP_SAVE_GET_NEXT_INV, dom, sizeof(handle),0,&handle) > 0 ) { if ( write_exact(io_fd, &handle.pool_id, sizeof(handle.pool_id)) ) return -1; if ( write_exact(io_fd, &handle.oid, sizeof(handle.oid)) ) return -1; if ( write_exact(io_fd, &handle.index, sizeof(handle.index)) ) return -1; count++; checksum += handle.pool_id + handle.oid.oid[0] + handle.oid.oid[1] + handle.oid.oid[2] + handle.index; } if ( count ) DPRINTF("needed %d tmem invalidates, check=%d\n",count,checksum); minusone = -1; if ( write_exact(io_fd, &minusone, sizeof(minusone)) ) return -1; return 0; } /* only called for live migration */ void xc_tmem_save_done(xc_interface *xch, int dom) { xc_tmem_control(xch,0,XEN_SYSCTL_TMEM_OP_SAVE_END,dom,0,0,NULL); } /* restore routines */ static int xc_tmem_restore_new_pool( xc_interface *xch, int cli_id, uint32_t pool_id, uint32_t flags, uint64_t uuid_lo, uint64_t uuid_hi) { xen_tmem_pool_info_t pool = { .flags.raw = flags, .id = pool_id, .n_pages = 0, .uuid[0] = uuid_lo, .uuid[1] = uuid_hi, }; return xc_tmem_control(xch, pool_id, XEN_SYSCTL_TMEM_OP_SET_POOLS, cli_id, sizeof(pool), 0 /* arg */, &pool); } int xc_tmem_restore(xc_interface *xch, int dom, int io_fd) { uint32_t minusone; xen_tmem_client_t info; int checksum = 0; unsigned int i; char *buf = NULL; if ( read_exact(io_fd, &info, sizeof(info)) ) return -1; /* We would never save if there weren't any pools! */ if ( !info.nr_pools ) return -1; if ( xc_tmem_control(xch,0,XEN_SYSCTL_TMEM_OP_RESTORE_BEGIN,dom,0,0,NULL) < 0 ) return -1; if ( xc_tmem_control(xch, 0 /* pool_id */, XEN_SYSCTL_TMEM_OP_SET_CLIENT_INFO, dom /* cli_id */, sizeof(info), 0 /* arg */, &info) < 0 ) return -1; if ( read_exact(io_fd, &minusone, sizeof(minusone)) ) return -1; for ( i = 0; i < info.nr_pools; i++ ) { int bufsize = 0, pagesize; int j; xen_tmem_pool_info_t pool; if ( read_exact(io_fd, &pool, sizeof(pool)) ) goto out_memory; if ( xc_tmem_restore_new_pool(xch, dom, pool.id, pool.flags.raw, pool.uuid[0], pool.uuid[1]) < 0 ) goto out_memory; if ( pool.n_pages <= 0 ) continue; pagesize = 1 << (pool.flags.u.pagebits + 12); if ( pagesize > bufsize ) { bufsize = pagesize; if ( (buf = realloc(buf,bufsize)) == NULL ) goto out_memory; } for ( j = pool.n_pages; j > 0; j-- ) { struct xen_tmem_oid oid; uint32_t index; int rc; if ( read_exact(io_fd, &oid, sizeof(oid)) ) goto out_memory; if ( oid.oid[0] == -1L && oid.oid[1] == -1L && oid.oid[2] == -1L ) break; if ( read_exact(io_fd, &index, sizeof(index)) ) goto out_memory; if ( read_exact(io_fd, buf, pagesize) ) goto out_memory; checksum += *buf; if ( (rc = xc_tmem_control_oid(xch, pool.id, XEN_SYSCTL_TMEM_OP_RESTORE_PUT_PAGE, dom, bufsize, index, oid, buf)) <= 0 ) { DPRINTF("xc_tmem_restore: putting page failed, rc=%d\n",rc); out_memory: free(buf); return -1; } } if ( pool.n_pages ) DPRINTF("restored %"PRId64" tmem pages for dom=%d pool=%d, check=%x\n", pool.n_pages - j, dom, pool.id, checksum); } free(buf); return 0; } /* only called for live migration, must be called after suspend */ int xc_tmem_restore_extra(xc_interface *xch, int dom, int io_fd) { uint32_t pool_id; struct xen_tmem_oid oid; uint32_t index; int count = 0; int checksum = 0; while ( read_exact(io_fd, &pool_id, sizeof(pool_id)) == 0 && pool_id != -1 ) { if ( read_exact(io_fd, &oid, sizeof(oid)) ) return -1; if ( read_exact(io_fd, &index, sizeof(index)) ) return -1; if ( xc_tmem_control_oid(xch, pool_id, XEN_SYSCTL_TMEM_OP_RESTORE_FLUSH_PAGE, dom, 0,index,oid,NULL) <= 0 ) return -1; count++; checksum += pool_id + oid.oid[0] + oid.oid[1] + oid.oid[2] + index; } if ( pool_id != -1 ) return -1; if ( count ) DPRINTF("invalidated %d tmem pages, check=%d\n",count,checksum); return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_resume.c0000664000175000017500000002070213256712137015055 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include "xg_private.h" #include "xg_save_restore.h" #if defined(__i386__) || defined(__x86_64__) #include #include #include static int modify_returncode(xc_interface *xch, uint32_t domid) { vcpu_guest_context_any_t ctxt; xc_dominfo_t info; xen_capabilities_info_t caps; struct domain_info_context _dinfo = {}; struct domain_info_context *dinfo = &_dinfo; int rc; if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 || info.domid != domid ) { PERROR("Could not get domain info"); return -1; } if ( !info.shutdown || (info.shutdown_reason != SHUTDOWN_suspend) ) { ERROR("Dom %d not suspended: (shutdown %d, reason %d)", domid, info.shutdown, info.shutdown_reason); errno = EINVAL; return -1; } if ( info.hvm ) { /* HVM guests without PV drivers have no return code to modify. */ uint64_t irq = 0; xc_hvm_param_get(xch, domid, HVM_PARAM_CALLBACK_IRQ, &irq); if ( !irq ) return 0; /* HVM guests have host address width. */ if ( xc_version(xch, XENVER_capabilities, &caps) != 0 ) { PERROR("Could not get Xen capabilities"); return -1; } dinfo->guest_width = strstr(caps, "x86_64") ? 8 : 4; } else { /* Probe PV guest address width. */ if ( xc_domain_get_guest_width(xch, domid, &dinfo->guest_width) ) return -1; } if ( (rc = xc_vcpu_getcontext(xch, domid, 0, &ctxt)) != 0 ) return rc; SET_FIELD(&ctxt, user_regs.eax, 1, dinfo->guest_width); if ( (rc = xc_vcpu_setcontext(xch, domid, 0, &ctxt)) != 0 ) return rc; return 0; } #else static int modify_returncode(xc_interface *xch, uint32_t domid) { return 0; } #endif static int xc_domain_resume_cooperative(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; int rc; /* * Set hypercall return code to indicate that suspend is cancelled * (rather than resuming in a new domain context). */ if ( (rc = modify_returncode(xch, domid)) != 0 ) return rc; domctl.cmd = XEN_DOMCTL_resumedomain; domctl.domain = domid; return do_domctl(xch, &domctl); } #if defined(__i386__) || defined(__x86_64__) static int xc_domain_resume_hvm(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; /* * The domctl XEN_DOMCTL_resumedomain unpause each vcpu. After * the domctl, the guest will run. * * If it is PVHVM, the guest called the hypercall * SCHEDOP_shutdown:SHUTDOWN_suspend * to suspend itself. We don't modify the return code, so the PV driver * will disconnect and reconnect. * * If it is a HVM, the guest will continue running. */ domctl.cmd = XEN_DOMCTL_resumedomain; domctl.domain = domid; return do_domctl(xch, &domctl); } #endif static int xc_domain_resume_any(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; xc_dominfo_t info; int i, rc = -1; #if defined(__i386__) || defined(__x86_64__) struct domain_info_context _dinfo = { .guest_width = 0, .p2m_size = 0 }; struct domain_info_context *dinfo = &_dinfo; unsigned long mfn; vcpu_guest_context_any_t ctxt; start_info_t *start_info; shared_info_t *shinfo = NULL; xen_pfn_t *p2m_frame_list_list = NULL; xen_pfn_t *p2m_frame_list = NULL; xen_pfn_t *p2m = NULL; #endif if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 ) { PERROR("Could not get domain info"); return rc; } /* * (x86 only) Rewrite store_mfn and console_mfn back to MFN (from PFN). */ #if defined(__i386__) || defined(__x86_64__) if ( info.hvm ) return xc_domain_resume_hvm(xch, domid); if ( xc_domain_get_guest_width(xch, domid, &dinfo->guest_width) != 0 ) { PERROR("Could not get domain width"); return rc; } if ( dinfo->guest_width != sizeof(long) ) { ERROR("Cannot resume uncooperative cross-address-size guests"); return rc; } /* Map the shared info frame */ shinfo = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ, info.shared_info_frame); if ( shinfo == NULL ) { ERROR("Couldn't map shared info"); goto out; } dinfo->p2m_size = shinfo->arch.max_pfn; p2m_frame_list_list = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ, shinfo->arch.pfn_to_mfn_frame_list_list); if ( p2m_frame_list_list == NULL ) { ERROR("Couldn't map p2m_frame_list_list"); goto out; } p2m_frame_list = xc_map_foreign_pages(xch, domid, PROT_READ, p2m_frame_list_list, P2M_FLL_ENTRIES); if ( p2m_frame_list == NULL ) { ERROR("Couldn't map p2m_frame_list"); goto out; } /* Map all the frames of the pfn->mfn table. For migrate to succeed, the guest must not change which frames are used for this purpose. (its not clear why it would want to change them, and we'll be OK from a safety POV anyhow. */ p2m = xc_map_foreign_pages(xch, domid, PROT_READ, p2m_frame_list, P2M_FL_ENTRIES); if ( p2m == NULL ) { ERROR("Couldn't map p2m table"); goto out; } if ( xc_vcpu_getcontext(xch, domid, 0, &ctxt) ) { ERROR("Could not get vcpu context"); goto out; } mfn = GET_FIELD(&ctxt, user_regs.edx, dinfo->guest_width); start_info = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); if ( start_info == NULL ) { ERROR("Couldn't map start_info"); goto out; } start_info->store_mfn = p2m[start_info->store_mfn]; start_info->console.domU.mfn = p2m[start_info->console.domU.mfn]; munmap(start_info, PAGE_SIZE); #endif /* defined(__i386__) || defined(__x86_64__) */ /* Reset all secondary CPU states. */ for ( i = 1; i <= info.max_vcpu_id; i++ ) if ( xc_vcpu_setcontext(xch, domid, i, NULL) != 0 ) { ERROR("Couldn't reset vcpu state"); goto out; } /* Ready to resume domain execution now. */ domctl.cmd = XEN_DOMCTL_resumedomain; domctl.domain = domid; rc = do_domctl(xch, &domctl); out: #if defined(__i386__) || defined(__x86_64__) if (p2m) munmap(p2m, P2M_FL_ENTRIES*PAGE_SIZE); if (p2m_frame_list) munmap(p2m_frame_list, P2M_FLL_ENTRIES*PAGE_SIZE); if (p2m_frame_list_list) munmap(p2m_frame_list_list, PAGE_SIZE); if (shinfo) munmap(shinfo, PAGE_SIZE); #endif return rc; } /* * Resume execution of a domain after suspend shutdown. * This can happen in one of two ways: * 1. (fast=1) Resume the guest without resetting the domain environment. * The guests's call to SCHEDOP_shutdown(SHUTDOWN_suspend) will return 1. * * 2. (fast=0) Reset guest environment so it believes it is resumed in a new * domain context. The guests's call to SCHEDOP_shutdown(SHUTDOWN_suspend) * will return 0. * * (1) should only by used for guests which can handle the special return * code. Also note that the insertion of the return code is quite interesting * and that the guest MUST be paused - otherwise we would be corrupting * the guest vCPU state. * * (2) should be used only for guests which cannot handle the special * new return code - and it is always safe (but slower). */ int xc_domain_resume(xc_interface *xch, uint32_t domid, int fast) { return (fast ? xc_domain_resume_cooperative(xch, domid) : xc_domain_resume_any(xch, domid)); } xen-4.9.2/tools/libxc/xc_cpuid_x86.c0000664000175000017500000006375013256712137015400 0ustar smbsmb/****************************************************************************** * xc_cpuid_x86.c * * Compute cpuid of a domain. * * Copyright (c) 2008, Citrix Systems, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include #include #include #include "xc_private.h" #include "xc_bitops.h" #include #include enum { #define XEN_CPUFEATURE(name, value) X86_FEATURE_##name = value, #include }; #include "_xc_cpuid_autogen.h" #define bitmaskof(idx) (1u << ((idx) & 31)) #define featureword_of(idx) ((idx) >> 5) #define clear_feature(idx, dst) ((dst) &= ~bitmaskof(idx)) #define set_feature(idx, dst) ((dst) |= bitmaskof(idx)) #define DEF_MAX_BASE 0x0000000du #define DEF_MAX_INTELEXT 0x80000008u #define DEF_MAX_AMDEXT 0x8000001cu int xc_get_cpu_levelling_caps(xc_interface *xch, uint32_t *caps) { DECLARE_SYSCTL; int ret; sysctl.cmd = XEN_SYSCTL_get_cpu_levelling_caps; ret = do_sysctl(xch, &sysctl); if ( !ret ) *caps = sysctl.u.cpu_levelling_caps.caps; return ret; } int xc_get_cpu_featureset(xc_interface *xch, uint32_t index, uint32_t *nr_features, uint32_t *featureset) { DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(featureset, *nr_features * sizeof(*featureset), XC_HYPERCALL_BUFFER_BOUNCE_OUT); int ret; if ( xc_hypercall_bounce_pre(xch, featureset) ) return -1; sysctl.cmd = XEN_SYSCTL_get_cpu_featureset; sysctl.u.cpu_featureset.index = index; sysctl.u.cpu_featureset.nr_features = *nr_features; set_xen_guest_handle(sysctl.u.cpu_featureset.features, featureset); ret = do_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, featureset); if ( !ret ) *nr_features = sysctl.u.cpu_featureset.nr_features; return ret; } uint32_t xc_get_cpu_featureset_size(void) { return FEATURESET_NR_ENTRIES; } const uint32_t *xc_get_static_cpu_featuremask( enum xc_static_cpu_featuremask mask) { const static uint32_t known[FEATURESET_NR_ENTRIES] = INIT_KNOWN_FEATURES, special[FEATURESET_NR_ENTRIES] = INIT_SPECIAL_FEATURES, pv[FEATURESET_NR_ENTRIES] = INIT_PV_FEATURES, hvm_shadow[FEATURESET_NR_ENTRIES] = INIT_HVM_SHADOW_FEATURES, hvm_hap[FEATURESET_NR_ENTRIES] = INIT_HVM_HAP_FEATURES, deep_features[FEATURESET_NR_ENTRIES] = INIT_DEEP_FEATURES; BUILD_BUG_ON(ARRAY_SIZE(known) != FEATURESET_NR_ENTRIES); BUILD_BUG_ON(ARRAY_SIZE(special) != FEATURESET_NR_ENTRIES); BUILD_BUG_ON(ARRAY_SIZE(pv) != FEATURESET_NR_ENTRIES); BUILD_BUG_ON(ARRAY_SIZE(hvm_shadow) != FEATURESET_NR_ENTRIES); BUILD_BUG_ON(ARRAY_SIZE(hvm_hap) != FEATURESET_NR_ENTRIES); BUILD_BUG_ON(ARRAY_SIZE(deep_features) != FEATURESET_NR_ENTRIES); switch ( mask ) { case XC_FEATUREMASK_KNOWN: return known; case XC_FEATUREMASK_SPECIAL: return special; case XC_FEATUREMASK_PV: return pv; case XC_FEATUREMASK_HVM_SHADOW: return hvm_shadow; case XC_FEATUREMASK_HVM_HAP: return hvm_hap; case XC_FEATUREMASK_DEEP_FEATURES: return deep_features; default: return NULL; } } const uint32_t *xc_get_feature_deep_deps(uint32_t feature) { static const struct { uint32_t feature; uint32_t fs[FEATURESET_NR_ENTRIES]; } deep_deps[] = INIT_DEEP_DEPS; unsigned int start = 0, end = ARRAY_SIZE(deep_deps); BUILD_BUG_ON(ARRAY_SIZE(deep_deps) != NR_DEEP_DEPS); /* deep_deps[] is sorted. Perform a binary search. */ while ( start < end ) { unsigned int mid = start + ((end - start) / 2); if ( deep_deps[mid].feature > feature ) end = mid; else if ( deep_deps[mid].feature < feature ) start = mid + 1; else return deep_deps[mid].fs; } return NULL; } struct cpuid_domain_info { enum { VENDOR_UNKNOWN, VENDOR_INTEL, VENDOR_AMD, } vendor; bool hvm; uint64_t xfeature_mask; uint32_t *featureset; unsigned int nr_features; /* PV-only information. */ bool pv64; /* HVM-only information. */ bool pae; bool nestedhvm; }; static void cpuid(const unsigned int *input, unsigned int *regs) { unsigned int count = (input[1] == XEN_CPUID_INPUT_UNUSED) ? 0 : input[1]; #ifdef __i386__ /* Use the stack to avoid reg constraint failures with some gcc flags */ asm ( "push %%ebx; push %%edx\n\t" "cpuid\n\t" "mov %%ebx,4(%4)\n\t" "mov %%edx,12(%4)\n\t" "pop %%edx; pop %%ebx\n\t" : "=a" (regs[0]), "=c" (regs[2]) : "0" (input[0]), "1" (count), "S" (regs) : "memory" ); #else asm ( "cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) : "0" (input[0]), "2" (count) ); #endif } static int get_cpuid_domain_info(xc_interface *xch, domid_t domid, struct cpuid_domain_info *info, uint32_t *featureset, unsigned int nr_features) { struct xen_domctl domctl = {}; xc_dominfo_t di; unsigned int in[2] = { 0, ~0U }, regs[4]; unsigned int i, host_nr_features = xc_get_cpu_featureset_size(); int rc; cpuid(in, regs); if ( regs[1] == 0x756e6547U && /* "GenuineIntel" */ regs[2] == 0x6c65746eU && regs[3] == 0x49656e69U ) info->vendor = VENDOR_INTEL; else if ( regs[1] == 0x68747541U && /* "AuthenticAMD" */ regs[2] == 0x444d4163U && regs[3] == 0x69746e65U ) info->vendor = VENDOR_AMD; else info->vendor = VENDOR_UNKNOWN; if ( xc_domain_getinfo(xch, domid, 1, &di) != 1 || di.domid != domid ) return -ESRCH; info->hvm = di.hvm; info->featureset = calloc(host_nr_features, sizeof(*info->featureset)); if ( !info->featureset ) return -ENOMEM; info->nr_features = host_nr_features; if ( featureset ) { memcpy(info->featureset, featureset, min(host_nr_features, nr_features) * sizeof(*info->featureset)); /* Check for truncated set bits. */ for ( i = nr_features; i < host_nr_features; ++i ) if ( featureset[i] != 0 ) return -EOPNOTSUPP; } /* Get xstate information. */ domctl.cmd = XEN_DOMCTL_getvcpuextstate; domctl.domain = domid; rc = do_domctl(xch, &domctl); if ( rc ) return rc; info->xfeature_mask = domctl.u.vcpuextstate.xfeature_mask; if ( di.hvm ) { uint64_t val; rc = xc_hvm_param_get(xch, domid, HVM_PARAM_PAE_ENABLED, &val); if ( rc ) return rc; info->pae = !!val; rc = xc_hvm_param_get(xch, domid, HVM_PARAM_NESTEDHVM, &val); if ( rc ) return rc; info->nestedhvm = !!val; if ( !featureset ) { rc = xc_get_cpu_featureset(xch, XEN_SYSCTL_cpu_featureset_hvm, &host_nr_features, info->featureset); if ( rc ) return rc; } } else { unsigned int width; rc = xc_domain_get_guest_width(xch, domid, &width); if ( rc ) return rc; info->pv64 = (width == 8); if ( !featureset ) { rc = xc_get_cpu_featureset(xch, XEN_SYSCTL_cpu_featureset_pv, &host_nr_features, info->featureset); if ( rc ) return rc; } } return 0; } static void free_cpuid_domain_info(struct cpuid_domain_info *info) { free(info->featureset); } static void amd_xc_cpuid_policy(xc_interface *xch, const struct cpuid_domain_info *info, const unsigned int *input, unsigned int *regs) { switch ( input[0] ) { case 0x00000002: case 0x00000004: regs[0] = regs[1] = regs[2] = 0; break; case 0x80000000: if ( regs[0] > DEF_MAX_AMDEXT ) regs[0] = DEF_MAX_AMDEXT; break; case 0x80000008: /* * ECX[15:12] is ApicIdCoreSize: ECX[7:0] is NumberOfCores (minus one). * Update to reflect vLAPIC_ID = vCPU_ID * 2. */ regs[2] = ((regs[2] + (1u << 12)) & 0xf000u) | ((regs[2] & 0xffu) << 1) | 1u; break; case 0x8000000a: { if ( !info->nestedhvm ) { regs[0] = regs[1] = regs[2] = regs[3] = 0; break; } #define SVM_FEATURE_NPT 0x00000001 /* Nested page table support */ #define SVM_FEATURE_LBRV 0x00000002 /* LBR virtualization support */ #define SVM_FEATURE_SVML 0x00000004 /* SVM locking MSR support */ #define SVM_FEATURE_NRIPS 0x00000008 /* Next RIP save on VMEXIT */ #define SVM_FEATURE_TSCRATEMSR 0x00000010 /* TSC ratio MSR support */ #define SVM_FEATURE_VMCBCLEAN 0x00000020 /* VMCB clean bits support */ #define SVM_FEATURE_FLUSHBYASID 0x00000040 /* TLB flush by ASID support */ #define SVM_FEATURE_DECODEASSISTS 0x00000080 /* Decode assists support */ #define SVM_FEATURE_PAUSEFILTER 0x00000400 /* Pause intercept filter */ /* Pass 1: Only passthrough SVM features which are * available in hw and which are implemented */ regs[3] &= (SVM_FEATURE_NPT | SVM_FEATURE_LBRV | \ SVM_FEATURE_NRIPS | SVM_FEATURE_PAUSEFILTER | \ SVM_FEATURE_DECODEASSISTS); /* Pass 2: Always enable SVM features which are emulated */ regs[3] |= SVM_FEATURE_VMCBCLEAN | SVM_FEATURE_TSCRATEMSR; break; } } } static void intel_xc_cpuid_policy(xc_interface *xch, const struct cpuid_domain_info *info, const unsigned int *input, unsigned int *regs) { switch ( input[0] ) { case 0x00000004: /* * EAX[31:26] is Maximum Cores Per Package (minus one). * Update to reflect vLAPIC_ID = vCPU_ID * 2. */ regs[0] = (((regs[0] & 0x7c000000u) << 1) | 0x04000000u | (regs[0] & 0x3ffu)); regs[3] &= 0x3ffu; break; case 0x80000000: if ( regs[0] > DEF_MAX_INTELEXT ) regs[0] = DEF_MAX_INTELEXT; break; case 0x80000005: regs[0] = regs[1] = regs[2] = 0; break; case 0x80000008: /* Mask AMD Number of Cores information. */ regs[2] = 0; break; } } static void xc_cpuid_hvm_policy(xc_interface *xch, const struct cpuid_domain_info *info, const unsigned int *input, unsigned int *regs) { switch ( input[0] ) { case 0x00000000: if ( regs[0] > DEF_MAX_BASE ) regs[0] = DEF_MAX_BASE; break; case 0x00000001: /* * EBX[23:16] is Maximum Logical Processors Per Package. * Update to reflect vLAPIC_ID = vCPU_ID * 2. */ regs[1] = (regs[1] & 0x0000ffffu) | ((regs[1] & 0x007f0000u) << 1); regs[2] = info->featureset[featureword_of(X86_FEATURE_SSE3)]; regs[3] = (info->featureset[featureword_of(X86_FEATURE_FPU)] | bitmaskof(X86_FEATURE_HTT)); break; case 0x00000007: /* Intel-defined CPU features */ if ( input[1] == 0 ) { regs[1] = info->featureset[featureword_of(X86_FEATURE_FSGSBASE)]; regs[2] = info->featureset[featureword_of(X86_FEATURE_PREFETCHWT1)]; regs[3] = info->featureset[featureword_of(X86_FEATURE_AVX512_4VNNIW)]; } else { regs[1] = 0; regs[2] = 0; regs[3] = 0; } regs[0] = 0; break; case 0x0000000d: /* Xen automatically calculates almost everything. */ if ( input[1] == 1 ) regs[0] = info->featureset[featureword_of(X86_FEATURE_XSAVEOPT)]; else regs[0] = 0; regs[1] = regs[2] = regs[3] = 0; break; case 0x80000000: /* Passthrough to cpu vendor specific functions */ break; case 0x80000001: regs[2] = (info->featureset[featureword_of(X86_FEATURE_LAHF_LM)] & ~bitmaskof(X86_FEATURE_CMP_LEGACY)); regs[3] = info->featureset[featureword_of(X86_FEATURE_SYSCALL)]; break; case 0x80000007: /* * Keep only TSCInvariant. This may be cleared by the hypervisor * depending on guest TSC and migration settings. */ regs[0] = regs[1] = regs[2] = 0; regs[3] &= 1u<<8; break; case 0x80000008: regs[0] &= 0x0000ffffu; regs[1] = info->featureset[featureword_of(X86_FEATURE_CLZERO)]; /* regs[2] handled in the per-vendor logic. */ regs[3] = 0; break; case 0x00000002: /* Intel cache info (dumped by AMD policy) */ case 0x00000004: /* Intel cache info (dumped by AMD policy) */ case 0x0000000a: /* Architectural Performance Monitor Features */ case 0x80000002: /* Processor name string */ case 0x80000003: /* ... continued */ case 0x80000004: /* ... continued */ case 0x80000005: /* AMD L1 cache/TLB info (dumped by Intel policy) */ case 0x80000006: /* AMD L2/3 cache/TLB info ; Intel L2 cache features */ case 0x8000000a: /* AMD SVM feature bits */ case 0x80000019: /* AMD 1G TLB */ case 0x8000001a: /* AMD perf hints */ case 0x8000001c: /* AMD lightweight profiling */ break; default: regs[0] = regs[1] = regs[2] = regs[3] = 0; break; } if ( info->vendor == VENDOR_AMD ) amd_xc_cpuid_policy(xch, info, input, regs); else intel_xc_cpuid_policy(xch, info, input, regs); } static void xc_cpuid_pv_policy(xc_interface *xch, const struct cpuid_domain_info *info, const unsigned int *input, unsigned int *regs) { switch ( input[0] ) { case 0x00000000: if ( regs[0] > DEF_MAX_BASE ) regs[0] = DEF_MAX_BASE; break; case 0x00000001: { /* Host topology exposed to PV guest. Provide host value. */ bool host_htt = regs[3] & bitmaskof(X86_FEATURE_HTT); /* * Don't pick host's Initial APIC ID which can change from run * to run. */ regs[1] &= 0x00ffffffu; regs[2] = info->featureset[featureword_of(X86_FEATURE_SSE3)]; regs[3] = (info->featureset[featureword_of(X86_FEATURE_FPU)] & ~bitmaskof(X86_FEATURE_HTT)); if ( host_htt ) regs[3] |= bitmaskof(X86_FEATURE_HTT); break; } case 0x00000007: if ( input[1] == 0 ) { regs[1] = info->featureset[featureword_of(X86_FEATURE_FSGSBASE)]; regs[2] = info->featureset[featureword_of(X86_FEATURE_PREFETCHWT1)]; regs[3] = info->featureset[featureword_of(X86_FEATURE_AVX512_4VNNIW)]; } else { regs[1] = 0; regs[2] = 0; regs[3] = 0; } regs[0] = 0; break; case 0x0000000d: /* Xen automatically calculates almost everything. */ if ( input[1] == 1 ) regs[0] = info->featureset[featureword_of(X86_FEATURE_XSAVEOPT)]; else regs[0] = 0; regs[1] = regs[2] = regs[3] = 0; break; case 0x80000000: { unsigned int max = info->vendor == VENDOR_AMD ? DEF_MAX_AMDEXT : DEF_MAX_INTELEXT; if ( regs[0] > max ) regs[0] = max; break; } case 0x80000001: { /* Host topology exposed to PV guest. Provide host CMP_LEGACY value. */ bool host_cmp_legacy = regs[2] & bitmaskof(X86_FEATURE_CMP_LEGACY); regs[2] = (info->featureset[featureword_of(X86_FEATURE_LAHF_LM)] & ~bitmaskof(X86_FEATURE_CMP_LEGACY)); regs[3] = info->featureset[featureword_of(X86_FEATURE_SYSCALL)]; if ( host_cmp_legacy ) regs[2] |= bitmaskof(X86_FEATURE_CMP_LEGACY); break; } case 0x00000005: /* MONITOR/MWAIT */ case 0x0000000b: /* Extended Topology Enumeration */ case 0x8000000a: /* SVM revision and features */ case 0x8000001b: /* Instruction Based Sampling */ case 0x8000001c: /* Light Weight Profiling */ case 0x8000001e: /* Extended topology reporting */ regs[0] = regs[1] = regs[2] = regs[3] = 0; break; } } static int xc_cpuid_policy(xc_interface *xch, const struct cpuid_domain_info *info, const unsigned int *input, unsigned int *regs) { /* * For hypervisor leaves (0x4000XXXX) only 0x4000xx00.EAX[7:0] bits (max * number of leaves) can be set by user. Hypervisor will enforce this so * all other bits are don't-care and we can set them to zero. */ if ( (input[0] & 0xffff0000) == 0x40000000 ) { regs[0] = regs[1] = regs[2] = regs[3] = 0; return 0; } if ( info->hvm ) xc_cpuid_hvm_policy(xch, info, input, regs); else xc_cpuid_pv_policy(xch, info, input, regs); return 0; } static int xc_cpuid_do_domctl( xc_interface *xch, domid_t domid, const unsigned int *input, const unsigned int *regs) { DECLARE_DOMCTL; memset(&domctl, 0, sizeof (domctl)); domctl.domain = domid; domctl.cmd = XEN_DOMCTL_set_cpuid; domctl.u.cpuid.input[0] = input[0]; domctl.u.cpuid.input[1] = input[1]; domctl.u.cpuid.eax = regs[0]; domctl.u.cpuid.ebx = regs[1]; domctl.u.cpuid.ecx = regs[2]; domctl.u.cpuid.edx = regs[3]; return do_domctl(xch, &domctl); } static char *alloc_str(void) { char *s = malloc(33); if ( s == NULL ) return s; memset(s, 0, 33); return s; } void xc_cpuid_to_str(const unsigned int *regs, char **strs) { int i, j; for ( i = 0; i < 4; i++ ) { strs[i] = alloc_str(); if ( strs[i] == NULL ) continue; for ( j = 0; j < 32; j++ ) strs[i][j] = !!((regs[i] & (1U << (31 - j)))) ? '1' : '0'; } } static void sanitise_featureset(struct cpuid_domain_info *info) { const uint32_t fs_size = xc_get_cpu_featureset_size(); uint32_t disabled_features[fs_size]; static const uint32_t deep_features[] = INIT_DEEP_FEATURES; unsigned int i, b; if ( info->hvm ) { /* HVM Guest */ if ( !info->pae ) clear_bit(X86_FEATURE_PAE, info->featureset); if ( !info->nestedhvm ) { clear_bit(X86_FEATURE_SVM, info->featureset); clear_bit(X86_FEATURE_VMX, info->featureset); } } else { /* PV or PVH Guest */ if ( !info->pv64 ) { clear_bit(X86_FEATURE_LM, info->featureset); if ( info->vendor != VENDOR_AMD ) clear_bit(X86_FEATURE_SYSCALL, info->featureset); } clear_bit(X86_FEATURE_PSE, info->featureset); clear_bit(X86_FEATURE_PSE36, info->featureset); clear_bit(X86_FEATURE_PGE, info->featureset); clear_bit(X86_FEATURE_PAGE1GB, info->featureset); } if ( info->xfeature_mask == 0 ) clear_bit(X86_FEATURE_XSAVE, info->featureset); /* Disable deep dependencies of disabled features. */ for ( i = 0; i < ARRAY_SIZE(disabled_features); ++i ) disabled_features[i] = ~info->featureset[i] & deep_features[i]; for ( b = 0; b < sizeof(disabled_features) * CHAR_BIT; ++b ) { const uint32_t *dfs; if ( !test_bit(b, disabled_features) || !(dfs = xc_get_feature_deep_deps(b)) ) continue; for ( i = 0; i < ARRAY_SIZE(disabled_features); ++i ) { info->featureset[i] &= ~dfs[i]; disabled_features[i] &= ~dfs[i]; } } } int xc_cpuid_apply_policy(xc_interface *xch, domid_t domid, uint32_t *featureset, unsigned int nr_features) { struct cpuid_domain_info info = {}; unsigned int input[2] = { 0, 0 }, regs[4]; unsigned int base_max, ext_max; int rc; rc = get_cpuid_domain_info(xch, domid, &info, featureset, nr_features); if ( rc ) goto out; cpuid(input, regs); base_max = (regs[0] <= DEF_MAX_BASE) ? regs[0] : DEF_MAX_BASE; input[0] = 0x80000000; cpuid(input, regs); if ( info.vendor == VENDOR_AMD ) ext_max = (regs[0] <= DEF_MAX_AMDEXT) ? regs[0] : DEF_MAX_AMDEXT; else ext_max = (regs[0] <= DEF_MAX_INTELEXT) ? regs[0] : DEF_MAX_INTELEXT; sanitise_featureset(&info); input[0] = 0; input[1] = XEN_CPUID_INPUT_UNUSED; for ( ; ; ) { cpuid(input, regs); xc_cpuid_policy(xch, &info, input, regs); if ( regs[0] || regs[1] || regs[2] || regs[3] ) { rc = xc_cpuid_do_domctl(xch, domid, input, regs); if ( rc ) goto out; } /* Intel cache descriptor leaves. */ if ( input[0] == 4 ) { input[1]++; /* More to do? Then loop keeping %%eax==0x00000004. */ if ( (regs[0] & 0x1f) != 0 ) continue; } input[0]++; if ( !(input[0] & 0x80000000u) && (input[0] > base_max ) ) input[0] = 0x80000000u; input[1] = XEN_CPUID_INPUT_UNUSED; if ( (input[0] == 4) || (input[0] == 7) ) input[1] = 0; else if ( input[0] == 0xd ) input[1] = 1; /* Xen automatically calculates almost everything. */ if ( (input[0] & 0x80000000u) && (input[0] > ext_max) ) break; } out: free_cpuid_domain_info(&info); return rc; } /* * Check whether a VM is allowed to launch on this host's processor type. * * @config format is similar to that of xc_cpuid_set(): * '1' -> the bit must be set to 1 * '0' -> must be 0 * 'x' -> we don't care * 's' -> (same) must be the same */ int xc_cpuid_check( xc_interface *xch, const unsigned int *input, const char **config, char **config_transformed) { int i, j, rc; unsigned int regs[4]; memset(config_transformed, 0, 4 * sizeof(*config_transformed)); cpuid(input, regs); for ( i = 0; i < 4; i++ ) { if ( config[i] == NULL ) continue; config_transformed[i] = alloc_str(); if ( config_transformed[i] == NULL ) { rc = -ENOMEM; goto fail_rc; } for ( j = 0; j < 32; j++ ) { unsigned char val = !!((regs[i] & (1U << (31 - j)))); if ( !strchr("10xs", config[i][j]) || ((config[i][j] == '1') && !val) || ((config[i][j] == '0') && val) ) goto fail; config_transformed[i][j] = config[i][j]; if ( config[i][j] == 's' ) config_transformed[i][j] = '0' + val; } } return 0; fail: rc = -EPERM; fail_rc: for ( i = 0; i < 4; i++ ) { free(config_transformed[i]); config_transformed[i] = NULL; } return rc; } /* * Configure a single input with the informatiom from config. * * Config is an array of strings: * config[0] = eax * config[1] = ebx * config[2] = ecx * config[3] = edx * * The format of the string is the following: * '1' -> force to 1 * '0' -> force to 0 * 'x' -> we don't care (use default) * 'k' -> pass through host value * 's' -> pass through the first time and then keep the same value * across save/restore and migration. * * For 's' and 'x' the configuration is overwritten with the value applied. */ int xc_cpuid_set( xc_interface *xch, domid_t domid, const unsigned int *input, const char **config, char **config_transformed) { int rc; unsigned int i, j, regs[4], polregs[4]; struct cpuid_domain_info info = {}; memset(config_transformed, 0, 4 * sizeof(*config_transformed)); rc = get_cpuid_domain_info(xch, domid, &info, NULL, 0); if ( rc ) goto out; cpuid(input, regs); memcpy(polregs, regs, sizeof(regs)); xc_cpuid_policy(xch, &info, input, polregs); for ( i = 0; i < 4; i++ ) { if ( config[i] == NULL ) { regs[i] = polregs[i]; continue; } config_transformed[i] = alloc_str(); if ( config_transformed[i] == NULL ) { rc = -ENOMEM; goto fail; } for ( j = 0; j < 32; j++ ) { unsigned char val = !!((regs[i] & (1U << (31 - j)))); unsigned char polval = !!((polregs[i] & (1U << (31 - j)))); rc = -EINVAL; if ( !strchr("10xks", config[i][j]) ) goto fail; if ( config[i][j] == '1' ) val = 1; else if ( config[i][j] == '0' ) val = 0; else if ( config[i][j] == 'x' ) val = polval; if ( val ) set_feature(31 - j, regs[i]); else clear_feature(31 - j, regs[i]); config_transformed[i][j] = config[i][j]; if ( config[i][j] == 's' ) config_transformed[i][j] = '0' + val; } } rc = xc_cpuid_do_domctl(xch, domid, input, regs); if ( rc == 0 ) goto out; fail: for ( i = 0; i < 4; i++ ) { free(config_transformed[i]); config_transformed[i] = NULL; } out: free_cpuid_domain_info(&info); return rc; } xen-4.9.2/tools/libxc/xc_core_arm.c0000664000175000017500000000670213256712137015350 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (c) 2011 Citrix Systems * */ #include "xg_private.h" #include "xc_core.h" #include int xc_core_arch_gpfn_may_present(struct xc_core_arch_context *arch_ctxt, unsigned long pfn) { /* TODO: memory from DT */ if (pfn >= 0x80000 && pfn < 0x88000) return 1; return 0; } int xc_core_arch_auto_translated_physmap(const xc_dominfo_t *info) { return 1; } int xc_core_arch_memory_map_get(xc_interface *xch, struct xc_core_arch_context *unused, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xc_core_memory_map_t **mapp, unsigned int *nr_entries) { xen_pfn_t p2m_size = 0; xc_core_memory_map_t *map; if ( xc_domain_nr_gpfns(xch, info->domid, &p2m_size) < 0 ) return -1; map = malloc(sizeof(*map)); if ( map == NULL ) { PERROR("Could not allocate memory"); return -1; } map->addr = 0; map->size = ((uint64_t)p2m_size) << PAGE_SHIFT; *mapp = map; *nr_entries = 1; return 0; } static int xc_core_arch_map_p2m_rw(xc_interface *xch, struct domain_info_context *dinfo, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xen_pfn_t **live_p2m, unsigned long *pfnp, int rw) { errno = ENOSYS; return -1; } int xc_core_arch_map_p2m(xc_interface *xch, unsigned int guest_width, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xen_pfn_t **live_p2m, unsigned long *pfnp) { struct domain_info_context _dinfo = { .guest_width = guest_width }; struct domain_info_context *dinfo = &_dinfo; return xc_core_arch_map_p2m_rw(xch, dinfo, info, live_shinfo, live_p2m, pfnp, 0); } int xc_core_arch_map_p2m_writable(xc_interface *xch, unsigned int guest_width, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xen_pfn_t **live_p2m, unsigned long *pfnp) { struct domain_info_context _dinfo = { .guest_width = guest_width }; struct domain_info_context *dinfo = &_dinfo; return xc_core_arch_map_p2m_rw(xch, dinfo, info, live_shinfo, live_p2m, pfnp, 1); } int xc_core_arch_get_scratch_gpfn(xc_interface *xch, domid_t domid, xen_pfn_t *gpfn) { /* * The Grant Table region space is not used until the guest is * booting. Use the first page for the scratch pfn. */ BUILD_BUG_ON(GUEST_GNTTAB_SIZE < XC_PAGE_SIZE); *gpfn = GUEST_GNTTAB_BASE >> XC_PAGE_SHIFT; return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_solaris.c0000664000175000017500000000235713256712137015237 0ustar smbsmb/****************************************************************************** * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include /* Optionally flush file to disk and discard page cache */ void discard_file_cache(xc_interface *xch, int fd, int flush) { // TODO: Implement for Solaris! } void *xc_memalign(xc_interface *xch, size_t alignment, size_t size) { return memalign(alignment, size); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_decompress_unsafe.c0000664000175000017500000000200213256712137020112 0ustar smbsmb#include #include #include #include "xg_private.h" #include "xc_dom_decompress_unsafe.h" static struct xc_dom_image *unsafe_dom; static unsigned char *output_blob; static unsigned int output_size; static void unsafe_error(const char *msg) { xc_dom_panic(unsafe_dom->xch, XC_INVALID_KERNEL, "%s", msg); } static int unsafe_flush(void *src, unsigned int size) { void *n = realloc(output_blob, output_size + size); if (!n) return -1; output_blob = n; memcpy(&output_blob[output_size], src, size); output_size += size; return size; } int xc_dom_decompress_unsafe( decompress_fn fn, struct xc_dom_image *dom, void **blob, size_t *size) { int ret; unsafe_dom = dom; output_blob = NULL; output_size = 0; ret = fn(dom->kernel_blob, dom->kernel_size, NULL, unsafe_flush, NULL, NULL, unsafe_error); if (ret) free(output_blob); else { *blob = output_blob; *size = output_size; } return ret; } xen-4.9.2/tools/libxc/xc_altp2m.c0000664000175000017500000001441513256712137014760 0ustar smbsmb/****************************************************************************** * * xc_altp2m.c * * Interface to altp2m related HVMOPs * * Copyright (c) 2015 Tamas K Lengyel (tamas@tklengyel.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include #include int xc_altp2m_get_domain_state(xc_interface *handle, domid_t dom, bool *state) { int rc; DECLARE_HYPERCALL_BUFFER(xen_hvm_altp2m_op_t, arg); arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->version = HVMOP_ALTP2M_INTERFACE_VERSION; arg->cmd = HVMOP_altp2m_get_domain_state; arg->domain = dom; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_altp2m, HYPERCALL_BUFFER_AS_ARG(arg)); if ( !rc ) *state = arg->u.domain_state.state; xc_hypercall_buffer_free(handle, arg); return rc; } int xc_altp2m_set_domain_state(xc_interface *handle, domid_t dom, bool state) { int rc; DECLARE_HYPERCALL_BUFFER(xen_hvm_altp2m_op_t, arg); arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->version = HVMOP_ALTP2M_INTERFACE_VERSION; arg->cmd = HVMOP_altp2m_set_domain_state; arg->domain = dom; arg->u.domain_state.state = state; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_altp2m, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_buffer_free(handle, arg); return rc; } /* This is a bit odd to me that it acts on current.. */ int xc_altp2m_set_vcpu_enable_notify(xc_interface *handle, domid_t domid, uint32_t vcpuid, xen_pfn_t gfn) { int rc; DECLARE_HYPERCALL_BUFFER(xen_hvm_altp2m_op_t, arg); arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->version = HVMOP_ALTP2M_INTERFACE_VERSION; arg->cmd = HVMOP_altp2m_vcpu_enable_notify; arg->domain = domid; arg->u.enable_notify.vcpu_id = vcpuid; arg->u.enable_notify.gfn = gfn; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_altp2m, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_buffer_free(handle, arg); return rc; } int xc_altp2m_create_view(xc_interface *handle, domid_t domid, xenmem_access_t default_access, uint16_t *view_id) { int rc; DECLARE_HYPERCALL_BUFFER(xen_hvm_altp2m_op_t, arg); arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->version = HVMOP_ALTP2M_INTERFACE_VERSION; arg->cmd = HVMOP_altp2m_create_p2m; arg->domain = domid; arg->u.view.view = -1; arg->u.view.hvmmem_default_access = default_access; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_altp2m, HYPERCALL_BUFFER_AS_ARG(arg)); if ( !rc ) *view_id = arg->u.view.view; xc_hypercall_buffer_free(handle, arg); return rc; } int xc_altp2m_destroy_view(xc_interface *handle, domid_t domid, uint16_t view_id) { int rc; DECLARE_HYPERCALL_BUFFER(xen_hvm_altp2m_op_t, arg); arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->version = HVMOP_ALTP2M_INTERFACE_VERSION; arg->cmd = HVMOP_altp2m_destroy_p2m; arg->domain = domid; arg->u.view.view = view_id; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_altp2m, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_buffer_free(handle, arg); return rc; } /* Switch all vCPUs of the domain to the specified altp2m view */ int xc_altp2m_switch_to_view(xc_interface *handle, domid_t domid, uint16_t view_id) { int rc; DECLARE_HYPERCALL_BUFFER(xen_hvm_altp2m_op_t, arg); arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->version = HVMOP_ALTP2M_INTERFACE_VERSION; arg->cmd = HVMOP_altp2m_switch_p2m; arg->domain = domid; arg->u.view.view = view_id; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_altp2m, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_buffer_free(handle, arg); return rc; } int xc_altp2m_set_mem_access(xc_interface *handle, domid_t domid, uint16_t view_id, xen_pfn_t gfn, xenmem_access_t access) { int rc; DECLARE_HYPERCALL_BUFFER(xen_hvm_altp2m_op_t, arg); arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->version = HVMOP_ALTP2M_INTERFACE_VERSION; arg->cmd = HVMOP_altp2m_set_mem_access; arg->domain = domid; arg->u.set_mem_access.view = view_id; arg->u.set_mem_access.hvmmem_access = access; arg->u.set_mem_access.gfn = gfn; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_altp2m, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_buffer_free(handle, arg); return rc; } int xc_altp2m_change_gfn(xc_interface *handle, domid_t domid, uint16_t view_id, xen_pfn_t old_gfn, xen_pfn_t new_gfn) { int rc; DECLARE_HYPERCALL_BUFFER(xen_hvm_altp2m_op_t, arg); arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->version = HVMOP_ALTP2M_INTERFACE_VERSION; arg->cmd = HVMOP_altp2m_change_gfn; arg->domain = domid; arg->u.change_gfn.view = view_id; arg->u.change_gfn.old_gfn = old_gfn; arg->u.change_gfn.new_gfn = new_gfn; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_altp2m, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_buffer_free(handle, arg); return rc; } xen-4.9.2/tools/libxc/xc_resource.c0000664000175000017500000001055513256712137015411 0ustar smbsmb/* * xc_resource.c * * Generic resource access API * * Copyright (C) 2014 Intel Corporation * Author Dongxiao Xu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "xc_private.h" static int xc_resource_op_one(xc_interface *xch, xc_resource_op_t *op) { int rc; DECLARE_PLATFORM_OP; DECLARE_NAMED_HYPERCALL_BOUNCE(entries, op->entries, op->nr_entries * sizeof(*op->entries), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); if ( xc_hypercall_bounce_pre(xch, entries) ) return -1; platform_op.cmd = XENPF_resource_op; platform_op.u.resource_op.nr_entries = op->nr_entries; platform_op.u.resource_op.cpu = op->cpu; set_xen_guest_handle(platform_op.u.resource_op.entries, entries); rc = do_platform_op(xch, &platform_op); op->result = rc; xc_hypercall_bounce_post(xch, entries); return rc; } static int xc_resource_op_multi(xc_interface *xch, uint32_t nr_ops, xc_resource_op_t *ops) { int rc, i, entries_size; xc_resource_op_t *op; multicall_entry_t *call; DECLARE_HYPERCALL_BUFFER(multicall_entry_t, call_list); xc_hypercall_buffer_array_t *platform_ops, *entries_list = NULL; call_list = xc_hypercall_buffer_alloc(xch, call_list, sizeof(*call_list) * nr_ops); if ( !call_list ) return -1; platform_ops = xc_hypercall_buffer_array_create(xch, nr_ops); if ( !platform_ops ) { rc = -1; goto out; } entries_list = xc_hypercall_buffer_array_create(xch, nr_ops); if ( !entries_list ) { rc = -1; goto out; } for ( i = 0; i < nr_ops; i++ ) { DECLARE_HYPERCALL_BUFFER(xen_platform_op_t, platform_op); DECLARE_HYPERCALL_BUFFER(xc_resource_entry_t, entries); op = ops + i; platform_op = xc_hypercall_buffer_array_alloc(xch, platform_ops, i, platform_op, sizeof(xen_platform_op_t)); if ( !platform_op ) { rc = -1; goto out; } entries_size = sizeof(xc_resource_entry_t) * op->nr_entries; entries = xc_hypercall_buffer_array_alloc(xch, entries_list, i, entries, entries_size); if ( !entries) { rc = -1; goto out; } memcpy(entries, op->entries, entries_size); call = call_list + i; call->op = __HYPERVISOR_platform_op; call->args[0] = HYPERCALL_BUFFER_AS_ARG(platform_op); platform_op->interface_version = XENPF_INTERFACE_VERSION; platform_op->cmd = XENPF_resource_op; platform_op->u.resource_op.cpu = op->cpu; platform_op->u.resource_op.nr_entries = op->nr_entries; set_xen_guest_handle(platform_op->u.resource_op.entries, entries); } rc = do_multicall_op(xch, HYPERCALL_BUFFER(call_list), nr_ops); for ( i = 0; i < nr_ops; i++ ) { DECLARE_HYPERCALL_BUFFER(xc_resource_entry_t, entries); op = ops + i; call = call_list + i; op->result = call->result; entries_size = sizeof(xc_resource_entry_t) * op->nr_entries; entries = xc_hypercall_buffer_array_get(xch, entries_list, i, entries, entries_size); memcpy(op->entries, entries, entries_size); } out: xc_hypercall_buffer_array_destroy(xch, entries_list); xc_hypercall_buffer_array_destroy(xch, platform_ops); xc_hypercall_buffer_free(xch, call_list); return rc; } int xc_resource_op(xc_interface *xch, uint32_t nr_ops, xc_resource_op_t *ops) { if ( nr_ops == 1 ) return xc_resource_op_one(xch, ops); else if ( nr_ops > 1 ) return xc_resource_op_multi(xch, nr_ops, ops); else return -1; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_monitor.c0000664000175000017500000001566713256712137015262 0ustar smbsmb/****************************************************************************** * * xc_monitor.c * * Interface to VM event monitor * * Copyright (c) 2015 Tamas K Lengyel (tamas@tklengyel.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" void *xc_monitor_enable(xc_interface *xch, domid_t domain_id, uint32_t *port) { return xc_vm_event_enable(xch, domain_id, HVM_PARAM_MONITOR_RING_PFN, port); } int xc_monitor_disable(xc_interface *xch, domid_t domain_id) { return xc_vm_event_control(xch, domain_id, XEN_VM_EVENT_DISABLE, XEN_DOMCTL_VM_EVENT_OP_MONITOR, NULL); } int xc_monitor_resume(xc_interface *xch, domid_t domain_id) { return xc_vm_event_control(xch, domain_id, XEN_VM_EVENT_RESUME, XEN_DOMCTL_VM_EVENT_OP_MONITOR, NULL); } int xc_monitor_get_capabilities(xc_interface *xch, domid_t domain_id, uint32_t *capabilities) { int rc; DECLARE_DOMCTL; if ( !capabilities ) { errno = EINVAL; return -1; } domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = XEN_DOMCTL_MONITOR_OP_GET_CAPABILITIES; rc = do_domctl(xch, &domctl); if ( rc ) return rc; *capabilities = domctl.u.monitor_op.event; return 0; } int xc_monitor_write_ctrlreg(xc_interface *xch, domid_t domain_id, uint16_t index, bool enable, bool sync, bool onchangeonly) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE : XEN_DOMCTL_MONITOR_OP_DISABLE; domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_WRITE_CTRLREG; domctl.u.monitor_op.u.mov_to_cr.index = index; domctl.u.monitor_op.u.mov_to_cr.sync = sync; domctl.u.monitor_op.u.mov_to_cr.onchangeonly = onchangeonly; return do_domctl(xch, &domctl); } int xc_monitor_mov_to_msr(xc_interface *xch, domid_t domain_id, uint32_t msr, bool enable) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE : XEN_DOMCTL_MONITOR_OP_DISABLE; domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_MOV_TO_MSR; domctl.u.monitor_op.u.mov_to_msr.msr = msr; return do_domctl(xch, &domctl); } int xc_monitor_software_breakpoint(xc_interface *xch, domid_t domain_id, bool enable) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE : XEN_DOMCTL_MONITOR_OP_DISABLE; domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_SOFTWARE_BREAKPOINT; return do_domctl(xch, &domctl); } int xc_monitor_singlestep(xc_interface *xch, domid_t domain_id, bool enable) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE : XEN_DOMCTL_MONITOR_OP_DISABLE; domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_SINGLESTEP; return do_domctl(xch, &domctl); } int xc_monitor_descriptor_access(xc_interface *xch, domid_t domain_id, bool enable) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE : XEN_DOMCTL_MONITOR_OP_DISABLE; domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_DESC_ACCESS; return do_domctl(xch, &domctl); } int xc_monitor_guest_request(xc_interface *xch, domid_t domain_id, bool enable, bool sync) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE : XEN_DOMCTL_MONITOR_OP_DISABLE; domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_GUEST_REQUEST; domctl.u.monitor_op.u.guest_request.sync = sync; return do_domctl(xch, &domctl); } int xc_monitor_emulate_each_rep(xc_interface *xch, domid_t domain_id, bool enable) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = XEN_DOMCTL_MONITOR_OP_EMULATE_EACH_REP; domctl.u.monitor_op.event = enable; return do_domctl(xch, &domctl); } int xc_monitor_debug_exceptions(xc_interface *xch, domid_t domain_id, bool enable, bool sync) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE : XEN_DOMCTL_MONITOR_OP_DISABLE; domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_DEBUG_EXCEPTION; domctl.u.monitor_op.u.debug_exception.sync = sync; return do_domctl(xch, &domctl); } int xc_monitor_cpuid(xc_interface *xch, domid_t domain_id, bool enable) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE : XEN_DOMCTL_MONITOR_OP_DISABLE; domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_CPUID; return do_domctl(xch, &domctl); } int xc_monitor_privileged_call(xc_interface *xch, domid_t domain_id, bool enable) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_monitor_op; domctl.domain = domain_id; domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE : XEN_DOMCTL_MONITOR_OP_DISABLE; domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_PRIVILEGED_CALL; return do_domctl(xch, &domctl); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_mem_access.c0000664000175000017500000000612613256712137015660 0ustar smbsmb/****************************************************************************** * * tools/libxc/xc_mem_access.c * * Interface to low-level memory access mode functionality * * Copyright (c) 2011 Virtuata, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include int xc_set_mem_access(xc_interface *xch, domid_t domain_id, xenmem_access_t access, uint64_t first_pfn, uint32_t nr) { xen_mem_access_op_t mao = { .op = XENMEM_access_op_set_access, .domid = domain_id, .access = access, .pfn = first_pfn, .nr = nr }; return do_memory_op(xch, XENMEM_access_op, &mao, sizeof(mao)); } int xc_set_mem_access_multi(xc_interface *xch, domid_t domain_id, uint8_t *access, uint64_t *pages, uint32_t nr) { DECLARE_HYPERCALL_BOUNCE(access, nr, XC_HYPERCALL_BUFFER_BOUNCE_IN); DECLARE_HYPERCALL_BOUNCE(pages, nr * sizeof(uint64_t), XC_HYPERCALL_BUFFER_BOUNCE_IN); int rc; xen_mem_access_op_t mao = { .op = XENMEM_access_op_set_access_multi, .domid = domain_id, .access = XENMEM_access_default + 1, /* Invalid value */ .pfn = ~0UL, /* Invalid GFN */ .nr = nr, }; if ( xc_hypercall_bounce_pre(xch, pages) || xc_hypercall_bounce_pre(xch, access) ) { PERROR("Could not bounce memory for XENMEM_access_op_set_access_multi"); return -1; } set_xen_guest_handle(mao.pfn_list, pages); set_xen_guest_handle(mao.access_list, access); rc = do_memory_op(xch, XENMEM_access_op, &mao, sizeof(mao)); xc_hypercall_bounce_post(xch, access); xc_hypercall_bounce_post(xch, pages); return rc; } int xc_get_mem_access(xc_interface *xch, domid_t domain_id, uint64_t pfn, xenmem_access_t *access) { int rc; xen_mem_access_op_t mao = { .op = XENMEM_access_op_get_access, .domid = domain_id, .pfn = pfn }; rc = do_memory_op(xch, XENMEM_access_op, &mao, sizeof(mao)); if ( rc == 0 ) *access = mao.access; return rc; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_physdev.c0000664000175000017500000000574113256712137015245 0ustar smbsmb/****************************************************************************** * xc_physdev.c * * API for manipulating physical-device access permissions. * * Copyright (c) 2004, Rolf Neugebauer (Intel Research Cambridge) * Copyright (c) 2004, K A Fraser (University of Cambridge) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" int xc_physdev_pci_access_modify(xc_interface *xch, uint32_t domid, int bus, int dev, int func, int enable) { errno = ENOSYS; return -1; } int xc_physdev_map_pirq(xc_interface *xch, int domid, int index, int *pirq) { int rc; struct physdev_map_pirq map; if ( !pirq ) { errno = EINVAL; return -1; } memset(&map, 0, sizeof(struct physdev_map_pirq)); map.domid = domid; map.type = MAP_PIRQ_TYPE_GSI; map.index = index; map.pirq = *pirq < 0 ? index : *pirq; rc = do_physdev_op(xch, PHYSDEVOP_map_pirq, &map, sizeof(map)); if ( !rc ) *pirq = map.pirq; return rc; } int xc_physdev_map_pirq_msi(xc_interface *xch, int domid, int index, int *pirq, int devfn, int bus, int entry_nr, uint64_t table_base) { int rc; struct physdev_map_pirq map; if ( !pirq ) { errno = EINVAL; return -1; } memset(&map, 0, sizeof(struct physdev_map_pirq)); map.domid = domid; map.type = MAP_PIRQ_TYPE_MSI; map.index = index; map.pirq = *pirq; map.bus = bus; map.devfn = devfn; map.entry_nr = entry_nr; map.table_base = table_base; rc = do_physdev_op(xch, PHYSDEVOP_map_pirq, &map, sizeof(map)); if ( !rc ) *pirq = map.pirq; return rc; } int xc_physdev_unmap_pirq(xc_interface *xch, int domid, int pirq) { int rc; struct physdev_unmap_pirq unmap; memset(&unmap, 0, sizeof(struct physdev_unmap_pirq)); unmap.domid = domid; unmap.pirq = pirq; rc = do_physdev_op(xch, PHYSDEVOP_unmap_pirq, &unmap, sizeof(unmap)); return rc; } xen-4.9.2/tools/libxc/Makefile0000664000175000017500000002123713256712137014363 0ustar smbsmbXEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk MAJOR = 4.9 MINOR = 0 ifeq ($(CONFIG_LIBXC_MINIOS),y) # Save/restore of a domain is currently incompatible with a stubdom environment override CONFIG_MIGRATE := n endif CTRL_SRCS-y := CTRL_SRCS-y += xc_altp2m.c CTRL_SRCS-y += xc_core.c CTRL_SRCS-$(CONFIG_X86) += xc_core_x86.c CTRL_SRCS-$(CONFIG_ARM) += xc_core_arm.c CTRL_SRCS-y += xc_cpupool.c CTRL_SRCS-y += xc_domain.c CTRL_SRCS-y += xc_evtchn.c CTRL_SRCS-y += xc_gnttab.c CTRL_SRCS-y += xc_misc.c CTRL_SRCS-y += xc_flask.c CTRL_SRCS-y += xc_physdev.c CTRL_SRCS-y += xc_private.c CTRL_SRCS-y += xc_csched.c CTRL_SRCS-y += xc_csched2.c CTRL_SRCS-y += xc_arinc653.c CTRL_SRCS-y += xc_rt.c CTRL_SRCS-y += xc_tbuf.c CTRL_SRCS-y += xc_pm.c CTRL_SRCS-y += xc_cpu_hotplug.c CTRL_SRCS-y += xc_resume.c CTRL_SRCS-y += xc_tmem.c CTRL_SRCS-y += xc_vm_event.c CTRL_SRCS-y += xc_monitor.c CTRL_SRCS-y += xc_mem_paging.c CTRL_SRCS-y += xc_mem_access.c CTRL_SRCS-y += xc_memshr.c CTRL_SRCS-y += xc_hcall_buf.c CTRL_SRCS-y += xc_foreign_memory.c CTRL_SRCS-y += xc_kexec.c CTRL_SRCS-y += xc_resource.c CTRL_SRCS-$(CONFIG_X86) += xc_psr.c CTRL_SRCS-$(CONFIG_X86) += xc_pagetab.c CTRL_SRCS-$(CONFIG_Linux) += xc_linux.c CTRL_SRCS-$(CONFIG_FreeBSD) += xc_freebsd.c CTRL_SRCS-$(CONFIG_SunOS) += xc_solaris.c CTRL_SRCS-$(CONFIG_NetBSD) += xc_netbsd.c CTRL_SRCS-$(CONFIG_NetBSDRump) += xc_netbsd.c CTRL_SRCS-$(CONFIG_MiniOS) += xc_minios.c CTRL_SRCS-y += xc_evtchn_compat.c CTRL_SRCS-y += xc_gnttab_compat.c CTRL_SRCS-y += xc_devicemodel_compat.c GUEST_SRCS-y := GUEST_SRCS-y += xg_private.c xc_suspend.c ifeq ($(CONFIG_MIGRATE),y) GUEST_SRCS-y += xc_sr_common.c GUEST_SRCS-$(CONFIG_X86) += xc_sr_common_x86.c GUEST_SRCS-$(CONFIG_X86) += xc_sr_common_x86_pv.c GUEST_SRCS-$(CONFIG_X86) += xc_sr_restore_x86_pv.c GUEST_SRCS-$(CONFIG_X86) += xc_sr_restore_x86_hvm.c GUEST_SRCS-$(CONFIG_X86) += xc_sr_save_x86_pv.c GUEST_SRCS-$(CONFIG_X86) += xc_sr_save_x86_hvm.c GUEST_SRCS-y += xc_sr_restore.c GUEST_SRCS-y += xc_sr_save.c GUEST_SRCS-y += xc_offline_page.c xc_compression.c else GUEST_SRCS-y += xc_nomigrate.c endif vpath %.c ../../xen/common/libelf CFLAGS += -I../../xen/common/libelf ELF_SRCS-y += libelf-tools.c libelf-loader.c ELF_SRCS-y += libelf-dominfo.c GUEST_SRCS-y += $(ELF_SRCS-y) $(patsubst %.c,%.o,$(ELF_SRCS-y)): CFLAGS += -Wno-pointer-sign $(patsubst %.c,%.opic,$(ELF_SRCS-y)): CFLAGS += -Wno-pointer-sign # new domain builder GUEST_SRCS-y += xc_dom_core.c xc_dom_boot.c GUEST_SRCS-y += xc_dom_elfloader.c GUEST_SRCS-$(CONFIG_X86) += xc_dom_bzimageloader.c GUEST_SRCS-$(CONFIG_X86) += xc_dom_decompress_lz4.c GUEST_SRCS-$(CONFIG_X86) += xc_dom_hvmloader.c GUEST_SRCS-$(CONFIG_ARM) += xc_dom_armzimageloader.c GUEST_SRCS-y += xc_dom_binloader.c GUEST_SRCS-y += xc_dom_compat_linux.c GUEST_SRCS-$(CONFIG_X86) += xc_dom_x86.c GUEST_SRCS-$(CONFIG_X86) += xc_cpuid_x86.c GUEST_SRCS-$(CONFIG_ARM) += xc_dom_arm.c ifeq ($(CONFIG_LIBXC_MINIOS),y) GUEST_SRCS-y += xc_dom_decompress_unsafe.c GUEST_SRCS-y += xc_dom_decompress_unsafe_bzip2.c GUEST_SRCS-y += xc_dom_decompress_unsafe_lzma.c GUEST_SRCS-y += xc_dom_decompress_unsafe_lzo1x.c GUEST_SRCS-y += xc_dom_decompress_unsafe_xz.c endif -include $(XEN_TARGET_ARCH)/Makefile CFLAGS += -Werror -Wmissing-prototypes CFLAGS += -I. -I./include $(CFLAGS_xeninclude) CFLAGS += -D__XEN_TOOLS__ # Needed for posix_fadvise64() in xc_linux.c CFLAGS-$(CONFIG_Linux) += -D_GNU_SOURCE CFLAGS += $(PTHREAD_CFLAGS) CFLAGS += $(CFLAGS_libxentoollog) CFLAGS += $(CFLAGS_libxenevtchn) CFLAGS += $(CFLAGS_libxendevicemodel) CTRL_LIB_OBJS := $(patsubst %.c,%.o,$(CTRL_SRCS-y)) CTRL_PIC_OBJS := $(patsubst %.c,%.opic,$(CTRL_SRCS-y)) GUEST_LIB_OBJS := $(patsubst %.c,%.o,$(GUEST_SRCS-y)) GUEST_PIC_OBJS := $(patsubst %.c,%.opic,$(GUEST_SRCS-y)) $(CTRL_LIB_OBJS) $(GUEST_LIB_OBJS) \ $(CTRL_PIC_OBJS) $(GUEST_PIC_OBJS): CFLAGS += -include $(XEN_ROOT)/tools/config.h # libxenguest includes xc_private.h, so needs this despite not using # this functionality directly. $(CTRL_LIB_OBJS) $(GUEST_LIB_OBJS) \ $(CTRL_PIC_OBJS) $(GUEST_PIC_OBJS): CFLAGS += $(CFLAGS_libxencall) $(CFLAGS_libxenforeignmemory) $(CTRL_LIB_OBJS) $(CTRL_PIC_OBJS): CFLAGS += $(CFLAGS_libxengnttab) LIB := libxenctrl.a ifneq ($(nosharedlibs),y) LIB += libxenctrl.so libxenctrl.so.$(MAJOR) libxenctrl.so.$(MAJOR).$(MINOR) endif LIB += libxenguest.a ifneq ($(nosharedlibs),y) LIB += libxenguest.so libxenguest.so.$(MAJOR) libxenguest.so.$(MAJOR).$(MINOR) endif genpath-target = $(call buildmakevars2header,_paths.h) $(eval $(genpath-target)) xc_private.h: _paths.h ifeq ($(CONFIG_X86),y) _xc_cpuid_autogen.h: $(XEN_ROOT)/xen/include/public/arch-x86/cpufeatureset.h $(XEN_ROOT)/xen/tools/gen-cpuid.py $(PYTHON) $(XEN_ROOT)/xen/tools/gen-cpuid.py -i $^ -o $@.new $(call move-if-changed,$@.new,$@) build: _xc_cpuid_autogen.h endif $(CTRL_LIB_OBJS) $(GUEST_LIB_OBJS) \ $(CTRL_PIC_OBJS) $(GUEST_PIC_OBJS): xc_private.h PKG_CONFIG := xencontrol.pc xenguest.pc PKG_CONFIG_VERSION := $(MAJOR).$(MINOR) ifneq ($(CONFIG_LIBXC_MINIOS),y) PKG_CONFIG_INST := $(PKG_CONFIG) $(PKG_CONFIG_INST): PKG_CONFIG_PREFIX = $(prefix) $(PKG_CONFIG_INST): PKG_CONFIG_INCDIR = $(includedir) $(PKG_CONFIG_INST): PKG_CONFIG_LIBDIR = $(libdir) endif PKG_CONFIG_LOCAL := $(foreach pc,$(PKG_CONFIG),$(PKG_CONFIG_DIR)/$(pc)) $(PKG_CONFIG_LOCAL): PKG_CONFIG_PREFIX = $(XEN_ROOT) $(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(XEN_LIBXC)/include $(PKG_CONFIG_LOCAL): PKG_CONFIG_LIBDIR = $(CURDIR) $(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude) .PHONY: all all: build .PHONY: build build: $(MAKE) libs .PHONY: libs libs: $(LIB) $(PKG_CONFIG_INST) $(PKG_CONFIG_LOCAL) .PHONY: install install: build $(INSTALL_DIR) $(DESTDIR)$(libdir) $(INSTALL_DIR) $(DESTDIR)$(includedir) $(INSTALL_SHLIB) libxenctrl.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir) $(INSTALL_DATA) libxenctrl.a $(DESTDIR)$(libdir) $(SYMLINK_SHLIB) libxenctrl.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libxenctrl.so.$(MAJOR) $(SYMLINK_SHLIB) libxenctrl.so.$(MAJOR) $(DESTDIR)$(libdir)/libxenctrl.so $(INSTALL_DATA) include/xenctrl.h include/xenctrl_compat.h $(DESTDIR)$(includedir) $(INSTALL_SHLIB) libxenguest.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir) $(INSTALL_DATA) libxenguest.a $(DESTDIR)$(libdir) $(SYMLINK_SHLIB) libxenguest.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libxenguest.so.$(MAJOR) $(SYMLINK_SHLIB) libxenguest.so.$(MAJOR) $(DESTDIR)$(libdir)/libxenguest.so $(INSTALL_DATA) include/xenguest.h $(DESTDIR)$(includedir) $(INSTALL_DATA) xencontrol.pc $(DESTDIR)$(PKG_INSTALLDIR) $(INSTALL_DATA) xenguest.pc $(DESTDIR)$(PKG_INSTALLDIR) .PHONY: TAGS TAGS: etags -t *.c *.h .PHONY: clean clean: rm -rf *.rpm $(LIB) *~ $(DEPS) \ _paths.h \ xencontrol.pc xenguest.pc \ $(CTRL_LIB_OBJS) $(CTRL_PIC_OBJS) \ $(GUEST_LIB_OBJS) $(GUEST_PIC_OBJS) .PHONY: distclean distclean: clean .PHONY: rpm rpm: build rm -rf staging mkdir staging mkdir staging/i386 rpmbuild --define "staging$$PWD/staging" --define '_builddir.' \ --define "_rpmdir$$PWD/staging" -bb rpm.spec mv staging/i386/*.rpm . rm -rf staging # libxenctrl libxenctrl.a: $(CTRL_LIB_OBJS) $(AR) rc $@ $^ libxenctrl.so: libxenctrl.so.$(MAJOR) $(SYMLINK_SHLIB) $< $@ libxenctrl.so.$(MAJOR): libxenctrl.so.$(MAJOR).$(MINOR) $(SYMLINK_SHLIB) $< $@ libxenctrl.so.$(MAJOR).$(MINOR): $(CTRL_PIC_OBJS) $(CC) $(LDFLAGS) $(PTHREAD_LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenctrl.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LDLIBS_libxentoollog) $(LDLIBS_libxenevtchn) $(LDLIBS_libxengnttab) $(LDLIBS_libxencall) $(LDLIBS_libxenforeignmemory) $(LDLIBS_libxendevicemodel) $(PTHREAD_LIBS) $(APPEND_LDFLAGS) # libxenguest libxenguest.a: $(GUEST_LIB_OBJS) $(AR) rc $@ $^ libxenguest.so: libxenguest.so.$(MAJOR) $(SYMLINK_SHLIB) $< $@ libxenguest.so.$(MAJOR): libxenguest.so.$(MAJOR).$(MINOR) $(SYMLINK_SHLIB) $< $@ ifeq ($(CONFIG_MiniOS),y) zlib-options = else zlib-options = $(ZLIB) endif xc_dom_bzimageloader.o: CFLAGS += $(filter -D%,$(zlib-options)) xc_dom_bzimageloader.opic: CFLAGS += $(filter -D%,$(zlib-options)) libxenguest.so.$(MAJOR).$(MINOR): COMPRESSION_LIBS = $(filter -l%,$(zlib-options)) libxenguest.so.$(MAJOR).$(MINOR): $(GUEST_PIC_OBJS) libxenctrl.so $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenguest.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $(GUEST_PIC_OBJS) $(COMPRESSION_LIBS) -lz $(LDLIBS_libxenevtchn) $(LDLIBS_libxenctrl) $(PTHREAD_LIBS) $(APPEND_LDFLAGS) -include $(DEPS) xen-4.9.2/tools/libxc/xc_cpu_hotplug.c0000664000175000017500000000262013256712137016105 0ustar smbsmb/****************************************************************************** * xc_cpu_hotplug.c - Libxc API for Xen Physical CPU hotplug Management * * Copyright (c) 2008, Shan Haitao * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * */ #include "xc_private.h" int xc_cpu_online(xc_interface *xch, int cpu) { DECLARE_SYSCTL; int ret; sysctl.cmd = XEN_SYSCTL_cpu_hotplug; sysctl.u.cpu_hotplug.cpu = cpu; sysctl.u.cpu_hotplug.op = XEN_SYSCTL_CPU_HOTPLUG_ONLINE; ret = xc_sysctl(xch, &sysctl); return ret; } int xc_cpu_offline(xc_interface *xch, int cpu) { DECLARE_SYSCTL; int ret; sysctl.cmd = XEN_SYSCTL_cpu_hotplug; sysctl.u.cpu_hotplug.cpu = cpu; sysctl.u.cpu_hotplug.op = XEN_SYSCTL_CPU_HOTPLUG_OFFLINE; ret = xc_sysctl(xch, &sysctl); return ret; } xen-4.9.2/tools/libxc/xc_private.h0000664000175000017500000003402213256712137015234 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #ifndef XC_PRIVATE_H #define XC_PRIVATE_H #include #include #include #include #include #include #include #include #include #include #include #include "_paths.h" #define XC_WANT_COMPAT_MAP_FOREIGN_API #define XC_INTERNAL_COMPAT_MAP_FOREIGN_API #include "xenctrl.h" #include #include #include #include #if defined(HAVE_VALGRIND_MEMCHECK_H) && !defined(NDEBUG) && !defined(__MINIOS__) /* Compile in Valgrind client requests? */ #include #else #define VALGRIND_MAKE_MEM_UNDEFINED(addr, len) /* addr, len */ #endif #if defined(__MINIOS__) /* * MiniOS's libc doesn't know about sys/uio.h or writev(). * Declare enough of sys/uio.h to compile. */ struct iovec { void *iov_base; size_t iov_len; }; #else #include #endif #define DECLARE_DOMCTL struct xen_domctl domctl #define DECLARE_SYSCTL struct xen_sysctl sysctl #define DECLARE_PHYSDEV_OP struct physdev_op physdev_op #define DECLARE_FLASK_OP struct xen_flask_op op #define DECLARE_PLATFORM_OP struct xen_platform_op platform_op #undef PAGE_SHIFT #undef PAGE_SIZE #undef PAGE_MASK #define PAGE_SHIFT XC_PAGE_SHIFT #define PAGE_SIZE XC_PAGE_SIZE #define PAGE_MASK XC_PAGE_MASK #ifndef ARRAY_SIZE /* MiniOS leaks ARRAY_SIZE into our namespace as part of a * stubdom build. It shouldn't... */ #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #endif /* ** Define max dirty page cache to permit during save/restore -- need to balance ** keeping cache usage down with CPU impact of invalidating too often. ** (Currently 16MB) */ #define MAX_PAGECACHE_USAGE (4*1024) struct xc_interface_core { int flags; xentoollog_logger *error_handler, *error_handler_tofree; xentoollog_logger *dombuild_logger, *dombuild_logger_tofree; struct xc_error last_error; /* for xc_get_last_error */ FILE *dombuild_logger_file; const char *currently_progress_reporting; /* Hypercall interface */ xencall_handle *xcall; /* Foreign mappings */ xenforeignmemory_handle *fmem; /* Device model */ xendevicemodel_handle *dmod; }; int osdep_privcmd_open(xc_interface *xch); int osdep_privcmd_close(xc_interface *xch); void *osdep_alloc_hypercall_buffer(xc_interface *xch, int npages); void osdep_free_hypercall_buffer(xc_interface *xch, void *ptr, int npages); void xc_report_error(xc_interface *xch, int code, const char *fmt, ...) __attribute__((format(printf,3,4))); void xc_reportv(xc_interface *xch, xentoollog_logger *lg, xentoollog_level, int code, const char *fmt, va_list args) __attribute__((format(printf,5,0))); void xc_report(xc_interface *xch, xentoollog_logger *lg, xentoollog_level, int code, const char *fmt, ...) __attribute__((format(printf,5,6))); const char *xc_set_progress_prefix(xc_interface *xch, const char *doing); void xc_report_progress_single(xc_interface *xch, const char *doing); void xc_report_progress_step(xc_interface *xch, unsigned long done, unsigned long total); /* anamorphic macros: struct xc_interface *xch must be in scope */ #define IPRINTF(_f, _a...) do { int IPRINTF_errno = errno; \ xc_report(xch, xch->error_handler, XTL_INFO,0, _f , ## _a); \ errno = IPRINTF_errno; \ } while (0) #define DPRINTF(_f, _a...) do { int DPRINTF_errno = errno; \ xc_report(xch, xch->error_handler, XTL_DETAIL,0, _f , ## _a); \ errno = DPRINTF_errno; \ } while (0) #define DBGPRINTF(_f, _a...) do { int DBGPRINTF_errno = errno; \ xc_report(xch, xch->error_handler, XTL_DEBUG,0, _f , ## _a); \ errno = DBGPRINTF_errno; \ } while (0) #define ERROR(_m, _a...) do { int ERROR_errno = errno; \ xc_report_error(xch,XC_INTERNAL_ERROR,_m , ## _a ); \ errno = ERROR_errno; \ } while (0) #define PERROR(_m, _a...) do { int PERROR_errno = errno; \ xc_report_error(xch,XC_INTERNAL_ERROR,_m " (%d = %s)", \ ## _a , errno, xc_strerror(xch, errno)); \ errno = PERROR_errno; \ } while (0) /* * HYPERCALL ARGUMENT BUFFERS * * Augment the public hypercall buffer interface with the ability to * bounce between user provided buffers and hypercall safe memory. * * Use xc_hypercall_bounce_pre/post instead of * xc_hypercall_buffer_alloc/free(_pages). The specified user * supplied buffer is automatically copied in/out of the hypercall * safe memory. */ enum { XC_HYPERCALL_BUFFER_BOUNCE_NONE = 0, XC_HYPERCALL_BUFFER_BOUNCE_IN = 1, XC_HYPERCALL_BUFFER_BOUNCE_OUT = 2, XC_HYPERCALL_BUFFER_BOUNCE_BOTH = 3 }; /* * Declare a named bounce buffer. * * Normally you should use DECLARE_HYPERCALL_BOUNCE (see below). * * This declaration should only be used when the user pointer is * non-trivial, e.g. when it is contained within an existing data * structure. */ #define DECLARE_NAMED_HYPERCALL_BOUNCE(_name, _ubuf, _sz, _dir) \ xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(_name) = { \ .hbuf = NULL, \ .param_shadow = NULL, \ .sz = _sz, .dir = _dir, .ubuf = _ubuf, \ } /* * Declare a bounce buffer shadowing the named user data pointer. */ #define DECLARE_HYPERCALL_BOUNCE(_ubuf, _sz, _dir) DECLARE_NAMED_HYPERCALL_BOUNCE(_ubuf, _ubuf, _sz, _dir) /* * Set the size of data to bounce. Useful when the size is not known * when the bounce buffer is declared. */ #define HYPERCALL_BOUNCE_SET_SIZE(_buf, _sz) do { (HYPERCALL_BUFFER(_buf))->sz = _sz; } while (0) /* * Change the direction. * * Can only be used if the bounce_pre/bounce_post commands have * not been used. */ #define HYPERCALL_BOUNCE_SET_DIR(_buf, _dir) do { if ((HYPERCALL_BUFFER(_buf))->hbuf) \ assert(1); \ (HYPERCALL_BUFFER(_buf))->dir = _dir; \ } while (0) /* * Initialise and free hypercall safe memory. Takes care of any required * copying. */ int xc__hypercall_bounce_pre(xc_interface *xch, xc_hypercall_buffer_t *bounce); #define xc_hypercall_bounce_pre(_xch, _name) xc__hypercall_bounce_pre(_xch, HYPERCALL_BUFFER(_name)) void xc__hypercall_bounce_post(xc_interface *xch, xc_hypercall_buffer_t *bounce); #define xc_hypercall_bounce_post(_xch, _name) xc__hypercall_bounce_post(_xch, HYPERCALL_BUFFER(_name)) /* * Release hypercall buffer cache */ void xc__hypercall_buffer_cache_release(xc_interface *xch); /* * Hypercall interfaces. */ static inline int do_xen_version(xc_interface *xch, int cmd, xc_hypercall_buffer_t *dest) { DECLARE_HYPERCALL_BUFFER_ARGUMENT(dest); return xencall2(xch->xcall, __HYPERVISOR_xen_version, cmd, HYPERCALL_BUFFER_AS_ARG(dest)); } static inline int do_physdev_op(xc_interface *xch, int cmd, void *op, size_t len) { int ret = -1; DECLARE_HYPERCALL_BOUNCE(op, len, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); if ( xc_hypercall_bounce_pre(xch, op) ) { PERROR("Could not bounce memory for physdev hypercall"); goto out1; } ret = xencall2(xch->xcall, __HYPERVISOR_physdev_op, cmd, HYPERCALL_BUFFER_AS_ARG(op)); if ( ret < 0 ) { if ( errno == EACCES ) DPRINTF("physdev operation failed -- need to" " rebuild the user-space tool set?\n"); } xc_hypercall_bounce_post(xch, op); out1: return ret; } static inline int do_domctl(xc_interface *xch, struct xen_domctl *domctl) { int ret = -1; DECLARE_HYPERCALL_BOUNCE(domctl, sizeof(*domctl), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); domctl->interface_version = XEN_DOMCTL_INTERFACE_VERSION; if ( xc_hypercall_bounce_pre(xch, domctl) ) { PERROR("Could not bounce buffer for domctl hypercall"); goto out1; } ret = xencall1(xch->xcall, __HYPERVISOR_domctl, HYPERCALL_BUFFER_AS_ARG(domctl)); if ( ret < 0 ) { if ( errno == EACCES ) DPRINTF("domctl operation failed -- need to" " rebuild the user-space tool set?\n"); } xc_hypercall_bounce_post(xch, domctl); out1: return ret; } static inline int do_sysctl(xc_interface *xch, struct xen_sysctl *sysctl) { int ret = -1; DECLARE_HYPERCALL_BOUNCE(sysctl, sizeof(*sysctl), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); sysctl->interface_version = XEN_SYSCTL_INTERFACE_VERSION; if ( xc_hypercall_bounce_pre(xch, sysctl) ) { PERROR("Could not bounce buffer for sysctl hypercall"); goto out1; } ret = xencall1(xch->xcall, __HYPERVISOR_sysctl, HYPERCALL_BUFFER_AS_ARG(sysctl)); if ( ret < 0 ) { if ( errno == EACCES ) DPRINTF("sysctl operation failed -- need to" " rebuild the user-space tool set?\n"); } xc_hypercall_bounce_post(xch, sysctl); out1: return ret; } static inline int do_platform_op(xc_interface *xch, struct xen_platform_op *platform_op) { int ret = -1; DECLARE_HYPERCALL_BOUNCE(platform_op, sizeof(*platform_op), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); platform_op->interface_version = XENPF_INTERFACE_VERSION; if ( xc_hypercall_bounce_pre(xch, platform_op) ) { PERROR("Could not bounce buffer for platform_op hypercall"); return -1; } ret = xencall1(xch->xcall, __HYPERVISOR_platform_op, HYPERCALL_BUFFER_AS_ARG(platform_op)); if ( ret < 0 ) { if ( errno == EACCES ) DPRINTF("platform operation failed -- need to" " rebuild the user-space tool set?\n"); } xc_hypercall_bounce_post(xch, platform_op); return ret; } static inline int do_multicall_op(xc_interface *xch, xc_hypercall_buffer_t *call_list, uint32_t nr_calls) { int ret = -1; DECLARE_HYPERCALL_BUFFER_ARGUMENT(call_list); ret = xencall2(xch->xcall, __HYPERVISOR_multicall, HYPERCALL_BUFFER_AS_ARG(call_list), nr_calls); if ( ret < 0 ) { if ( errno == EACCES ) DPRINTF("multicall operation failed -- need to" " rebuild the user-space tool set?\n"); } return ret; } long do_memory_op(xc_interface *xch, int cmd, void *arg, size_t len); void *xc_map_foreign_ranges(xc_interface *xch, uint32_t dom, size_t size, int prot, size_t chunksize, privcmd_mmap_entry_t entries[], int nentries); int xc_get_pfn_type_batch(xc_interface *xch, uint32_t dom, unsigned int num, xen_pfn_t *); void bitmap_64_to_byte(uint8_t *bp, const uint64_t *lp, int nbits); void bitmap_byte_to_64(uint64_t *lp, const uint8_t *bp, int nbits); /* Optionally flush file to disk and discard page cache */ void discard_file_cache(xc_interface *xch, int fd, int flush); #define MAX_MMU_UPDATES 1024 struct xc_mmu { mmu_update_t updates[MAX_MMU_UPDATES]; int idx; unsigned int subject; }; /* Structure returned by xc_alloc_mmu_updates must be free()'ed by caller. */ struct xc_mmu *xc_alloc_mmu_updates(xc_interface *xch, unsigned int subject); int xc_add_mmu_update(xc_interface *xch, struct xc_mmu *mmu, unsigned long long ptr, unsigned long long val); int xc_flush_mmu_updates(xc_interface *xch, struct xc_mmu *mmu); /* Return 0 on success; -1 on error setting errno. */ int read_exact(int fd, void *data, size_t size); /* EOF => -1, errno=0 */ int write_exact(int fd, const void *data, size_t size); int writev_exact(int fd, const struct iovec *iov, int iovcnt); int xc_ffs8(uint8_t x); int xc_ffs16(uint16_t x); int xc_ffs32(uint32_t x); int xc_ffs64(uint64_t x); #define min(X, Y) ({ \ const typeof (X) _x = (X); \ const typeof (Y) _y = (Y); \ (void) (&_x == &_y); \ (_x < _y) ? _x : _y; }) #define max(X, Y) ({ \ const typeof (X) _x = (X); \ const typeof (Y) _y = (Y); \ (void) (&_x == &_y); \ (_x > _y) ? _x : _y; }) #define min_t(type,x,y) \ ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) #define max_t(type,x,y) \ ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) #define DOMPRINTF(fmt, args...) xc_dom_printf(dom->xch, fmt, ## args) #define DOMPRINTF_CALLED(xch) xc_dom_printf((xch), "%s: called", __FUNCTION__) /** * vm_event operations. Internal use only. */ int xc_vm_event_control(xc_interface *xch, domid_t domain_id, unsigned int op, unsigned int mode, uint32_t *port); /* * Enables vm_event and returns the mapped ring page indicated by param. * param can be HVM_PARAM_PAGING/ACCESS/SHARING_RING_PFN */ void *xc_vm_event_enable(xc_interface *xch, domid_t domain_id, int param, uint32_t *port); int do_dm_op(xc_interface *xch, domid_t domid, unsigned int nr_bufs, ...); #endif /* __XC_PRIVATE_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_bitops.h0000664000175000017500000000345613256712137015071 0ustar smbsmb#ifndef XC_BITOPS_H #define XC_BITOPS_H 1 /* bitmap operations for single threaded access */ #include #include /* Needed by several includees, but no longer used for bitops. */ #define BITS_PER_LONG (sizeof(unsigned long) * 8) #define ORDER_LONG (sizeof(unsigned long) == 4 ? 5 : 6) #define BITMAP_ENTRY(_nr,_bmap) ((_bmap))[(_nr) / 8] #define BITMAP_SHIFT(_nr) ((_nr) % 8) /* calculate required space for number of longs needed to hold nr_bits */ static inline int bitmap_size(int nr_bits) { return (nr_bits + 7) / 8; } static inline void *bitmap_alloc(int nr_bits) { return calloc(1, bitmap_size(nr_bits)); } static inline void bitmap_set(void *addr, int nr_bits) { memset(addr, 0xff, bitmap_size(nr_bits)); } static inline void bitmap_clear(void *addr, int nr_bits) { memset(addr, 0, bitmap_size(nr_bits)); } static inline int test_bit(int nr, const void *_addr) { const char *addr = _addr; return (BITMAP_ENTRY(nr, addr) >> BITMAP_SHIFT(nr)) & 1; } static inline void clear_bit(int nr, void *_addr) { char *addr = _addr; BITMAP_ENTRY(nr, addr) &= ~(1UL << BITMAP_SHIFT(nr)); } static inline void set_bit(int nr, void *_addr) { char *addr = _addr; BITMAP_ENTRY(nr, addr) |= (1UL << BITMAP_SHIFT(nr)); } static inline int test_and_clear_bit(int nr, void *addr) { int oldbit = test_bit(nr, addr); clear_bit(nr, addr); return oldbit; } static inline int test_and_set_bit(int nr, void *addr) { int oldbit = test_bit(nr, addr); set_bit(nr, addr); return oldbit; } static inline void bitmap_or(void *_dst, const void *_other, int nr_bits) { char *dst = _dst; const char *other = _other; int i; for ( i = 0; i < bitmap_size(nr_bits); ++i ) dst[i] |= other[i]; } #endif /* XC_BITOPS_H */ xen-4.9.2/tools/libxc/xc_gnttab_compat.c0000664000175000017500000000622013256712137016376 0ustar smbsmb/* * Compat shims for use of 3rd party consumers of libxenctrl xc_gnt{tab,shr} * functionality which has been split into separate libraries. */ #include #define XC_WANT_COMPAT_GNTTAB_API #include "xenctrl.h" xc_gnttab *xc_gnttab_open(xentoollog_logger *logger, unsigned open_flags) { return xengnttab_open(logger, open_flags); } int xc_gnttab_close(xc_gnttab *xcg) { return xengnttab_close(xcg); } void *xc_gnttab_map_grant_ref(xc_gnttab *xcg, uint32_t domid, uint32_t ref, int prot) { return xengnttab_map_grant_ref(xcg, domid, ref, prot); } void *xc_gnttab_map_grant_refs(xc_gnttab *xcg, uint32_t count, uint32_t *domids, uint32_t *refs, int prot) { return xengnttab_map_grant_refs(xcg, count, domids, refs, prot); } void *xc_gnttab_map_domain_grant_refs(xc_gnttab *xcg, uint32_t count, uint32_t domid, uint32_t *refs, int prot) { return xengnttab_map_domain_grant_refs(xcg, count, domid, refs, prot); } void *xc_gnttab_map_grant_ref_notify(xc_gnttab *xcg, uint32_t domid, uint32_t ref, int prot, uint32_t notify_offset, evtchn_port_t notify_port) { return xengnttab_map_grant_ref_notify(xcg, domid, ref, prot, notify_offset, notify_port); } int xc_gnttab_munmap(xc_gnttab *xcg, void *start_address, uint32_t count) { return xengnttab_unmap(xcg, start_address, count); } int xc_gnttab_set_max_grants(xc_gnttab *xcg, uint32_t count) { return xengnttab_set_max_grants(xcg, count); } xc_gntshr *xc_gntshr_open(xentoollog_logger *logger, unsigned open_flags) { return xengntshr_open(logger, open_flags); } int xc_gntshr_close(xc_gntshr *xcg) { return xengntshr_close(xcg); } void *xc_gntshr_share_pages(xc_gntshr *xcg, uint32_t domid, int count, uint32_t *refs, int writable) { return xengntshr_share_pages(xcg, domid, count, refs, writable); } void *xc_gntshr_share_page_notify(xc_gntshr *xcg, uint32_t domid, uint32_t *ref, int writable, uint32_t notify_offset, evtchn_port_t notify_port) { return xengntshr_share_page_notify(xcg, domid, ref, writable, notify_offset, notify_port); } int xc_gntshr_munmap(xc_gntshr *xcg, void *start_address, uint32_t count) { return xengntshr_unshare(xcg, start_address, count); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_rt.c0000664000175000017500000001022113256712137014175 0ustar smbsmb/**************************************************************************** * * File: xc_rt.c * Author: Sisu Xi * Meng Xu * * Description: XC Interface to the rtds scheduler * Note: VCPU's parameter (period, budget) is in microsecond (us). * All VCPUs of the same domain have same period and budget. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" int xc_sched_rtds_domain_set(xc_interface *xch, uint32_t domid, struct xen_domctl_sched_rtds *sdom) { int rc; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_scheduler_op; domctl.domain = (domid_t) domid; domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_RTDS; domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_putinfo; domctl.u.scheduler_op.u.rtds.period = sdom->period; domctl.u.scheduler_op.u.rtds.budget = sdom->budget; rc = do_domctl(xch, &domctl); return rc; } int xc_sched_rtds_domain_get(xc_interface *xch, uint32_t domid, struct xen_domctl_sched_rtds *sdom) { int rc; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_scheduler_op; domctl.domain = (domid_t) domid; domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_RTDS; domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_getinfo; rc = do_domctl(xch, &domctl); if ( rc == 0 ) *sdom = domctl.u.scheduler_op.u.rtds; return rc; } int xc_sched_rtds_vcpu_set(xc_interface *xch, uint32_t domid, struct xen_domctl_schedparam_vcpu *vcpus, uint32_t num_vcpus) { int rc = 0; unsigned processed = 0; DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(vcpus, sizeof(*vcpus) * num_vcpus, XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, vcpus) ) return -1; domctl.cmd = XEN_DOMCTL_scheduler_op; domctl.domain = (domid_t) domid; domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_RTDS; domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_putvcpuinfo; while ( processed < num_vcpus ) { domctl.u.scheduler_op.u.v.nr_vcpus = num_vcpus - processed; set_xen_guest_handle_offset(domctl.u.scheduler_op.u.v.vcpus, vcpus, processed); if ( (rc = do_domctl(xch, &domctl)) != 0 ) break; processed += domctl.u.scheduler_op.u.v.nr_vcpus; } xc_hypercall_bounce_post(xch, vcpus); return rc; } int xc_sched_rtds_vcpu_get(xc_interface *xch, uint32_t domid, struct xen_domctl_schedparam_vcpu *vcpus, uint32_t num_vcpus) { int rc = 0; unsigned processed = 0; DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(vcpus, sizeof(*vcpus) * num_vcpus, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); if ( xc_hypercall_bounce_pre(xch, vcpus) ) return -1; domctl.cmd = XEN_DOMCTL_scheduler_op; domctl.domain = (domid_t) domid; domctl.u.scheduler_op.sched_id = XEN_SCHEDULER_RTDS; domctl.u.scheduler_op.cmd = XEN_DOMCTL_SCHEDOP_getvcpuinfo; while ( processed < num_vcpus ) { domctl.u.scheduler_op.u.v.nr_vcpus = num_vcpus - processed; set_xen_guest_handle_offset(domctl.u.scheduler_op.u.v.vcpus, vcpus, processed); if ( (rc = do_domctl(xch, &domctl)) != 0 ) break; processed += domctl.u.scheduler_op.u.v.nr_vcpus; } xc_hypercall_bounce_post(xch, vcpus); return rc; } xen-4.9.2/tools/libxc/xc_psr.c0000664000175000017500000002027013256712137014361 0ustar smbsmb/* * xc_psr.c * * platform shared resource related API functions. * * Copyright (C) 2014 Intel Corporation * Author Dongxiao Xu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include "xc_private.h" #include "xc_msr_x86.h" #define IA32_CMT_CTR_ERROR_MASK (0x3ull << 62) #define EVTID_L3_OCCUPANCY 0x1 #define EVTID_TOTAL_MEM_COUNT 0x2 #define EVTID_LOCAL_MEM_COUNT 0x3 int xc_psr_cmt_attach(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_psr_cmt_op; domctl.domain = (domid_t)domid; domctl.u.psr_cmt_op.cmd = XEN_DOMCTL_PSR_CMT_OP_ATTACH; return do_domctl(xch, &domctl); } int xc_psr_cmt_detach(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_psr_cmt_op; domctl.domain = (domid_t)domid; domctl.u.psr_cmt_op.cmd = XEN_DOMCTL_PSR_CMT_OP_DETACH; return do_domctl(xch, &domctl); } int xc_psr_cmt_get_domain_rmid(xc_interface *xch, uint32_t domid, uint32_t *rmid) { int rc; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_psr_cmt_op; domctl.domain = (domid_t)domid; domctl.u.psr_cmt_op.cmd = XEN_DOMCTL_PSR_CMT_OP_QUERY_RMID; rc = do_domctl(xch, &domctl); if ( !rc ) *rmid = domctl.u.psr_cmt_op.data; return rc; } int xc_psr_cmt_get_total_rmid(xc_interface *xch, uint32_t *total_rmid) { static int val = 0; int rc; DECLARE_SYSCTL; if ( val ) { *total_rmid = val; return 0; } sysctl.cmd = XEN_SYSCTL_psr_cmt_op; sysctl.u.psr_cmt_op.cmd = XEN_SYSCTL_PSR_CMT_get_total_rmid; sysctl.u.psr_cmt_op.flags = 0; rc = xc_sysctl(xch, &sysctl); if ( !rc ) val = *total_rmid = sysctl.u.psr_cmt_op.u.data; return rc; } int xc_psr_cmt_get_l3_upscaling_factor(xc_interface *xch, uint32_t *upscaling_factor) { static int val = 0; int rc; DECLARE_SYSCTL; if ( val ) { *upscaling_factor = val; return 0; } sysctl.cmd = XEN_SYSCTL_psr_cmt_op; sysctl.u.psr_cmt_op.cmd = XEN_SYSCTL_PSR_CMT_get_l3_upscaling_factor; sysctl.u.psr_cmt_op.flags = 0; rc = xc_sysctl(xch, &sysctl); if ( !rc ) val = *upscaling_factor = sysctl.u.psr_cmt_op.u.data; return rc; } int xc_psr_cmt_get_l3_event_mask(xc_interface *xch, uint32_t *event_mask) { int rc; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_psr_cmt_op; sysctl.u.psr_cmt_op.cmd = XEN_SYSCTL_PSR_CMT_get_l3_event_mask; sysctl.u.psr_cmt_op.flags = 0; rc = xc_sysctl(xch, &sysctl); if ( !rc ) *event_mask = sysctl.u.psr_cmt_op.u.data; return rc; } int xc_psr_cmt_get_l3_cache_size(xc_interface *xch, uint32_t cpu, uint32_t *l3_cache_size) { static int val = 0; int rc; DECLARE_SYSCTL; if ( val ) { *l3_cache_size = val; return 0; } sysctl.cmd = XEN_SYSCTL_psr_cmt_op; sysctl.u.psr_cmt_op.cmd = XEN_SYSCTL_PSR_CMT_get_l3_cache_size; sysctl.u.psr_cmt_op.flags = 0; sysctl.u.psr_cmt_op.u.l3_cache.cpu = cpu; rc = xc_sysctl(xch, &sysctl); if ( !rc ) val = *l3_cache_size= sysctl.u.psr_cmt_op.u.data; return rc; } int xc_psr_cmt_get_data(xc_interface *xch, uint32_t rmid, uint32_t cpu, xc_psr_cmt_type type, uint64_t *monitor_data, uint64_t *tsc) { xc_resource_op_t op; xc_resource_entry_t entries[3]; xc_resource_entry_t *tsc_entry = NULL; uint32_t evtid, nr = 0; int rc; switch ( type ) { case XC_PSR_CMT_L3_OCCUPANCY: evtid = EVTID_L3_OCCUPANCY; break; case XC_PSR_CMT_TOTAL_MEM_COUNT: evtid = EVTID_TOTAL_MEM_COUNT; break; case XC_PSR_CMT_LOCAL_MEM_COUNT: evtid = EVTID_LOCAL_MEM_COUNT; break; default: return -1; } entries[nr].u.cmd = XEN_RESOURCE_OP_MSR_WRITE; entries[nr].idx = MSR_IA32_CMT_EVTSEL; entries[nr].val = (uint64_t)rmid << 32 | evtid; entries[nr].rsvd = 0; nr++; entries[nr].u.cmd = XEN_RESOURCE_OP_MSR_READ; entries[nr].idx = MSR_IA32_CMT_CTR; entries[nr].val = 0; entries[nr].rsvd = 0; nr++; if ( tsc != NULL ) { tsc_entry = &entries[nr]; entries[nr].u.cmd = XEN_RESOURCE_OP_MSR_READ; entries[nr].idx = MSR_IA32_TSC; entries[nr].val = 0; entries[nr].rsvd = 0; nr++; } assert(nr <= ARRAY_SIZE(entries)); op.cpu = cpu; op.nr_entries = nr; op.entries = entries; rc = xc_resource_op(xch, 1, &op); if ( rc < 0 ) return rc; if ( op.result != nr || entries[1].val & IA32_CMT_CTR_ERROR_MASK ) return -1; *monitor_data = entries[1].val; if ( tsc_entry != NULL ) *tsc = tsc_entry->val; return 0; } int xc_psr_cmt_enabled(xc_interface *xch) { static int val = -1; int rc; DECLARE_SYSCTL; if ( val >= 0 ) return val; sysctl.cmd = XEN_SYSCTL_psr_cmt_op; sysctl.u.psr_cmt_op.cmd = XEN_SYSCTL_PSR_CMT_enabled; sysctl.u.psr_cmt_op.flags = 0; rc = do_sysctl(xch, &sysctl); if ( !rc ) { val = sysctl.u.psr_cmt_op.u.data; return val; } return 0; } int xc_psr_cat_set_domain_data(xc_interface *xch, uint32_t domid, xc_psr_cat_type type, uint32_t target, uint64_t data) { DECLARE_DOMCTL; uint32_t cmd; switch ( type ) { case XC_PSR_CAT_L3_CBM: cmd = XEN_DOMCTL_PSR_CAT_OP_SET_L3_CBM; break; case XC_PSR_CAT_L3_CBM_CODE: cmd = XEN_DOMCTL_PSR_CAT_OP_SET_L3_CODE; break; case XC_PSR_CAT_L3_CBM_DATA: cmd = XEN_DOMCTL_PSR_CAT_OP_SET_L3_DATA; break; default: errno = EINVAL; return -1; } domctl.cmd = XEN_DOMCTL_psr_cat_op; domctl.domain = (domid_t)domid; domctl.u.psr_cat_op.cmd = cmd; domctl.u.psr_cat_op.target = target; domctl.u.psr_cat_op.data = data; return do_domctl(xch, &domctl); } int xc_psr_cat_get_domain_data(xc_interface *xch, uint32_t domid, xc_psr_cat_type type, uint32_t target, uint64_t *data) { int rc; DECLARE_DOMCTL; uint32_t cmd; switch ( type ) { case XC_PSR_CAT_L3_CBM: cmd = XEN_DOMCTL_PSR_CAT_OP_GET_L3_CBM; break; case XC_PSR_CAT_L3_CBM_CODE: cmd = XEN_DOMCTL_PSR_CAT_OP_GET_L3_CODE; break; case XC_PSR_CAT_L3_CBM_DATA: cmd = XEN_DOMCTL_PSR_CAT_OP_GET_L3_DATA; break; default: errno = EINVAL; return -1; } domctl.cmd = XEN_DOMCTL_psr_cat_op; domctl.domain = (domid_t)domid; domctl.u.psr_cat_op.cmd = cmd; domctl.u.psr_cat_op.target = target; rc = do_domctl(xch, &domctl); if ( !rc ) *data = domctl.u.psr_cat_op.data; return rc; } int xc_psr_cat_get_l3_info(xc_interface *xch, uint32_t socket, uint32_t *cos_max, uint32_t *cbm_len, bool *cdp_enabled) { int rc; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_psr_cat_op; sysctl.u.psr_cat_op.cmd = XEN_SYSCTL_PSR_CAT_get_l3_info; sysctl.u.psr_cat_op.target = socket; rc = xc_sysctl(xch, &sysctl); if ( !rc ) { *cos_max = sysctl.u.psr_cat_op.u.l3_info.cos_max; *cbm_len = sysctl.u.psr_cat_op.u.l3_info.cbm_len; *cdp_enabled = sysctl.u.psr_cat_op.u.l3_info.flags & XEN_SYSCTL_PSR_CAT_L3_CDP; } return rc; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_decompress.h0000664000175000017500000000025213256712137016563 0ustar smbsmb#ifndef __MINIOS__ # include "xc_dom.h" #else # include "xc_dom_decompress_unsafe.h" #endif int xc_try_lz4_decode(struct xc_dom_image *dom, void **blob, size_t *size); xen-4.9.2/tools/libxc/xc_dom_compat_linux.c0000664000175000017500000000542613256712137017124 0ustar smbsmb/* * Xen domain builder -- compatibility code. * * Replacements for xc_linux_build & friends, * as example code and to make the new builder * usable as drop-in replacement. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * written 2006 by Gerd Hoffmann . * */ #include #include #include #include #include #include "xenctrl.h" #include "xg_private.h" #include "xc_dom.h" /* ------------------------------------------------------------------------ */ int xc_linux_build(xc_interface *xch, uint32_t domid, unsigned int mem_mb, const char *image_name, const char *initrd_name, const char *cmdline, const char *features, unsigned long flags, unsigned int store_evtchn, unsigned long *store_mfn, unsigned int console_evtchn, unsigned long *console_mfn) { struct xc_dom_image *dom; int rc; xc_dom_loginit(xch); dom = xc_dom_allocate(xch, cmdline, features); if (dom == NULL) return -1; if ( (rc = xc_dom_kernel_file(dom, image_name)) != 0 ) goto out; if ( initrd_name && strlen(initrd_name) && ((rc = xc_dom_ramdisk_file(dom, initrd_name)) != 0) ) goto out; dom->flags |= flags; dom->console_evtchn = console_evtchn; dom->xenstore_evtchn = store_evtchn; if ( (rc = xc_dom_boot_xen_init(dom, xch, domid)) != 0 ) goto out; if ( (rc = xc_dom_parse_image(dom)) != 0 ) goto out; if ( (rc = xc_dom_mem_init(dom, mem_mb)) != 0 ) goto out; if ( (rc = xc_dom_boot_mem_init(dom)) != 0 ) goto out; if ( (rc = xc_dom_build_image(dom)) != 0 ) goto out; if ( (rc = xc_dom_boot_image(dom)) != 0 ) goto out; if ( (rc = xc_dom_gnttab_init(dom)) != 0) goto out; *console_mfn = xc_dom_p2m(dom, dom->console_pfn); *store_mfn = xc_dom_p2m(dom, dom->xenstore_pfn); out: xc_dom_release(dom); return rc; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_netbsd.c0000664000175000017500000000365513256712137015044 0ustar smbsmb/****************************************************************************** * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include #include #include /* Optionally flush file to disk and discard page cache */ void discard_file_cache(xc_interface *xch, int fd, int flush) { off_t cur = 0; int saved_errno = errno; if ( flush && (fsync(fd) < 0) ) { /*PERROR("Failed to flush file: %s", strerror(errno));*/ goto out; } /* * Calculate last page boundry of amount written so far * unless we are flushing in which case entire cache * is discarded. */ if ( !flush ) { if ( ( cur = lseek(fd, 0, SEEK_CUR)) == (off_t)-1 ) cur = 0; cur &= ~(PAGE_SIZE - 1); } /* Discard from the buffer cache. */ if ( posix_fadvise(fd, 0, cur, POSIX_FADV_DONTNEED) < 0 ) { /*PERROR("Failed to discard cache: %s", strerror(errno));*/ goto out; } out: errno = saved_errno; } void *xc_memalign(xc_interface *xch, size_t alignment, size_t size) { return valloc(size); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_evtchn_compat.c0000664000175000017500000000274713256712137016420 0ustar smbsmb/* * Compat shims for use of 3rd party consumers of libxenctrl xc_evtchn * functionality which has been split into separate libraries. */ #include #define XC_WANT_COMPAT_EVTCHN_API #include "xenctrl.h" xc_evtchn *xc_evtchn_open(xentoollog_logger *logger, unsigned open_flags) { return xenevtchn_open(logger, open_flags); } int xc_evtchn_close(xc_evtchn *xce) { return xenevtchn_close(xce); } int xc_evtchn_fd(xc_evtchn *xce) { return xenevtchn_fd(xce); } int xc_evtchn_notify(xc_evtchn *xce, evtchn_port_t port) { return xenevtchn_notify(xce, port); } evtchn_port_or_error_t xc_evtchn_bind_unbound_port(xc_evtchn *xce, int domid) { return xenevtchn_bind_unbound_port(xce, domid); } evtchn_port_or_error_t xc_evtchn_bind_interdomain(xc_evtchn *xce, int domid, evtchn_port_t remote_port) { return xenevtchn_bind_interdomain(xce, domid, remote_port); } evtchn_port_or_error_t xc_evtchn_bind_virq(xc_evtchn *xce, unsigned int virq) { return xenevtchn_bind_virq(xce, virq); } int xc_evtchn_unbind(xc_evtchn *xce, evtchn_port_t port) { return xenevtchn_unbind(xce, port); } evtchn_port_or_error_t xc_evtchn_pending(xc_evtchn *xce) { return xenevtchn_pending(xce); } int xc_evtchn_unmask(xc_evtchn *xce, evtchn_port_t port) { return xenevtchn_unmask(xce, port); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_x86.c0000664000175000017500000016546313256712137015057 0ustar smbsmb/* * Xen domain builder -- i386 and x86_64 bits. * * Most architecture-specific code for x86 goes here. * - prepare page tables. * - fill architecture-specific structs. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * written 2006 by Gerd Hoffmann . * */ #include #include #include #include #include #include #include #include #include #include #include #include "xg_private.h" #include "xc_dom.h" #include "xenctrl.h" /* ------------------------------------------------------------------------ */ #define SUPERPAGE_BATCH_SIZE 512 #define SUPERPAGE_2MB_SHIFT 9 #define SUPERPAGE_2MB_NR_PFNS (1UL << SUPERPAGE_2MB_SHIFT) #define SUPERPAGE_1GB_SHIFT 18 #define SUPERPAGE_1GB_NR_PFNS (1UL << SUPERPAGE_1GB_SHIFT) #define X86_CR0_PE 0x01 #define X86_CR0_ET 0x10 #define SPECIALPAGE_PAGING 0 #define SPECIALPAGE_ACCESS 1 #define SPECIALPAGE_SHARING 2 #define SPECIALPAGE_BUFIOREQ 3 #define SPECIALPAGE_XENSTORE 4 #define SPECIALPAGE_IOREQ 5 #define SPECIALPAGE_IDENT_PT 6 #define SPECIALPAGE_CONSOLE 7 #define special_pfn(x) \ (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES + (x)) #define NR_IOREQ_SERVER_PAGES 8 #define ioreq_server_pfn(x) (special_pfn(0) - NR_IOREQ_SERVER_PAGES + (x)) #define bits_to_mask(bits) (((xen_vaddr_t)1 << (bits))-1) #define round_down(addr, mask) ((addr) & ~(mask)) #define round_up(addr, mask) ((addr) | (mask)) #define round_pg_up(addr) (((addr) + PAGE_SIZE_X86 - 1) & ~(PAGE_SIZE_X86 - 1)) #define HVMLOADER_MODULE_MAX_COUNT 1 #define HVMLOADER_MODULE_NAME_SIZE 10 struct xc_dom_params { unsigned levels; xen_vaddr_t vaddr_mask; x86_pgentry_t lvl_prot[4]; }; struct xc_dom_x86_mapping_lvl { xen_vaddr_t from; xen_vaddr_t to; xen_pfn_t pfn; unsigned int pgtables; }; struct xc_dom_x86_mapping { struct xc_dom_x86_mapping_lvl area; struct xc_dom_x86_mapping_lvl lvls[4]; }; struct xc_dom_image_x86 { unsigned n_mappings; #define MAPPING_MAX 2 struct xc_dom_x86_mapping maps[MAPPING_MAX]; struct xc_dom_params *params; }; /* get guest IO ABI protocol */ const char *xc_domain_get_native_protocol(xc_interface *xch, uint32_t domid) { int ret; uint32_t guest_width; const char *protocol; ret = xc_domain_get_guest_width(xch, domid, &guest_width); if ( ret ) return NULL; switch (guest_width) { case 4: /* 32 bit guest */ protocol = XEN_IO_PROTO_ABI_X86_32; break; case 8: /* 64 bit guest */ protocol = XEN_IO_PROTO_ABI_X86_64; break; default: protocol = NULL; } return protocol; } static int count_pgtables(struct xc_dom_image *dom, xen_vaddr_t from, xen_vaddr_t to, xen_pfn_t pfn) { struct xc_dom_image_x86 *domx86 = dom->arch_private; struct xc_dom_x86_mapping *map, *map_cmp; xen_pfn_t pfn_end; xen_vaddr_t mask; unsigned bits; int l, m; if ( domx86->n_mappings == MAPPING_MAX ) { xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY, "%s: too many mappings\n", __FUNCTION__); return -ENOMEM; } map = domx86->maps + domx86->n_mappings; pfn_end = pfn + ((to - from) >> PAGE_SHIFT_X86); if ( pfn_end >= dom->p2m_size ) { xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY, "%s: not enough memory for initial mapping (%#"PRIpfn" > %#"PRIpfn")", __FUNCTION__, pfn_end, dom->p2m_size); return -ENOMEM; } for ( m = 0; m < domx86->n_mappings; m++ ) { map_cmp = domx86->maps + m; if ( from < map_cmp->area.to && to > map_cmp->area.from ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: overlapping mappings\n", __FUNCTION__); return -EINVAL; } } memset(map, 0, sizeof(*map)); map->area.from = from & domx86->params->vaddr_mask; map->area.to = to & domx86->params->vaddr_mask; for ( l = domx86->params->levels - 1; l >= 0; l-- ) { map->lvls[l].pfn = dom->pfn_alloc_end + map->area.pgtables; if ( l == domx86->params->levels - 1 ) { /* Top level page table in first mapping only. */ if ( domx86->n_mappings == 0 ) { map->lvls[l].from = 0; map->lvls[l].to = domx86->params->vaddr_mask; map->lvls[l].pgtables = 1; map->area.pgtables++; } continue; } bits = PAGE_SHIFT_X86 + (l + 1) * PGTBL_LEVEL_SHIFT_X86; mask = bits_to_mask(bits); map->lvls[l].from = map->area.from & ~mask; map->lvls[l].to = map->area.to | mask; if ( domx86->params->levels == PGTBL_LEVELS_I386 && domx86->n_mappings == 0 && to < 0xc0000000 && l == 1 ) { DOMPRINTF("%s: PAE: extra l2 page table for l3#3", __FUNCTION__); map->lvls[l].to = domx86->params->vaddr_mask; } for ( m = 0; m < domx86->n_mappings; m++ ) { map_cmp = domx86->maps + m; if ( map_cmp->lvls[l].from == map_cmp->lvls[l].to ) continue; if ( map->lvls[l].from >= map_cmp->lvls[l].from && map->lvls[l].to <= map_cmp->lvls[l].to ) { map->lvls[l].from = 0; map->lvls[l].to = 0; break; } assert(map->lvls[l].from >= map_cmp->lvls[l].from || map->lvls[l].to <= map_cmp->lvls[l].to); if ( map->lvls[l].from >= map_cmp->lvls[l].from && map->lvls[l].from <= map_cmp->lvls[l].to ) map->lvls[l].from = map_cmp->lvls[l].to + 1; if ( map->lvls[l].to >= map_cmp->lvls[l].from && map->lvls[l].to <= map_cmp->lvls[l].to ) map->lvls[l].to = map_cmp->lvls[l].from - 1; } if ( map->lvls[l].from < map->lvls[l].to ) map->lvls[l].pgtables = ((map->lvls[l].to - map->lvls[l].from) >> bits) + 1; DOMPRINTF("%s: 0x%016" PRIx64 "/%d: 0x%016" PRIx64 " -> 0x%016" PRIx64 ", %d table(s)", __FUNCTION__, mask, bits, map->lvls[l].from, map->lvls[l].to, map->lvls[l].pgtables); map->area.pgtables += map->lvls[l].pgtables; } return 0; } static int alloc_pgtables(struct xc_dom_image *dom) { int pages, extra_pages; xen_vaddr_t try_virt_end; struct xc_dom_image_x86 *domx86 = dom->arch_private; struct xc_dom_x86_mapping *map = domx86->maps + domx86->n_mappings; extra_pages = dom->alloc_bootstack ? 1 : 0; extra_pages += (512 * 1024) / PAGE_SIZE_X86; /* 512kB padding */ pages = extra_pages; for ( ; ; ) { try_virt_end = round_up(dom->virt_alloc_end + pages * PAGE_SIZE_X86, bits_to_mask(22)); /* 4MB alignment */ if ( count_pgtables(dom, dom->parms.virt_base, try_virt_end, 0) ) return -1; pages = map->area.pgtables + extra_pages; if ( dom->virt_alloc_end + pages * PAGE_SIZE_X86 <= try_virt_end + 1 ) break; } map->area.pfn = 0; domx86->n_mappings++; dom->virt_pgtab_end = try_virt_end + 1; return xc_dom_alloc_segment(dom, &dom->pgtables_seg, "page tables", 0, map->area.pgtables * PAGE_SIZE_X86); } /* ------------------------------------------------------------------------ */ /* i386 pagetables */ static struct xc_dom_params x86_32_params = { .levels = PGTBL_LEVELS_I386, .vaddr_mask = bits_to_mask(VIRT_BITS_I386), .lvl_prot[0] = _PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED, .lvl_prot[1] = _PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER, .lvl_prot[2] = _PAGE_PRESENT, }; static int alloc_pgtables_x86_32_pae(struct xc_dom_image *dom) { struct xc_dom_image_x86 *domx86 = dom->arch_private; domx86->params = &x86_32_params; return alloc_pgtables(dom); } #define pfn_to_paddr(pfn) ((xen_paddr_t)(pfn) << PAGE_SHIFT_X86) #define pgentry_to_pfn(entry) ((xen_pfn_t)((entry) >> PAGE_SHIFT_X86)) /* * Move the l3 page table page below 4G for guests which do not * support the extended-cr3 format. The l3 is currently empty so we * do not need to preserve the current contents. */ static xen_pfn_t move_l3_below_4G(struct xc_dom_image *dom, xen_pfn_t l3pfn, xen_pfn_t l3mfn) { xen_pfn_t new_l3mfn; struct xc_mmu *mmu; void *l3tab; mmu = xc_alloc_mmu_updates(dom->xch, dom->guest_domid); if ( mmu == NULL ) { DOMPRINTF("%s: failed at %d", __FUNCTION__, __LINE__); return l3mfn; } xc_dom_unmap_one(dom, l3pfn); new_l3mfn = xc_make_page_below_4G(dom->xch, dom->guest_domid, l3mfn); if ( !new_l3mfn ) goto out; dom->p2m_host[l3pfn] = new_l3mfn; if ( xc_dom_update_guest_p2m(dom) != 0 ) goto out; if ( xc_add_mmu_update(dom->xch, mmu, (((unsigned long long)new_l3mfn) << XC_DOM_PAGE_SHIFT(dom)) | MMU_MACHPHYS_UPDATE, l3pfn) ) goto out; if ( xc_flush_mmu_updates(dom->xch, mmu) ) goto out; /* * This ensures that the entire pgtables_seg is mapped by a single * mmap region. arch_setup_bootlate() relies on this to be able to * unmap and pin the pagetables. */ if ( xc_dom_seg_to_ptr(dom, &dom->pgtables_seg) == NULL ) goto out; l3tab = xc_dom_pfn_to_ptr(dom, l3pfn, 1); if ( l3tab == NULL ) { DOMPRINTF("%s: xc_dom_pfn_to_ptr(dom, l3pfn, 1) => NULL", __FUNCTION__); goto out; /* our one call site will call xc_dom_panic and fail */ } memset(l3tab, 0, XC_DOM_PAGE_SIZE(dom)); DOMPRINTF("%s: successfully relocated L3 below 4G. " "(L3 PFN %#"PRIpfn" MFN %#"PRIpfn"=>%#"PRIpfn")", __FUNCTION__, l3pfn, l3mfn, new_l3mfn); l3mfn = new_l3mfn; out: free(mmu); return l3mfn; } static x86_pgentry_t *get_pg_table_x86(struct xc_dom_image *dom, int m, int l) { struct xc_dom_image_x86 *domx86 = dom->arch_private; struct xc_dom_x86_mapping *map; x86_pgentry_t *pg; map = domx86->maps + m; pg = xc_dom_pfn_to_ptr(dom, map->lvls[l].pfn, 0); if ( pg ) return pg; xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: xc_dom_pfn_to_ptr failed", __FUNCTION__); return NULL; } static x86_pgentry_t get_pg_prot_x86(struct xc_dom_image *dom, int l, xen_pfn_t pfn) { struct xc_dom_image_x86 *domx86 = dom->arch_private; struct xc_dom_x86_mapping *map; xen_pfn_t pfn_s, pfn_e; x86_pgentry_t prot; unsigned m; prot = domx86->params->lvl_prot[l]; if ( l > 0 ) return prot; for ( m = 0; m < domx86->n_mappings; m++ ) { map = domx86->maps + m; pfn_s = map->lvls[domx86->params->levels - 1].pfn; pfn_e = map->area.pgtables + pfn_s; if ( pfn >= pfn_s && pfn < pfn_e ) return prot & ~_PAGE_RW; } return prot; } static int setup_pgtables_x86(struct xc_dom_image *dom) { struct xc_dom_image_x86 *domx86 = dom->arch_private; struct xc_dom_x86_mapping *map1, *map2; struct xc_dom_x86_mapping_lvl *lvl; xen_vaddr_t from, to; xen_pfn_t pfn, p, p_s, p_e; x86_pgentry_t *pg; unsigned m1, m2; int l; for ( l = domx86->params->levels - 1; l >= 0; l-- ) for ( m1 = 0; m1 < domx86->n_mappings; m1++ ) { map1 = domx86->maps + m1; from = map1->lvls[l].from; to = map1->lvls[l].to; pg = get_pg_table_x86(dom, m1, l); if ( !pg ) return -1; for ( m2 = 0; m2 < domx86->n_mappings; m2++ ) { map2 = domx86->maps + m2; lvl = (l > 0) ? map2->lvls + l - 1 : &map2->area; if ( l > 0 && lvl->pgtables == 0 ) continue; if ( lvl->from >= to || lvl->to <= from ) continue; p_s = (max(from, lvl->from) - from) >> (PAGE_SHIFT_X86 + l * PGTBL_LEVEL_SHIFT_X86); p_e = (min(to, lvl->to) - from) >> (PAGE_SHIFT_X86 + l * PGTBL_LEVEL_SHIFT_X86); pfn = ((max(from, lvl->from) - lvl->from) >> (PAGE_SHIFT_X86 + l * PGTBL_LEVEL_SHIFT_X86)) + lvl->pfn; for ( p = p_s; p <= p_e; p++ ) { pg[p] = pfn_to_paddr(xc_dom_p2m(dom, pfn)) | get_pg_prot_x86(dom, l, pfn); pfn++; } } } return 0; } static int setup_pgtables_x86_32_pae(struct xc_dom_image *dom) { struct xc_dom_image_x86 *domx86 = dom->arch_private; xen_pfn_t l3mfn, l3pfn; l3pfn = domx86->maps[0].lvls[2].pfn; l3mfn = xc_dom_p2m(dom, l3pfn); if ( dom->parms.pae == XEN_PAE_YES ) { if ( l3mfn >= 0x100000 ) l3mfn = move_l3_below_4G(dom, l3pfn, l3mfn); if ( l3mfn >= 0x100000 ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,"%s: cannot move L3" " below 4G. extended-cr3 not supported by guest. " "(L3 PFN %#"PRIpfn" MFN %#"PRIpfn")", __FUNCTION__, l3pfn, l3mfn); return -EINVAL; } } return setup_pgtables_x86(dom); } /* ------------------------------------------------------------------------ */ /* x86_64 pagetables */ static struct xc_dom_params x86_64_params = { .levels = PGTBL_LEVELS_X86_64, .vaddr_mask = bits_to_mask(VIRT_BITS_X86_64), .lvl_prot[0] = _PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED, .lvl_prot[1] = _PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER, .lvl_prot[2] = _PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER, .lvl_prot[3] = _PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER, }; static int alloc_pgtables_x86_64(struct xc_dom_image *dom) { struct xc_dom_image_x86 *domx86 = dom->arch_private; domx86->params = &x86_64_params; return alloc_pgtables(dom); } static int setup_pgtables_x86_64(struct xc_dom_image *dom) { return setup_pgtables_x86(dom); } /* ------------------------------------------------------------------------ */ static int alloc_p2m_list(struct xc_dom_image *dom, size_t p2m_alloc_size) { if ( xc_dom_alloc_segment(dom, &dom->p2m_seg, "phys2mach", 0, p2m_alloc_size) ) return -1; dom->p2m_guest = xc_dom_seg_to_ptr(dom, &dom->p2m_seg); if ( dom->p2m_guest == NULL ) return -1; return 0; } static int alloc_p2m_list_x86_32(struct xc_dom_image *dom) { size_t p2m_alloc_size = dom->p2m_size * dom->arch_hooks->sizeof_pfn; p2m_alloc_size = round_pg_up(p2m_alloc_size); return alloc_p2m_list(dom, p2m_alloc_size); } static int alloc_p2m_list_x86_64(struct xc_dom_image *dom) { struct xc_dom_image_x86 *domx86 = dom->arch_private; struct xc_dom_x86_mapping *map = domx86->maps + domx86->n_mappings; size_t p2m_alloc_size = dom->p2m_size * dom->arch_hooks->sizeof_pfn; xen_vaddr_t from, to; unsigned lvl; p2m_alloc_size = round_pg_up(p2m_alloc_size); if ( dom->parms.p2m_base != UNSET_ADDR ) { from = dom->parms.p2m_base; to = from + p2m_alloc_size - 1; if ( count_pgtables(dom, from, to, dom->pfn_alloc_end) ) return -1; map->area.pfn = dom->pfn_alloc_end; for ( lvl = 0; lvl < 4; lvl++ ) map->lvls[lvl].pfn += p2m_alloc_size >> PAGE_SHIFT_X86; domx86->n_mappings++; p2m_alloc_size += map->area.pgtables << PAGE_SHIFT_X86; } return alloc_p2m_list(dom, p2m_alloc_size); } /* ------------------------------------------------------------------------ */ static int alloc_magic_pages(struct xc_dom_image *dom) { /* allocate special pages */ dom->start_info_pfn = xc_dom_alloc_page(dom, "start info"); if ( dom->start_info_pfn == INVALID_PFN ) return -1; dom->xenstore_pfn = xc_dom_alloc_page(dom, "xenstore"); if ( dom->xenstore_pfn == INVALID_PFN ) return -1; dom->console_pfn = xc_dom_alloc_page(dom, "console"); if ( dom->console_pfn == INVALID_PFN ) return -1; if ( xc_dom_translated(dom) ) { dom->shared_info_pfn = xc_dom_alloc_page(dom, "shared info"); if ( dom->shared_info_pfn == INVALID_PFN ) return -1; } dom->alloc_bootstack = 1; return 0; } static void build_hvm_info(void *hvm_info_page, struct xc_dom_image *dom) { struct hvm_info_table *hvm_info = (struct hvm_info_table *) (((unsigned char *)hvm_info_page) + HVM_INFO_OFFSET); uint8_t sum; int i; memset(hvm_info_page, 0, PAGE_SIZE); /* Fill in the header. */ memcpy(hvm_info->signature, "HVM INFO", sizeof(hvm_info->signature)); hvm_info->length = sizeof(struct hvm_info_table); /* Sensible defaults: these can be overridden by the caller. */ hvm_info->apic_mode = 1; hvm_info->nr_vcpus = 1; memset(hvm_info->vcpu_online, 0xff, sizeof(hvm_info->vcpu_online)); /* Memory parameters. */ hvm_info->low_mem_pgend = dom->lowmem_end >> PAGE_SHIFT; hvm_info->high_mem_pgend = dom->highmem_end >> PAGE_SHIFT; hvm_info->reserved_mem_pgstart = ioreq_server_pfn(0); /* Finish with the checksum. */ for ( i = 0, sum = 0; i < hvm_info->length; i++ ) sum += ((uint8_t *)hvm_info)[i]; hvm_info->checksum = -sum; } static int alloc_magic_pages_hvm(struct xc_dom_image *dom) { unsigned long i; uint32_t *ident_pt, domid = dom->guest_domid; int rc; xen_pfn_t special_array[X86_HVM_NR_SPECIAL_PAGES]; xen_pfn_t ioreq_server_array[NR_IOREQ_SERVER_PAGES]; xc_interface *xch = dom->xch; size_t start_info_size = sizeof(struct hvm_start_info); /* Allocate and clear special pages. */ for ( i = 0; i < X86_HVM_NR_SPECIAL_PAGES; i++ ) special_array[i] = special_pfn(i); rc = xc_domain_populate_physmap_exact(xch, domid, X86_HVM_NR_SPECIAL_PAGES, 0, 0, special_array); if ( rc != 0 ) { DOMPRINTF("Could not allocate special pages."); goto error_out; } if ( xc_clear_domain_pages(xch, domid, special_pfn(0), X86_HVM_NR_SPECIAL_PAGES) ) goto error_out; xc_hvm_param_set(xch, domid, HVM_PARAM_STORE_PFN, special_pfn(SPECIALPAGE_XENSTORE)); xc_hvm_param_set(xch, domid, HVM_PARAM_BUFIOREQ_PFN, special_pfn(SPECIALPAGE_BUFIOREQ)); xc_hvm_param_set(xch, domid, HVM_PARAM_IOREQ_PFN, special_pfn(SPECIALPAGE_IOREQ)); xc_hvm_param_set(xch, domid, HVM_PARAM_CONSOLE_PFN, special_pfn(SPECIALPAGE_CONSOLE)); xc_hvm_param_set(xch, domid, HVM_PARAM_PAGING_RING_PFN, special_pfn(SPECIALPAGE_PAGING)); xc_hvm_param_set(xch, domid, HVM_PARAM_MONITOR_RING_PFN, special_pfn(SPECIALPAGE_ACCESS)); xc_hvm_param_set(xch, domid, HVM_PARAM_SHARING_RING_PFN, special_pfn(SPECIALPAGE_SHARING)); if ( !dom->device_model ) { if ( dom->cmdline ) { dom->cmdline_size = ROUNDUP(strlen(dom->cmdline) + 1, 8); start_info_size += dom->cmdline_size; } /* Limited to one module. */ if ( dom->ramdisk_blob ) start_info_size += sizeof(struct hvm_modlist_entry); } else { start_info_size += sizeof(struct hvm_modlist_entry) * HVMLOADER_MODULE_MAX_COUNT; /* * Add extra space to write modules name. * The HVMLOADER_MODULE_NAME_SIZE accounts for NUL byte. */ start_info_size += HVMLOADER_MODULE_NAME_SIZE * HVMLOADER_MODULE_MAX_COUNT; /* * Allocate and clear additional ioreq server pages. The default * server will use the IOREQ and BUFIOREQ special pages above. */ for ( i = 0; i < NR_IOREQ_SERVER_PAGES; i++ ) ioreq_server_array[i] = ioreq_server_pfn(i); rc = xc_domain_populate_physmap_exact(xch, domid, NR_IOREQ_SERVER_PAGES, 0, 0, ioreq_server_array); if ( rc != 0 ) { DOMPRINTF("Could not allocate ioreq server pages."); goto error_out; } if ( xc_clear_domain_pages(xch, domid, ioreq_server_pfn(0), NR_IOREQ_SERVER_PAGES) ) goto error_out; /* Tell the domain where the pages are and how many there are */ xc_hvm_param_set(xch, domid, HVM_PARAM_IOREQ_SERVER_PFN, ioreq_server_pfn(0)); xc_hvm_param_set(xch, domid, HVM_PARAM_NR_IOREQ_SERVER_PAGES, NR_IOREQ_SERVER_PAGES); } rc = xc_dom_alloc_segment(dom, &dom->start_info_seg, "HVM start info", 0, start_info_size); if ( rc != 0 ) { DOMPRINTF("Unable to reserve memory for the start info"); goto out; } /* * Identity-map page table is required for running with CR0.PG=0 when * using Intel EPT. Create a 32-bit non-PAE page directory of superpages. */ if ( (ident_pt = xc_map_foreign_range( xch, domid, PAGE_SIZE, PROT_READ | PROT_WRITE, special_pfn(SPECIALPAGE_IDENT_PT))) == NULL ) goto error_out; for ( i = 0; i < PAGE_SIZE / sizeof(*ident_pt); i++ ) ident_pt[i] = ((i << 22) | _PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_PSE); munmap(ident_pt, PAGE_SIZE); xc_hvm_param_set(xch, domid, HVM_PARAM_IDENT_PT, special_pfn(SPECIALPAGE_IDENT_PT) << PAGE_SHIFT); dom->console_pfn = special_pfn(SPECIALPAGE_CONSOLE); dom->xenstore_pfn = special_pfn(SPECIALPAGE_XENSTORE); dom->parms.virt_hypercall = -1; rc = 0; goto out; error_out: rc = -1; out: return rc; } /* ------------------------------------------------------------------------ */ static int start_info_x86_32(struct xc_dom_image *dom) { struct xc_dom_image_x86 *domx86 = dom->arch_private; start_info_x86_32_t *start_info = xc_dom_pfn_to_ptr(dom, dom->start_info_pfn, 1); xen_pfn_t shinfo = xc_dom_translated(dom) ? dom->shared_info_pfn : dom->shared_info_mfn; DOMPRINTF_CALLED(dom->xch); if ( start_info == NULL ) { DOMPRINTF("%s: xc_dom_pfn_to_ptr failed on start_info", __FUNCTION__); return -1; /* our caller throws away our return value :-/ */ } memset(start_info, 0, sizeof(*start_info)); strncpy(start_info->magic, dom->guest_type, sizeof(start_info->magic)); start_info->magic[sizeof(start_info->magic) - 1] = '\0'; start_info->nr_pages = dom->total_pages; start_info->shared_info = shinfo << PAGE_SHIFT_X86; start_info->pt_base = dom->pgtables_seg.vstart; start_info->nr_pt_frames = domx86->maps[0].area.pgtables; start_info->mfn_list = dom->p2m_seg.vstart; start_info->flags = dom->flags; start_info->store_mfn = xc_dom_p2m(dom, dom->xenstore_pfn); start_info->store_evtchn = dom->xenstore_evtchn; start_info->console.domU.mfn = xc_dom_p2m(dom, dom->console_pfn); start_info->console.domU.evtchn = dom->console_evtchn; if ( dom->ramdisk_blob ) { start_info->mod_start = dom->initrd_start; start_info->mod_len = dom->initrd_len; } if ( dom->cmdline ) { strncpy((char *)start_info->cmd_line, dom->cmdline, MAX_GUEST_CMDLINE); start_info->cmd_line[MAX_GUEST_CMDLINE - 1] = '\0'; } return 0; } static int start_info_x86_64(struct xc_dom_image *dom) { struct xc_dom_image_x86 *domx86 = dom->arch_private; start_info_x86_64_t *start_info = xc_dom_pfn_to_ptr(dom, dom->start_info_pfn, 1); xen_pfn_t shinfo = xc_dom_translated(dom) ? dom->shared_info_pfn : dom->shared_info_mfn; DOMPRINTF_CALLED(dom->xch); if ( start_info == NULL ) { DOMPRINTF("%s: xc_dom_pfn_to_ptr failed on start_info", __FUNCTION__); return -1; /* our caller throws away our return value :-/ */ } memset(start_info, 0, sizeof(*start_info)); strncpy(start_info->magic, dom->guest_type, sizeof(start_info->magic)); start_info->magic[sizeof(start_info->magic) - 1] = '\0'; start_info->nr_pages = dom->total_pages; start_info->shared_info = shinfo << PAGE_SHIFT_X86; start_info->pt_base = dom->pgtables_seg.vstart; start_info->nr_pt_frames = domx86->maps[0].area.pgtables; start_info->mfn_list = dom->p2m_seg.vstart; if ( dom->parms.p2m_base != UNSET_ADDR ) { start_info->first_p2m_pfn = dom->p2m_seg.pfn; start_info->nr_p2m_frames = dom->p2m_seg.pages; } start_info->flags = dom->flags; start_info->store_mfn = xc_dom_p2m(dom, dom->xenstore_pfn); start_info->store_evtchn = dom->xenstore_evtchn; start_info->console.domU.mfn = xc_dom_p2m(dom, dom->console_pfn); start_info->console.domU.evtchn = dom->console_evtchn; if ( dom->ramdisk_blob ) { start_info->mod_start = dom->initrd_start; start_info->mod_len = dom->initrd_len; } if ( dom->cmdline ) { strncpy((char *)start_info->cmd_line, dom->cmdline, MAX_GUEST_CMDLINE); start_info->cmd_line[MAX_GUEST_CMDLINE - 1] = '\0'; } return 0; } static int shared_info_x86_32(struct xc_dom_image *dom, void *ptr) { shared_info_x86_32_t *shared_info = ptr; int i; DOMPRINTF_CALLED(dom->xch); memset(shared_info, 0, sizeof(*shared_info)); for ( i = 0; i < XEN_LEGACY_MAX_VCPUS; i++ ) shared_info->vcpu_info[i].evtchn_upcall_mask = 1; return 0; } static int shared_info_x86_64(struct xc_dom_image *dom, void *ptr) { shared_info_x86_64_t *shared_info = ptr; int i; DOMPRINTF_CALLED(dom->xch); memset(shared_info, 0, sizeof(*shared_info)); for ( i = 0; i < XEN_LEGACY_MAX_VCPUS; i++ ) shared_info->vcpu_info[i].evtchn_upcall_mask = 1; return 0; } /* ------------------------------------------------------------------------ */ static int vcpu_x86_32(struct xc_dom_image *dom) { vcpu_guest_context_any_t any_ctx; vcpu_guest_context_x86_32_t *ctxt = &any_ctx.x32; xen_pfn_t cr3_pfn; int rc; DOMPRINTF_CALLED(dom->xch); /* clear everything */ memset(ctxt, 0, sizeof(*ctxt)); ctxt->user_regs.eip = dom->parms.virt_entry; ctxt->user_regs.esp = dom->parms.virt_base + (dom->bootstack_pfn + 1) * PAGE_SIZE_X86; ctxt->user_regs.esi = dom->parms.virt_base + (dom->start_info_pfn) * PAGE_SIZE_X86; ctxt->user_regs.eflags = 1 << 9; /* Interrupt Enable */ ctxt->flags = VGCF_in_kernel_X86_32 | VGCF_online_X86_32; if ( dom->parms.pae == XEN_PAE_EXTCR3 || dom->parms.pae == XEN_PAE_BIMODAL ) ctxt->vm_assist |= (1UL << VMASST_TYPE_pae_extended_cr3); cr3_pfn = xc_dom_p2m(dom, dom->pgtables_seg.pfn); ctxt->ctrlreg[3] = xen_pfn_to_cr3_x86_32(cr3_pfn); DOMPRINTF("%s: cr3: pfn 0x%" PRIpfn " mfn 0x%" PRIpfn "", __FUNCTION__, dom->pgtables_seg.pfn, cr3_pfn); ctxt->user_regs.ds = FLAT_KERNEL_DS_X86_32; ctxt->user_regs.es = FLAT_KERNEL_DS_X86_32; ctxt->user_regs.fs = FLAT_KERNEL_DS_X86_32; ctxt->user_regs.gs = FLAT_KERNEL_DS_X86_32; ctxt->user_regs.ss = FLAT_KERNEL_SS_X86_32; ctxt->user_regs.cs = FLAT_KERNEL_CS_X86_32; ctxt->kernel_ss = ctxt->user_regs.ss; ctxt->kernel_sp = ctxt->user_regs.esp; rc = xc_vcpu_setcontext(dom->xch, dom->guest_domid, 0, &any_ctx); if ( rc != 0 ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: SETVCPUCONTEXT failed (rc=%d)", __func__, rc); return rc; } static int vcpu_x86_64(struct xc_dom_image *dom) { vcpu_guest_context_any_t any_ctx; vcpu_guest_context_x86_64_t *ctxt = &any_ctx.x64; xen_pfn_t cr3_pfn; int rc; DOMPRINTF_CALLED(dom->xch); /* clear everything */ memset(ctxt, 0, sizeof(*ctxt)); ctxt->user_regs.rip = dom->parms.virt_entry; ctxt->user_regs.rsp = dom->parms.virt_base + (dom->bootstack_pfn + 1) * PAGE_SIZE_X86; ctxt->user_regs.rsi = dom->parms.virt_base + (dom->start_info_pfn) * PAGE_SIZE_X86; ctxt->user_regs.rflags = 1 << 9; /* Interrupt Enable */ ctxt->flags = VGCF_in_kernel_X86_64 | VGCF_online_X86_64; cr3_pfn = xc_dom_p2m(dom, dom->pgtables_seg.pfn); ctxt->ctrlreg[3] = xen_pfn_to_cr3_x86_64(cr3_pfn); DOMPRINTF("%s: cr3: pfn 0x%" PRIpfn " mfn 0x%" PRIpfn "", __FUNCTION__, dom->pgtables_seg.pfn, cr3_pfn); ctxt->user_regs.ds = FLAT_KERNEL_DS_X86_64; ctxt->user_regs.es = FLAT_KERNEL_DS_X86_64; ctxt->user_regs.fs = FLAT_KERNEL_DS_X86_64; ctxt->user_regs.gs = FLAT_KERNEL_DS_X86_64; ctxt->user_regs.ss = FLAT_KERNEL_SS_X86_64; ctxt->user_regs.cs = FLAT_KERNEL_CS_X86_64; ctxt->kernel_ss = ctxt->user_regs.ss; ctxt->kernel_sp = ctxt->user_regs.esp; rc = xc_vcpu_setcontext(dom->xch, dom->guest_domid, 0, &any_ctx); if ( rc != 0 ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: SETVCPUCONTEXT failed (rc=%d)", __func__, rc); return rc; } static int vcpu_hvm(struct xc_dom_image *dom) { struct { struct hvm_save_descriptor header_d; HVM_SAVE_TYPE(HEADER) header; struct hvm_save_descriptor cpu_d; HVM_SAVE_TYPE(CPU) cpu; struct hvm_save_descriptor end_d; HVM_SAVE_TYPE(END) end; } bsp_ctx; uint8_t *full_ctx = NULL; int rc; DOMPRINTF_CALLED(dom->xch); /* * Get the full HVM context in order to have the header, it is not * possible to get the header with getcontext_partial, and crafting one * from userspace is also not an option since cpuid is trapped and * modified by Xen. */ rc = xc_domain_hvm_getcontext(dom->xch, dom->guest_domid, NULL, 0); if ( rc <= 0 ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: unable to fetch HVM context size (rc=%d)", __func__, rc); goto out; } full_ctx = calloc(1, rc); if ( full_ctx == NULL ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: unable to allocate memory for HVM context (rc=%d)", __func__, rc); rc = -ENOMEM; goto out; } rc = xc_domain_hvm_getcontext(dom->xch, dom->guest_domid, full_ctx, rc); if ( rc <= 0 ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: unable to fetch HVM context (rc=%d)", __func__, rc); goto out; } /* Copy the header to our partial context. */ memset(&bsp_ctx, 0, sizeof(bsp_ctx)); memcpy(&bsp_ctx, full_ctx, sizeof(struct hvm_save_descriptor) + HVM_SAVE_LENGTH(HEADER)); /* Set the CPU descriptor. */ bsp_ctx.cpu_d.typecode = HVM_SAVE_CODE(CPU); bsp_ctx.cpu_d.instance = 0; bsp_ctx.cpu_d.length = HVM_SAVE_LENGTH(CPU); /* Set the cached part of the relevant segment registers. */ bsp_ctx.cpu.cs_base = 0; bsp_ctx.cpu.ds_base = 0; bsp_ctx.cpu.ss_base = 0; bsp_ctx.cpu.tr_base = 0; bsp_ctx.cpu.cs_limit = ~0u; bsp_ctx.cpu.ds_limit = ~0u; bsp_ctx.cpu.ss_limit = ~0u; bsp_ctx.cpu.tr_limit = 0x67; bsp_ctx.cpu.cs_arbytes = 0xc9b; bsp_ctx.cpu.ds_arbytes = 0xc93; bsp_ctx.cpu.ss_arbytes = 0xc93; bsp_ctx.cpu.tr_arbytes = 0x8b; /* Set the control registers. */ bsp_ctx.cpu.cr0 = X86_CR0_PE | X86_CR0_ET; /* Set the IP. */ bsp_ctx.cpu.rip = dom->parms.phys_entry; if ( dom->start_info_seg.pfn ) bsp_ctx.cpu.rbx = dom->start_info_seg.pfn << PAGE_SHIFT; /* Set the end descriptor. */ bsp_ctx.end_d.typecode = HVM_SAVE_CODE(END); bsp_ctx.end_d.instance = 0; bsp_ctx.end_d.length = HVM_SAVE_LENGTH(END); rc = xc_domain_hvm_setcontext(dom->xch, dom->guest_domid, (uint8_t *)&bsp_ctx, sizeof(bsp_ctx)); if ( rc != 0 ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: SETHVMCONTEXT failed (rc=%d)", __func__, rc); out: free(full_ctx); return rc; } /* ------------------------------------------------------------------------ */ static int x86_compat(xc_interface *xch, domid_t domid, char *guest_type) { static const struct { char *guest; uint32_t size; } types[] = { { "xen-3.0-x86_32p", 32 }, { "xen-3.0-x86_64", 64 }, }; DECLARE_DOMCTL; int i,rc; memset(&domctl, 0, sizeof(domctl)); domctl.domain = domid; domctl.cmd = XEN_DOMCTL_set_address_size; for ( i = 0; i < ARRAY_SIZE(types); i++ ) if ( !strcmp(types[i].guest, guest_type) ) domctl.u.address_size.size = types[i].size; if ( domctl.u.address_size.size == 0 ) /* nothing to do */ return 0; xc_dom_printf(xch, "%s: guest %s, address size %" PRId32 "", __FUNCTION__, guest_type, domctl.u.address_size.size); rc = do_domctl(xch, &domctl); if ( rc != 0 ) xc_dom_printf(xch, "%s: warning: failed (rc=%d)", __FUNCTION__, rc); return rc; } static int meminit_pv(struct xc_dom_image *dom) { int rc; xen_pfn_t pfn, allocsz, mfn, total, pfn_base; int i, j, k; xen_vmemrange_t dummy_vmemrange[1]; unsigned int dummy_vnode_to_pnode[1]; xen_vmemrange_t *vmemranges; unsigned int *vnode_to_pnode; unsigned int nr_vmemranges, nr_vnodes; rc = x86_compat(dom->xch, dom->guest_domid, dom->guest_type); if ( rc ) return rc; /* try to claim pages for early warning of insufficient memory avail */ if ( dom->claim_enabled ) { rc = xc_domain_claim_pages(dom->xch, dom->guest_domid, dom->total_pages); if ( rc ) return rc; } /* Setup dummy vNUMA information if it's not provided. Note * that this is a valid state if libxl doesn't provide any * vNUMA information. * * The dummy values make libxc allocate all pages from * arbitrary physical nodes. This is the expected behaviour if * no vNUMA configuration is provided to libxc. * * Note that the following hunk is just for the convenience of * allocation code. No defaulting happens in libxc. */ if ( dom->nr_vmemranges == 0 ) { nr_vmemranges = 1; vmemranges = dummy_vmemrange; vmemranges[0].start = 0; vmemranges[0].end = (uint64_t)dom->total_pages << PAGE_SHIFT; vmemranges[0].flags = 0; vmemranges[0].nid = 0; nr_vnodes = 1; vnode_to_pnode = dummy_vnode_to_pnode; vnode_to_pnode[0] = XC_NUMA_NO_NODE; } else { nr_vmemranges = dom->nr_vmemranges; nr_vnodes = dom->nr_vnodes; vmemranges = dom->vmemranges; vnode_to_pnode = dom->vnode_to_pnode; } total = dom->p2m_size = 0; for ( i = 0; i < nr_vmemranges; i++ ) { total += ((vmemranges[i].end - vmemranges[i].start) >> PAGE_SHIFT); dom->p2m_size = max(dom->p2m_size, (xen_pfn_t)(vmemranges[i].end >> PAGE_SHIFT)); } if ( total != dom->total_pages ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: vNUMA page count mismatch (0x%"PRIpfn" != 0x%"PRIpfn")", __func__, total, dom->total_pages); return -EINVAL; } dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * dom->p2m_size); if ( dom->p2m_host == NULL ) return -EINVAL; for ( pfn = 0; pfn < dom->p2m_size; pfn++ ) dom->p2m_host[pfn] = INVALID_PFN; /* allocate guest memory */ for ( i = 0; i < nr_vmemranges; i++ ) { unsigned int memflags; uint64_t pages, super_pages; unsigned int pnode = vnode_to_pnode[vmemranges[i].nid]; xen_pfn_t extents[SUPERPAGE_BATCH_SIZE]; xen_pfn_t pfn_base_idx; memflags = 0; if ( pnode != XC_NUMA_NO_NODE ) memflags |= XENMEMF_exact_node(pnode); pages = (vmemranges[i].end - vmemranges[i].start) >> PAGE_SHIFT; super_pages = pages >> SUPERPAGE_2MB_SHIFT; pfn_base = vmemranges[i].start >> PAGE_SHIFT; for ( pfn = pfn_base; pfn < pfn_base+pages; pfn++ ) dom->p2m_host[pfn] = pfn; pfn_base_idx = pfn_base; while ( super_pages ) { uint64_t count = min_t(uint64_t, super_pages, SUPERPAGE_BATCH_SIZE); super_pages -= count; for ( pfn = pfn_base_idx, j = 0; pfn < pfn_base_idx + (count << SUPERPAGE_2MB_SHIFT); pfn += SUPERPAGE_2MB_NR_PFNS, j++ ) extents[j] = dom->p2m_host[pfn]; rc = xc_domain_populate_physmap(dom->xch, dom->guest_domid, count, SUPERPAGE_2MB_SHIFT, memflags, extents); if ( rc < 0 ) return rc; /* Expand the returned mfns into the p2m array. */ pfn = pfn_base_idx; for ( j = 0; j < rc; j++ ) { mfn = extents[j]; for ( k = 0; k < SUPERPAGE_2MB_NR_PFNS; k++, pfn++ ) dom->p2m_host[pfn] = mfn + k; } pfn_base_idx = pfn; } for ( j = pfn_base_idx - pfn_base; j < pages; j += allocsz ) { allocsz = min_t(uint64_t, 1024 * 1024, pages - j); rc = xc_domain_populate_physmap_exact(dom->xch, dom->guest_domid, allocsz, 0, memflags, &dom->p2m_host[pfn_base + j]); if ( rc ) { if ( pnode != XC_NUMA_NO_NODE ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: failed to allocate 0x%"PRIx64" pages (v=%d, p=%d)", __func__, pages, i, pnode); else xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: failed to allocate 0x%"PRIx64" pages", __func__, pages); return rc; } } rc = 0; } /* Ensure no unclaimed pages are left unused. * OK to call if hadn't done the earlier claim call. */ xc_domain_claim_pages(dom->xch, dom->guest_domid, 0 /* cancel claim */); return rc; } /* * Check whether there exists mmio hole in the specified memory range. * Returns 1 if exists, else returns 0. */ static int check_mmio_hole(uint64_t start, uint64_t memsize, uint64_t mmio_start, uint64_t mmio_size) { if ( start + memsize <= mmio_start || start >= mmio_start + mmio_size ) return 0; else return 1; } static int meminit_hvm(struct xc_dom_image *dom) { unsigned long i, vmemid, nr_pages = dom->total_pages; unsigned long p2m_size; unsigned long target_pages = dom->target_pages; unsigned long cur_pages, cur_pfn; int rc; unsigned long stat_normal_pages = 0, stat_2mb_pages = 0, stat_1gb_pages = 0; unsigned int memflags = 0; int claim_enabled = dom->claim_enabled; uint64_t total_pages; xen_vmemrange_t dummy_vmemrange[2]; unsigned int dummy_vnode_to_pnode[1]; xen_vmemrange_t *vmemranges; unsigned int *vnode_to_pnode; unsigned int nr_vmemranges, nr_vnodes; xc_interface *xch = dom->xch; uint32_t domid = dom->guest_domid; if ( nr_pages > target_pages ) memflags |= XENMEMF_populate_on_demand; if ( dom->nr_vmemranges == 0 ) { /* Build dummy vnode information * * Guest physical address space layout: * [0, hole_start) [hole_start, 4G) [4G, highmem_end) * * Of course if there is no high memory, the second vmemrange * has no effect on the actual result. */ dummy_vmemrange[0].start = 0; dummy_vmemrange[0].end = dom->lowmem_end; dummy_vmemrange[0].flags = 0; dummy_vmemrange[0].nid = 0; nr_vmemranges = 1; if ( dom->highmem_end > (1ULL << 32) ) { dummy_vmemrange[1].start = 1ULL << 32; dummy_vmemrange[1].end = dom->highmem_end; dummy_vmemrange[1].flags = 0; dummy_vmemrange[1].nid = 0; nr_vmemranges++; } dummy_vnode_to_pnode[0] = XC_NUMA_NO_NODE; nr_vnodes = 1; vmemranges = dummy_vmemrange; vnode_to_pnode = dummy_vnode_to_pnode; } else { if ( nr_pages > target_pages ) { DOMPRINTF("Cannot enable vNUMA and PoD at the same time"); goto error_out; } nr_vmemranges = dom->nr_vmemranges; nr_vnodes = dom->nr_vnodes; vmemranges = dom->vmemranges; vnode_to_pnode = dom->vnode_to_pnode; } total_pages = 0; p2m_size = 0; for ( i = 0; i < nr_vmemranges; i++ ) { total_pages += ((vmemranges[i].end - vmemranges[i].start) >> PAGE_SHIFT); p2m_size = p2m_size > (vmemranges[i].end >> PAGE_SHIFT) ? p2m_size : (vmemranges[i].end >> PAGE_SHIFT); } if ( total_pages != nr_pages ) { DOMPRINTF("vNUMA memory pages mismatch (0x%"PRIx64" != 0x%lx)", total_pages, nr_pages); goto error_out; } dom->p2m_size = p2m_size; dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * dom->p2m_size); if ( dom->p2m_host == NULL ) { DOMPRINTF("Could not allocate p2m"); goto error_out; } for ( i = 0; i < p2m_size; i++ ) dom->p2m_host[i] = ((xen_pfn_t)-1); for ( vmemid = 0; vmemid < nr_vmemranges; vmemid++ ) { uint64_t pfn; for ( pfn = vmemranges[vmemid].start >> PAGE_SHIFT; pfn < vmemranges[vmemid].end >> PAGE_SHIFT; pfn++ ) dom->p2m_host[pfn] = pfn; } /* * Try to claim pages for early warning of insufficient memory available. * This should go before xc_domain_set_pod_target, becuase that function * actually allocates memory for the guest. Claiming after memory has been * allocated is pointless. */ if ( claim_enabled ) { rc = xc_domain_claim_pages(xch, domid, target_pages - dom->vga_hole_size); if ( rc != 0 ) { DOMPRINTF("Could not allocate memory for HVM guest as we cannot claim memory!"); goto error_out; } } if ( memflags & XENMEMF_populate_on_demand ) { /* * Subtract VGA_HOLE_SIZE from target_pages for the VGA * "hole". Xen will adjust the PoD cache size so that domain * tot_pages will be target_pages - VGA_HOLE_SIZE after * this call. */ rc = xc_domain_set_pod_target(xch, domid, target_pages - dom->vga_hole_size, NULL, NULL, NULL); if ( rc != 0 ) { DOMPRINTF("Could not set PoD target for HVM guest.\n"); goto error_out; } } /* * Allocate memory for HVM guest, skipping VGA hole 0xA0000-0xC0000. * * We attempt to allocate 1GB pages if possible. It falls back on 2MB * pages if 1GB allocation fails. 4KB pages will be used eventually if * both fail. * * Under 2MB mode, we allocate pages in batches of no more than 8MB to * ensure that we can be preempted and hence dom0 remains responsive. */ if ( dom->device_model ) { rc = xc_domain_populate_physmap_exact( xch, domid, 0xa0, 0, memflags, &dom->p2m_host[0x00]); if ( rc != 0 ) { DOMPRINTF("Could not populate low memory (< 0xA0).\n"); goto error_out; } } stat_normal_pages = 0; for ( vmemid = 0; vmemid < nr_vmemranges; vmemid++ ) { unsigned int new_memflags = memflags; uint64_t end_pages; unsigned int vnode = vmemranges[vmemid].nid; unsigned int pnode = vnode_to_pnode[vnode]; if ( pnode != XC_NUMA_NO_NODE ) new_memflags |= XENMEMF_exact_node(pnode); end_pages = vmemranges[vmemid].end >> PAGE_SHIFT; /* * Consider vga hole belongs to the vmemrange that covers * 0xA0000-0xC0000. Note that 0x00000-0xA0000 is populated just * before this loop. */ if ( vmemranges[vmemid].start == 0 && dom->device_model ) { cur_pages = 0xc0; stat_normal_pages += 0xc0; } else cur_pages = vmemranges[vmemid].start >> PAGE_SHIFT; rc = 0; while ( (rc == 0) && (end_pages > cur_pages) ) { /* Clip count to maximum 1GB extent. */ unsigned long count = end_pages - cur_pages; unsigned long max_pages = SUPERPAGE_1GB_NR_PFNS; if ( count > max_pages ) count = max_pages; cur_pfn = dom->p2m_host[cur_pages]; /* Take care the corner cases of super page tails */ if ( ((cur_pfn & (SUPERPAGE_1GB_NR_PFNS-1)) != 0) && (count > (-cur_pfn & (SUPERPAGE_1GB_NR_PFNS-1))) ) count = -cur_pfn & (SUPERPAGE_1GB_NR_PFNS-1); else if ( ((count & (SUPERPAGE_1GB_NR_PFNS-1)) != 0) && (count > SUPERPAGE_1GB_NR_PFNS) ) count &= ~(SUPERPAGE_1GB_NR_PFNS - 1); /* Attemp to allocate 1GB super page. Because in each pass * we only allocate at most 1GB, we don't have to clip * super page boundaries. */ if ( ((count | cur_pfn) & (SUPERPAGE_1GB_NR_PFNS - 1)) == 0 && /* Check if there exists MMIO hole in the 1GB memory * range */ !check_mmio_hole(cur_pfn << PAGE_SHIFT, SUPERPAGE_1GB_NR_PFNS << PAGE_SHIFT, dom->mmio_start, dom->mmio_size) ) { long done; unsigned long nr_extents = count >> SUPERPAGE_1GB_SHIFT; xen_pfn_t sp_extents[nr_extents]; for ( i = 0; i < nr_extents; i++ ) sp_extents[i] = dom->p2m_host[cur_pages+(i< 0 ) { stat_1gb_pages += done; done <<= SUPERPAGE_1GB_SHIFT; cur_pages += done; count -= done; } } if ( count != 0 ) { /* Clip count to maximum 8MB extent. */ max_pages = SUPERPAGE_2MB_NR_PFNS * 4; if ( count > max_pages ) count = max_pages; /* Clip partial superpage extents to superpage * boundaries. */ if ( ((cur_pfn & (SUPERPAGE_2MB_NR_PFNS-1)) != 0) && (count > (-cur_pfn & (SUPERPAGE_2MB_NR_PFNS-1))) ) count = -cur_pfn & (SUPERPAGE_2MB_NR_PFNS-1); else if ( ((count & (SUPERPAGE_2MB_NR_PFNS-1)) != 0) && (count > SUPERPAGE_2MB_NR_PFNS) ) count &= ~(SUPERPAGE_2MB_NR_PFNS - 1); /* clip non-s.p. tail */ /* Attempt to allocate superpage extents. */ if ( ((count | cur_pfn) & (SUPERPAGE_2MB_NR_PFNS - 1)) == 0 ) { long done; unsigned long nr_extents = count >> SUPERPAGE_2MB_SHIFT; xen_pfn_t sp_extents[nr_extents]; for ( i = 0; i < nr_extents; i++ ) sp_extents[i] = dom->p2m_host[cur_pages+(i< 0 ) { stat_2mb_pages += done; done <<= SUPERPAGE_2MB_SHIFT; cur_pages += done; count -= done; } } } /* Fall back to 4kB extents. */ if ( count != 0 ) { rc = xc_domain_populate_physmap_exact( xch, domid, count, 0, new_memflags, &dom->p2m_host[cur_pages]); cur_pages += count; stat_normal_pages += count; } } if ( rc != 0 ) { DOMPRINTF("Could not allocate memory for HVM guest."); goto error_out; } } DPRINTF("PHYSICAL MEMORY ALLOCATION:\n"); DPRINTF(" 4KB PAGES: 0x%016lx\n", stat_normal_pages); DPRINTF(" 2MB PAGES: 0x%016lx\n", stat_2mb_pages); DPRINTF(" 1GB PAGES: 0x%016lx\n", stat_1gb_pages); rc = 0; goto out; error_out: rc = -1; out: /* ensure no unclaimed pages are left unused */ xc_domain_claim_pages(xch, domid, 0 /* cancels the claim */); return rc; } /* ------------------------------------------------------------------------ */ static int bootearly(struct xc_dom_image *dom) { if ( dom->container_type == XC_DOM_PV_CONTAINER && elf_xen_feature_get(XENFEAT_auto_translated_physmap, dom->f_active) ) { DOMPRINTF("PV Autotranslate guests no longer supported"); errno = EOPNOTSUPP; return -1; } return 0; } static int bootlate_pv(struct xc_dom_image *dom) { static const struct { char *guest; unsigned long pgd_type; } types[] = { { "xen-3.0-x86_32", MMUEXT_PIN_L2_TABLE}, { "xen-3.0-x86_32p", MMUEXT_PIN_L3_TABLE}, { "xen-3.0-x86_64", MMUEXT_PIN_L4_TABLE}, }; unsigned long pgd_type = 0; shared_info_t *shared_info; xen_pfn_t shinfo; int i, rc; for ( i = 0; i < ARRAY_SIZE(types); i++ ) if ( !strcmp(types[i].guest, dom->guest_type) ) pgd_type = types[i].pgd_type; /* Drop references to all initial page tables before pinning. */ xc_dom_unmap_one(dom, dom->pgtables_seg.pfn); xc_dom_unmap_one(dom, dom->p2m_seg.pfn); rc = pin_table(dom->xch, pgd_type, xc_dom_p2m(dom, dom->pgtables_seg.pfn), dom->guest_domid); if ( rc != 0 ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: pin_table failed (pfn 0x%" PRIpfn ", rc=%d)", __FUNCTION__, dom->pgtables_seg.pfn, rc); return rc; } shinfo = dom->shared_info_mfn; /* setup shared_info page */ DOMPRINTF("%s: shared_info: pfn 0x%" PRIpfn ", mfn 0x%" PRIpfn "", __FUNCTION__, dom->shared_info_pfn, dom->shared_info_mfn); shared_info = xc_map_foreign_range(dom->xch, dom->guest_domid, PAGE_SIZE_X86, PROT_READ | PROT_WRITE, shinfo); if ( shared_info == NULL ) return -1; dom->arch_hooks->shared_info(dom, shared_info); munmap(shared_info, PAGE_SIZE_X86); return 0; } static int alloc_pgtables_hvm(struct xc_dom_image *dom) { DOMPRINTF("%s: doing nothing", __func__); return 0; } /* * The memory layout of the start_info page and the modules, and where the * addresses are stored: * * /----------------------------------\ * | struct hvm_start_info | * +----------------------------------+ <- start_info->modlist_paddr * | struct hvm_modlist_entry[0] | * +----------------------------------+ * | struct hvm_modlist_entry[1] | * +----------------------------------+ <- modlist[0].cmdline_paddr * | cmdline of module 0 | * | char[HVMLOADER_MODULE_NAME_SIZE] | * +----------------------------------+ <- modlist[1].cmdline_paddr * | cmdline of module 1 | * +----------------------------------+ */ static void add_module_to_list(struct xc_dom_image *dom, struct xc_hvm_firmware_module *module, const char *name, struct hvm_modlist_entry *modlist, struct hvm_start_info *start_info) { uint32_t index = start_info->nr_modules; void *modules_cmdline_start = modlist + HVMLOADER_MODULE_MAX_COUNT; uint64_t modlist_paddr = (dom->start_info_seg.pfn << PAGE_SHIFT) + ((uintptr_t)modlist - (uintptr_t)start_info); uint64_t modules_cmdline_paddr = modlist_paddr + sizeof(struct hvm_modlist_entry) * HVMLOADER_MODULE_MAX_COUNT; if ( module->length == 0 ) return; assert(start_info->nr_modules < HVMLOADER_MODULE_MAX_COUNT); assert(strnlen(name, HVMLOADER_MODULE_NAME_SIZE) < HVMLOADER_MODULE_NAME_SIZE); modlist[index].paddr = module->guest_addr_out; modlist[index].size = module->length; strncpy(modules_cmdline_start + HVMLOADER_MODULE_NAME_SIZE * index, name, HVMLOADER_MODULE_NAME_SIZE); modlist[index].cmdline_paddr = modules_cmdline_paddr + HVMLOADER_MODULE_NAME_SIZE * index; start_info->nr_modules++; } static int bootlate_hvm(struct xc_dom_image *dom) { uint32_t domid = dom->guest_domid; xc_interface *xch = dom->xch; struct hvm_start_info *start_info; size_t start_info_size; struct hvm_modlist_entry *modlist; start_info_size = sizeof(*start_info) + dom->cmdline_size; if ( dom->ramdisk_blob ) start_info_size += sizeof(struct hvm_modlist_entry); if ( start_info_size > dom->start_info_seg.pages << XC_DOM_PAGE_SHIFT(dom) ) { DOMPRINTF("Trying to map beyond start_info_seg"); return -1; } start_info = xc_map_foreign_range(xch, domid, start_info_size, PROT_READ | PROT_WRITE, dom->start_info_seg.pfn); if ( start_info == NULL ) { DOMPRINTF("Unable to map HVM start info page"); return -1; } modlist = (void*)(start_info + 1) + dom->cmdline_size; if ( !dom->device_model ) { if ( dom->cmdline ) { char *cmdline = (void*)(start_info + 1); strncpy(cmdline, dom->cmdline, dom->cmdline_size); start_info->cmdline_paddr = (dom->start_info_seg.pfn << PAGE_SHIFT) + ((uintptr_t)cmdline - (uintptr_t)start_info); } if ( dom->ramdisk_blob ) { modlist[0].paddr = dom->ramdisk_seg.vstart - dom->parms.virt_base; modlist[0].size = dom->ramdisk_seg.vend - dom->ramdisk_seg.vstart; start_info->nr_modules = 1; } /* ACPI module 0 is the RSDP */ start_info->rsdp_paddr = dom->acpi_modules[0].guest_addr_out ? : 0; } else { add_module_to_list(dom, &dom->system_firmware_module, "firmware", modlist, start_info); } if ( start_info->nr_modules ) { start_info->modlist_paddr = (dom->start_info_seg.pfn << PAGE_SHIFT) + ((uintptr_t)modlist - (uintptr_t)start_info); } start_info->magic = XEN_HVM_START_MAGIC_VALUE; munmap(start_info, start_info_size); if ( dom->device_model ) { void *hvm_info_page; if ( (hvm_info_page = xc_map_foreign_range( xch, domid, PAGE_SIZE, PROT_READ | PROT_WRITE, HVM_INFO_PFN)) == NULL ) return -1; build_hvm_info(hvm_info_page, dom); munmap(hvm_info_page, PAGE_SIZE); } return 0; } bool xc_dom_translated(const struct xc_dom_image *dom) { /* HVM guests are translated. PV guests are not. */ return dom->container_type == XC_DOM_HVM_CONTAINER; } /* ------------------------------------------------------------------------ */ static struct xc_dom_arch xc_dom_32_pae = { .guest_type = "xen-3.0-x86_32p", .native_protocol = XEN_IO_PROTO_ABI_X86_32, .page_shift = PAGE_SHIFT_X86, .sizeof_pfn = 4, .p2m_base_supported = 0, .arch_private_size = sizeof(struct xc_dom_image_x86), .alloc_magic_pages = alloc_magic_pages, .alloc_pgtables = alloc_pgtables_x86_32_pae, .alloc_p2m_list = alloc_p2m_list_x86_32, .setup_pgtables = setup_pgtables_x86_32_pae, .start_info = start_info_x86_32, .shared_info = shared_info_x86_32, .vcpu = vcpu_x86_32, .meminit = meminit_pv, .bootearly = bootearly, .bootlate = bootlate_pv, }; static struct xc_dom_arch xc_dom_64 = { .guest_type = "xen-3.0-x86_64", .native_protocol = XEN_IO_PROTO_ABI_X86_64, .page_shift = PAGE_SHIFT_X86, .sizeof_pfn = 8, .p2m_base_supported = 1, .arch_private_size = sizeof(struct xc_dom_image_x86), .alloc_magic_pages = alloc_magic_pages, .alloc_pgtables = alloc_pgtables_x86_64, .alloc_p2m_list = alloc_p2m_list_x86_64, .setup_pgtables = setup_pgtables_x86_64, .start_info = start_info_x86_64, .shared_info = shared_info_x86_64, .vcpu = vcpu_x86_64, .meminit = meminit_pv, .bootearly = bootearly, .bootlate = bootlate_pv, }; static struct xc_dom_arch xc_hvm_32 = { .guest_type = "hvm-3.0-x86_32", .native_protocol = XEN_IO_PROTO_ABI_X86_32, .page_shift = PAGE_SHIFT_X86, .sizeof_pfn = 4, .alloc_magic_pages = alloc_magic_pages_hvm, .alloc_pgtables = alloc_pgtables_hvm, .setup_pgtables = NULL, .start_info = NULL, .shared_info = NULL, .vcpu = vcpu_hvm, .meminit = meminit_hvm, .bootearly = bootearly, .bootlate = bootlate_hvm, }; static void __init register_arch_hooks(void) { xc_dom_register_arch_hooks(&xc_dom_32_pae); xc_dom_register_arch_hooks(&xc_dom_64); xc_dom_register_arch_hooks(&xc_hvm_32); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xg_private.h0000664000175000017500000001077213256712137015246 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #ifndef XG_PRIVATE_H #define XG_PRIVATE_H #include #include #include #include #include #include #include #include #include #include "xc_private.h" #include "xenguest.h" #include #include #ifndef ELFSIZE #include #if UINT_MAX == ULONG_MAX #define ELFSIZE 32 #else #define ELFSIZE 64 #endif #endif char *xc_read_image(xc_interface *xch, const char *filename, unsigned long *size); char *xc_inflate_buffer(xc_interface *xch, const char *in_buf, unsigned long in_size, unsigned long *out_size); unsigned long csum_page (void * page); #define _PAGE_PRESENT 0x001 #define _PAGE_RW 0x002 #define _PAGE_USER 0x004 #define _PAGE_PWT 0x008 #define _PAGE_PCD 0x010 #define _PAGE_ACCESSED 0x020 #define _PAGE_DIRTY 0x040 #define _PAGE_PAT 0x080 #define _PAGE_PSE 0x080 #define _PAGE_GLOBAL 0x100 #define VIRT_BITS_I386 32 #define VIRT_BITS_X86_64 48 #define PGTBL_LEVELS_I386 3 #define PGTBL_LEVELS_X86_64 4 #define PGTBL_LEVEL_SHIFT_X86 9 #define L1_PAGETABLE_SHIFT_PAE 12 #define L2_PAGETABLE_SHIFT_PAE 21 #define L3_PAGETABLE_SHIFT_PAE 30 #define L1_PAGETABLE_ENTRIES_PAE 512 #define L2_PAGETABLE_ENTRIES_PAE 512 #define L3_PAGETABLE_ENTRIES_PAE 4 #define L1_PAGETABLE_SHIFT_X86_64 12 #define L2_PAGETABLE_SHIFT_X86_64 21 #define L3_PAGETABLE_SHIFT_X86_64 30 #define L4_PAGETABLE_SHIFT_X86_64 39 #define L1_PAGETABLE_ENTRIES_X86_64 512 #define L2_PAGETABLE_ENTRIES_X86_64 512 #define L3_PAGETABLE_ENTRIES_X86_64 512 #define L4_PAGETABLE_ENTRIES_X86_64 512 typedef uint64_t x86_pgentry_t; #define PAGE_SHIFT_ARM 12 #define PAGE_SIZE_ARM (1UL << PAGE_SHIFT_ARM) #define PAGE_MASK_ARM (~(PAGE_SIZE_ARM-1)) #define PAGE_SHIFT_X86 12 #define PAGE_SIZE_X86 (1UL << PAGE_SHIFT_X86) #define PAGE_MASK_X86 (~(PAGE_SIZE_X86-1)) #define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1)) #define NRPAGES(x) (ROUNDUP(x, PAGE_SHIFT) >> PAGE_SHIFT) /* XXX SMH: following skanky macros rely on variable p2m_size being set */ /* XXX TJD: also, "guest_width" should be the guest's sizeof(unsigned long) */ struct domain_info_context { unsigned int guest_width; unsigned long p2m_size; }; static inline xen_pfn_t xc_pfn_to_mfn(xen_pfn_t pfn, xen_pfn_t *p2m, unsigned gwidth) { if ( gwidth == sizeof(uint64_t) ) /* 64 bit guest. Need to truncate their pfns for 32 bit toolstacks. */ return ((uint64_t *)p2m)[pfn]; else { /* 32 bit guest. Need to expand INVALID_MFN for 64 bit toolstacks. */ uint32_t mfn = ((uint32_t *)p2m)[pfn]; return mfn == ~0U ? INVALID_MFN : mfn; } } /* Number of xen_pfn_t in a page */ #define FPP (PAGE_SIZE/(dinfo->guest_width)) /* Number of entries in the pfn_to_mfn_frame_list_list */ #define P2M_FLL_ENTRIES (((dinfo->p2m_size)+(FPP*FPP)-1)/(FPP*FPP)) /* Number of entries in the pfn_to_mfn_frame_list */ #define P2M_FL_ENTRIES (((dinfo->p2m_size)+FPP-1)/FPP) /* Size in bytes of the pfn_to_mfn_frame_list */ #define P2M_GUEST_FL_SIZE ((P2M_FL_ENTRIES) * (dinfo->guest_width)) #define P2M_TOOLS_FL_SIZE ((P2M_FL_ENTRIES) * \ max_t(size_t, sizeof(xen_pfn_t), dinfo->guest_width)) /* Masks for PTE<->PFN conversions */ #define MADDR_BITS_X86 ((dinfo->guest_width == 8) ? 52 : 44) #define MFN_MASK_X86 ((1ULL << (MADDR_BITS_X86 - PAGE_SHIFT_X86)) - 1) #define MADDR_MASK_X86 (MFN_MASK_X86 << PAGE_SHIFT_X86) int pin_table(xc_interface *xch, unsigned int type, unsigned long mfn, domid_t dom); #endif /* XG_PRIVATE_H */ xen-4.9.2/tools/libxc/xc_dom_decompress_unsafe_bzip2.c0000664000175000017500000000047113256712137021230 0ustar smbsmb#include #include #include #include "xg_private.h" #include "xc_dom_decompress_unsafe.h" #include "../../xen/common/bunzip2.c" int xc_try_bzip2_decode( struct xc_dom_image *dom, void **blob, size_t *size) { return xc_dom_decompress_unsafe(bunzip2, dom, blob, size); } xen-4.9.2/tools/libxc/xc_pagetab.c0000664000175000017500000000725513256712137015170 0ustar smbsmb/****************************************************************************** * xc_pagetab.c * * Function to translate virtual to physical addresses. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include #define CR0_PG 0x80000000 #define CR4_PAE 0x20 #define PTE_PSE 0x80 #define EFER_LMA 0x400 unsigned long xc_translate_foreign_address(xc_interface *xch, uint32_t dom, int vcpu, unsigned long long virt) { xc_dominfo_t dominfo; uint64_t paddr, mask, pte = 0; int size, level, pt_levels = 2; void *map; if (xc_domain_getinfo(xch, dom, 1, &dominfo) != 1 || dominfo.domid != dom) return 0; /* What kind of paging are we dealing with? */ if (dominfo.hvm) { struct hvm_hw_cpu ctx; if (xc_domain_hvm_getcontext_partial(xch, dom, HVM_SAVE_CODE(CPU), vcpu, &ctx, sizeof ctx) != 0) return 0; if (!(ctx.cr0 & CR0_PG)) return virt >> PAGE_SHIFT; pt_levels = (ctx.msr_efer&EFER_LMA) ? 4 : (ctx.cr4&CR4_PAE) ? 3 : 2; paddr = ctx.cr3 & ((pt_levels == 3) ? ~0x1full : ~0xfffull); } else { unsigned int gwidth; vcpu_guest_context_any_t ctx; if (xc_vcpu_getcontext(xch, dom, vcpu, &ctx) != 0) return 0; if (xc_domain_get_guest_width(xch, dom, &gwidth) != 0) return 0; if (gwidth == 8) { pt_levels = 4; paddr = (uint64_t)xen_cr3_to_pfn_x86_64(ctx.x64.ctrlreg[3]) << PAGE_SHIFT; } else { pt_levels = 3; paddr = (uint64_t)xen_cr3_to_pfn_x86_32(ctx.x32.ctrlreg[3]) << PAGE_SHIFT; } } if (pt_levels == 4) { virt &= 0x0000ffffffffffffull; mask = 0x0000ff8000000000ull; } else if (pt_levels == 3) { virt &= 0x00000000ffffffffull; mask = 0x0000007fc0000000ull; } else { virt &= 0x00000000ffffffffull; mask = 0x00000000ffc00000ull; } size = (pt_levels == 2 ? 4 : 8); /* Walk the pagetables */ for (level = pt_levels; level > 0; level--) { paddr += ((virt & mask) >> (xc_ffs64(mask) - 1)) * size; map = xc_map_foreign_range(xch, dom, PAGE_SIZE, PROT_READ, paddr >>PAGE_SHIFT); if (!map) return 0; memcpy(&pte, map + (paddr & (PAGE_SIZE - 1)), size); munmap(map, PAGE_SIZE); if (!(pte & 1)) { errno = EADDRNOTAVAIL; return 0; } paddr = pte & 0x000ffffffffff000ull; if ((level == 2 || (level == 3 && pt_levels == 4)) && (pte & PTE_PSE)) { mask = ((mask ^ ~-mask) >> 1); /* All bits below first set bit */ return ((paddr & ~mask) | (virt & mask)) >> PAGE_SHIFT; } mask >>= (pt_levels == 2 ? 10 : 9); } return paddr >> PAGE_SHIFT; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_compression.c0000664000175000017500000003771113256712137016126 0ustar smbsmb/****************************************************************************** * xc_compression.c * * Checkpoint Compression using Page Delta Algorithm. * - A LRU cache of recently dirtied guest pages is maintained. * - For each dirty guest page in the checkpoint, if a previous version of the * page exists in the cache, XOR both pages and send the non-zero sections * to the receiver. The cache is then updated with the newer copy of guest page. * - The receiver will XOR the non-zero sections against its copy of the guest * page, thereby bringing the guest page up-to-date with the sender side. * * Copyright (c) 2011 Shriram Rajagopalan (rshriram@cs.ubc.ca). * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * */ #include #include #include #include #include #include "xc_private.h" #include "xenctrl.h" #include "xg_save_restore.h" #include "xg_private.h" #include "xc_dom.h" /* Page Cache for Delta Compression*/ #define DELTA_CACHE_SIZE (XC_PAGE_SIZE * 8192) /* Internal page buffer to hold dirty pages of a checkpoint, * to be compressed after the domain is resumed for execution. */ #define PAGE_BUFFER_SIZE (XC_PAGE_SIZE * 8192) struct cache_page { char *page; xen_pfn_t pfn; struct cache_page *next; struct cache_page *prev; }; struct compression_ctx { /* compression buffer - holds compressed data */ char *compbuf; unsigned long compbuf_size; unsigned long compbuf_pos; /* Page buffer to hold pages to be compressed */ char *inputbuf; /* pfns of pages to be compressed */ xen_pfn_t *sendbuf_pfns; unsigned int pfns_len; unsigned int pfns_index; /* Compression Cache (LRU) */ char *cache_base; struct cache_page **pfn2cache; struct cache_page *cache; struct cache_page *page_list_head; struct cache_page *page_list_tail; unsigned long dom_pfnlist_size; }; #define RUNFLAG 0 #define SKIPFLAG ((char)128) #define FLAGMASK SKIPFLAG #define LENMASK ((char)127) /* * see xg_save_restore.h for details on the compressed stream format. * delta size = 4 bytes. * run header = 1 byte (1 bit for runtype, 7bits for run length). * i.e maximum size of a run = 127 * 4 = 508 bytes. * Worst case compression: Entire page has changed. * In the worst case, the size of the compressed page is * 8 runs of 508 bytes + 1 run of 32 bytes + 9 run headers * = 4105 bytes. * We could detect this worst case and send the entire page with a * FULL_PAGE marker, reducing the total size to 4097 bytes. The cost * of this size reduction is an additional memcpy, on top of two previous * memcpy (to the compressed stream and the cache page in the for loop). * * We might as well sacrifice an extra 8 bytes instead of a memcpy. */ #define WORST_COMP_PAGE_SIZE (XC_PAGE_SIZE + 9) /* * A zero length skip indicates full page. */ #define EMPTY_PAGE 0 #define FULL_PAGE SKIPFLAG #define FULL_PAGE_SIZE (XC_PAGE_SIZE + 1) #define MAX_DELTAS (XC_PAGE_SIZE/sizeof(uint32_t)) /* * Add a pagetable page or a new page (uncached) * if srcpage is a pagetable page, cache_page is null. * if srcpage is a page that was not previously in the cache, * cache_page points to a free page slot in the cache where * this new page can be copied to. */ static int add_full_page(comp_ctx *ctx, char *srcpage, char *cache_page) { char *dest = (ctx->compbuf + ctx->compbuf_pos); if ( (ctx->compbuf_pos + FULL_PAGE_SIZE) > ctx->compbuf_size) return -1; if (cache_page) memcpy(cache_page, srcpage, XC_PAGE_SIZE); dest[0] = FULL_PAGE; memcpy(&dest[1], srcpage, XC_PAGE_SIZE); ctx->compbuf_pos += FULL_PAGE_SIZE; return FULL_PAGE_SIZE; } static int compress_page(comp_ctx *ctx, char *srcpage, char *cache_page) { char *dest = (ctx->compbuf + ctx->compbuf_pos); uint32_t *new, *old; int off, runptr = 0; int wascopying = 0, copying = 0, bytes_skipped = 0; int complen = 0, pageoff = 0, runbytes = 0; char runlen = 0; if ( (ctx->compbuf_pos + WORST_COMP_PAGE_SIZE) > ctx->compbuf_size) return -1; /* * There are no alignment issues here since srcpage is * domU's page passed from xc_domain_save and cache_page is * a ptr to cache page (cache is page aligned). */ new = (uint32_t*)srcpage; old = (uint32_t*)cache_page; for (off = 0; off <= MAX_DELTAS; off++) { /* * At (off == MAX_DELTAS), we are processing the last run * in the page. Since there is no XORing, make wascopying != copying * to satisfy the if-block below. */ copying = ((off < MAX_DELTAS) ? (old[off] != new[off]) : !wascopying); if (runlen) { /* switching between run types or current run is full */ if ( (wascopying != copying) || (runlen == LENMASK) ) { runbytes = runlen * sizeof(uint32_t); runlen |= (wascopying ? RUNFLAG : SKIPFLAG); dest[complen++] = runlen; if (wascopying) /* RUNFLAG */ { pageoff = runptr * sizeof(uint32_t); memcpy(dest + complen, srcpage + pageoff, runbytes); memcpy(cache_page + pageoff, srcpage + pageoff, runbytes); complen += runbytes; } else /* SKIPFLAG */ { bytes_skipped += runbytes; } runlen = 0; runptr = off; } } runlen++; wascopying = copying; } /* * Check for empty page. */ if (bytes_skipped == XC_PAGE_SIZE) { complen = 1; dest[0] = EMPTY_PAGE; } ctx->compbuf_pos += complen; return complen; } static char *get_cache_page(comp_ctx *ctx, xen_pfn_t pfn, int *israw) { struct cache_page *item = NULL; item = ctx->pfn2cache[pfn]; if (!item) { *israw = 1; /* If the list is full, evict a page from the tail end. */ item = ctx->page_list_tail; if (item->pfn != INVALID_PFN) ctx->pfn2cache[item->pfn] = NULL; item->pfn = pfn; ctx->pfn2cache[pfn] = item; } /* if requested item is in cache move to head of list */ if (item != ctx->page_list_head) { if (item == ctx->page_list_tail) { /* item at tail of list. */ ctx->page_list_tail = item->prev; (ctx->page_list_tail)->next = NULL; } else { /* item in middle of list */ item->prev->next = item->next; item->next->prev = item->prev; } item->prev = NULL; item->next = ctx->page_list_head; (ctx->page_list_head)->prev = item; ctx->page_list_head = item; } return (ctx->page_list_head)->page; } /* Remove pagetable pages from cache and move to tail, as free pages */ static void invalidate_cache_page(comp_ctx *ctx, xen_pfn_t pfn) { struct cache_page *item = NULL; item = ctx->pfn2cache[pfn]; if (item) { if (item != ctx->page_list_tail) { /* item at head of list */ if (item == ctx->page_list_head) { ctx->page_list_head = (ctx->page_list_head)->next; (ctx->page_list_head)->prev = NULL; } else /* item in middle of list */ { item->prev->next = item->next; item->next->prev = item->prev; } item->next = NULL; item->prev = ctx->page_list_tail; (ctx->page_list_tail)->next = item; ctx->page_list_tail = item; } ctx->pfn2cache[pfn] = NULL; (ctx->page_list_tail)->pfn = INVALID_PFN; } } int xc_compression_add_page(xc_interface *xch, comp_ctx *ctx, char *page, xen_pfn_t pfn, int israw) { if (pfn > ctx->dom_pfnlist_size) { ERROR("Invalid pfn passed into " "xc_compression_add_page %" PRIpfn "\n", pfn); return -2; } /* pagetable page */ if (israw) invalidate_cache_page(ctx, pfn); ctx->sendbuf_pfns[ctx->pfns_len] = israw ? INVALID_PFN : pfn; memcpy(ctx->inputbuf + ctx->pfns_len * XC_PAGE_SIZE, page, XC_PAGE_SIZE); ctx->pfns_len++; /* check if we have run out of space. If so, * we need to synchronously compress the pages and flush them out */ if (ctx->pfns_len == NRPAGES(PAGE_BUFFER_SIZE)) return -1; return 0; } int xc_compression_compress_pages(xc_interface *xch, comp_ctx *ctx, char *compbuf, unsigned long compbuf_size, unsigned long *compbuf_len) { char *cache_copy = NULL, *current_page = NULL; int israw, rc = 1; if (!ctx->pfns_len || (ctx->pfns_index == ctx->pfns_len)) { ctx->pfns_len = ctx->pfns_index = 0; return 0; } ctx->compbuf_pos = 0; ctx->compbuf = compbuf; ctx->compbuf_size = compbuf_size; for (; ctx->pfns_index < ctx->pfns_len; ctx->pfns_index++) { israw = 0; cache_copy = NULL; current_page = ctx->inputbuf + ctx->pfns_index * XC_PAGE_SIZE; if (ctx->sendbuf_pfns[ctx->pfns_index] == INVALID_PFN) israw = 1; else cache_copy = get_cache_page(ctx, ctx->sendbuf_pfns[ctx->pfns_index], &israw); if (israw) rc = (add_full_page(ctx, current_page, cache_copy) >= 0); else rc = (compress_page(ctx, current_page, cache_copy) >= 0); if ( !rc ) { /* Out of space in outbuf! flush and come back */ rc = -1; break; } } if (compbuf_len) *compbuf_len = ctx->compbuf_pos; return rc; } inline void xc_compression_reset_pagebuf(xc_interface *xch, comp_ctx *ctx) { ctx->pfns_index = ctx->pfns_len = 0; } int xc_compression_uncompress_page(xc_interface *xch, char *compbuf, unsigned long compbuf_size, unsigned long *compbuf_pos, char *destpage) { unsigned long pos; unsigned int len = 0, pagepos = 0; char flag; pos = *compbuf_pos; if (pos >= compbuf_size) { ERROR("Out of bounds exception in compression buffer (a):" "read ptr:%lu, bufsize = %lu\n", *compbuf_pos, compbuf_size); return -1; } switch (compbuf[pos]) { case EMPTY_PAGE: pos++; break; case FULL_PAGE: { /* Check if the input buffer has 4KB of data */ if ((pos + FULL_PAGE_SIZE) > compbuf_size) { ERROR("Out of bounds exception in compression buffer (b):" "read ptr = %lu, bufsize = %lu\n", *compbuf_pos, compbuf_size); return -1; } memcpy(destpage, &compbuf[pos + 1], XC_PAGE_SIZE); pos += FULL_PAGE_SIZE; } break; default: /* Normal page with one or more runs */ { do { flag = compbuf[pos] & FLAGMASK; len = (compbuf[pos] & LENMASK) * sizeof(uint32_t); /* Sanity Check: Zero-length runs are allowed only for * FULL_PAGE and EMPTY_PAGE. */ if (!len) { ERROR("Zero length run encountered for normal page: " "buffer (d):read ptr = %lu, flag = %u, " "bufsize = %lu, pagepos = %u\n", pos, (unsigned int)flag, compbuf_size, pagepos); return -1; } pos++; if (flag == RUNFLAG) { /* Check if the input buffer has len bytes of data * and whether it would fit in the destination page. */ if (((pos + len) > compbuf_size) || ((pagepos + len) > XC_PAGE_SIZE)) { ERROR("Out of bounds exception in compression " "buffer (c):read ptr = %lu, runlen = %u, " "bufsize = %lu, pagepos = %u\n", pos, len, compbuf_size, pagepos); return -1; } memcpy(&destpage[pagepos], &compbuf[pos], len); pos += len; } pagepos += len; } while ((pagepos < XC_PAGE_SIZE) && (pos < compbuf_size)); /* Make sure we have copied/skipped 4KB worth of data */ if (pagepos != XC_PAGE_SIZE) { ERROR("Invalid data in compression buffer:" "read ptr = %lu, bufsize = %lu, pagepos = %u\n", pos, compbuf_size, pagepos); return -1; } } } *compbuf_pos = pos; return 0; } void xc_compression_free_context(xc_interface *xch, comp_ctx *ctx) { if (!ctx) return; free(ctx->inputbuf); free(ctx->sendbuf_pfns); free(ctx->cache_base); free(ctx->pfn2cache); free(ctx->cache); free(ctx); } comp_ctx *xc_compression_create_context(xc_interface *xch, unsigned long p2m_size) { unsigned long i; comp_ctx *ctx = NULL; unsigned long num_cache_pages = DELTA_CACHE_SIZE/XC_PAGE_SIZE; ctx = (comp_ctx *)malloc(sizeof(comp_ctx)); if (!ctx) { ERROR("Failed to allocate compression_ctx\n"); goto error; } memset(ctx, 0, sizeof(comp_ctx)); ctx->inputbuf = xc_memalign(xch, XC_PAGE_SIZE, PAGE_BUFFER_SIZE); if (!ctx->inputbuf) { ERROR("Failed to allocate page buffer\n"); goto error; } ctx->cache_base = xc_memalign(xch, XC_PAGE_SIZE, DELTA_CACHE_SIZE); if (!ctx->cache_base) { ERROR("Failed to allocate delta cache\n"); goto error; } ctx->sendbuf_pfns = malloc(NRPAGES(PAGE_BUFFER_SIZE) * sizeof(xen_pfn_t)); if (!ctx->sendbuf_pfns) { ERROR("Could not alloc sendbuf_pfns\n"); goto error; } memset(ctx->sendbuf_pfns, -1, NRPAGES(PAGE_BUFFER_SIZE) * sizeof(xen_pfn_t)); ctx->pfn2cache = calloc(p2m_size, sizeof(struct cache_page *)); if (!ctx->pfn2cache) { ERROR("Could not alloc pfn2cache map\n"); goto error; } ctx->cache = malloc(num_cache_pages * sizeof(struct cache_page)); if (!ctx->cache) { ERROR("Could not alloc compression cache\n"); goto error; } for (i = 0; i < num_cache_pages; i++) { ctx->cache[i].pfn = INVALID_PFN; ctx->cache[i].page = ctx->cache_base + i * XC_PAGE_SIZE; ctx->cache[i].prev = (i == 0) ? NULL : &(ctx->cache[i - 1]); ctx->cache[i].next = ((i+1) == num_cache_pages)? NULL : &(ctx->cache[i + 1]); } ctx->page_list_head = &(ctx->cache[0]); ctx->page_list_tail = &(ctx->cache[num_cache_pages -1]); ctx->dom_pfnlist_size = p2m_size; return ctx; error: xc_compression_free_context(xch, ctx); return NULL; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_common_x86.h0000664000175000017500000000100013256712137016251 0ustar smbsmb#ifndef __COMMON_X86__H #define __COMMON_X86__H #include "xc_sr_common.h" /* * Obtains a domains TSC information from Xen and writes a TSC_INFO record * into the stream. */ int write_tsc_info(struct xc_sr_context *ctx); /* * Parses a TSC_INFO record and applies the result to the domain. */ int handle_tsc_info(struct xc_sr_context *ctx, struct xc_sr_record *rec); #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xenguest.pc.in0000664000175000017500000000042313256712137015510 0ustar smbsmbprefix=@@prefix@@ includedir=@@incdir@@ libdir=@@libdir@@ Name: Xenguest Description: The Xenguest library for Xen hypervisor Version: @@version@@ Cflags: -I${includedir} Libs: @@libsflag@@${libdir} -lxenguest Requires.private: xentoollog,xencall,xenforeignmemory,xenevtchn xen-4.9.2/tools/libxc/xencontrol.pc.in0000664000175000017500000000047713256712137016052 0ustar smbsmbprefix=@@prefix@@ includedir=@@incdir@@ libdir=@@libdir@@ Name: Xencontrol Description: The Xencontrol library for Xen hypervisor Version: @@version@@ Cflags: -I${includedir} @@cflagslocal@@ Libs: @@libsflag@@${libdir} -lxenctrl Requires.private: xenevtchn,xengnttab,xencall,xenforeignmemory,xendevicemodel,xentoollog xen-4.9.2/tools/libxc/xc_arinc653.c0000664000175000017500000000543213256712137015112 0ustar smbsmb/****************************************************************************** * xc_arinc653.c * * XC interface to the ARINC653 scheduler * * 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. * * Copyright (c) 2010 DornerWorks, Ltd. */ #include "xc_private.h" int xc_sched_arinc653_schedule_set( xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_arinc653_schedule *schedule) { int rc; DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE( schedule, sizeof(*schedule), XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, schedule) ) return -1; sysctl.cmd = XEN_SYSCTL_scheduler_op; sysctl.u.scheduler_op.cpupool_id = cpupool_id; sysctl.u.scheduler_op.sched_id = XEN_SCHEDULER_ARINC653; sysctl.u.scheduler_op.cmd = XEN_SYSCTL_SCHEDOP_putinfo; set_xen_guest_handle(sysctl.u.scheduler_op.u.sched_arinc653.schedule, schedule); rc = do_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, schedule); return rc; } int xc_sched_arinc653_schedule_get( xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_arinc653_schedule *schedule) { int rc; DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE( schedule, sizeof(*schedule), XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( xc_hypercall_bounce_pre(xch, schedule) ) return -1; sysctl.cmd = XEN_SYSCTL_scheduler_op; sysctl.u.scheduler_op.cpupool_id = cpupool_id; sysctl.u.scheduler_op.sched_id = XEN_SCHEDULER_ARINC653; sysctl.u.scheduler_op.cmd = XEN_SYSCTL_SCHEDOP_getinfo; set_xen_guest_handle(sysctl.u.scheduler_op.u.sched_arinc653.schedule, schedule); rc = do_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, schedule); return rc; } xen-4.9.2/tools/libxc/xc_dom_elfloader.c0000664000175000017500000001541113256712137016352 0ustar smbsmb/* * Xen domain builder -- ELF bits. * * Parse and load ELF kernel images. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * written 2006 by Gerd Hoffmann . * */ #include #include #include #include #include "xg_private.h" #include "xc_dom.h" #include "xc_bitops.h" #define XEN_VER "xen-3.0" /* ------------------------------------------------------------------------ */ static void log_callback(struct elf_binary *elf, void *caller_data, bool iserr, const char *fmt, va_list al) { xc_interface *xch = caller_data; xc_reportv(xch, xch->dombuild_logger ? xch->dombuild_logger : xch->error_handler, iserr ? XTL_ERROR : XTL_DETAIL, iserr ? XC_INVALID_KERNEL : XC_ERROR_NONE, fmt, al); } void xc_elf_set_logfile(xc_interface *xch, struct elf_binary *elf, int verbose) { elf_set_log(elf, log_callback, xch, verbose /* convert to bool */); } /* ------------------------------------------------------------------------ */ static char *xc_dom_guest_type(struct xc_dom_image *dom, struct elf_binary *elf) { uint64_t machine = elf_uval(elf, elf->ehdr, e_machine); if ( dom->container_type == XC_DOM_HVM_CONTAINER && dom->parms.phys_entry != UNSET_ADDR32 ) return "hvm-3.0-x86_32"; switch ( machine ) { case EM_386: switch ( dom->parms.pae ) { case XEN_PAE_BIMODAL: if ( strstr(dom->xen_caps, "xen-3.0-x86_32p") ) return "xen-3.0-x86_32p"; return "xen-3.0-x86_32"; case XEN_PAE_EXTCR3: case XEN_PAE_YES: return "xen-3.0-x86_32p"; case XEN_PAE_NO: default: return "xen-3.0-x86_32"; } case EM_X86_64: return "xen-3.0-x86_64"; default: return "xen-3.0-unknown"; } } /* ------------------------------------------------------------------------ */ /* parse elf binary */ static elf_negerrnoval check_elf_kernel(struct xc_dom_image *dom, bool verbose) { if ( dom->kernel_blob == NULL ) { if ( verbose ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: no kernel image loaded", __FUNCTION__); return -EINVAL; } if ( !elf_is_elfbinary(dom->kernel_blob, dom->kernel_size) ) { if ( verbose ) xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: kernel is not an ELF image", __FUNCTION__); return -EINVAL; } return 0; } static elf_negerrnoval xc_dom_probe_elf_kernel(struct xc_dom_image *dom) { struct elf_binary elf; int rc; rc = check_elf_kernel(dom, 0); if ( rc != 0 ) return rc; rc = elf_init(&elf, dom->kernel_blob, dom->kernel_size); if ( rc != 0 ) return rc; /* * We need to check that it contains Xen ELFNOTES, * or else we might be trying to load a plain ELF. */ elf_parse_binary(&elf); rc = elf_xen_parse(&elf, &dom->parms); if ( rc != 0 ) return rc; return 0; } static elf_errorstatus xc_dom_parse_elf_kernel(struct xc_dom_image *dom) /* * This function sometimes returns -1 for error and sometimes * an errno value. ?!?! */ { struct elf_binary *elf; elf_errorstatus rc; rc = check_elf_kernel(dom, 1); if ( rc != 0 ) return rc; elf = xc_dom_malloc(dom, sizeof(*elf)); if ( elf == NULL ) return -1; dom->private_loader = elf; rc = elf_init(elf, dom->kernel_blob, dom->kernel_size); xc_elf_set_logfile(dom->xch, elf, 1); if ( rc != 0 ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: corrupted ELF image", __FUNCTION__); return rc; } /* Find the section-header strings table. */ if ( ELF_PTRVAL_INVALID(elf->sec_strtab) ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: ELF image" " has no shstrtab", __FUNCTION__); rc = -EINVAL; goto out; } /* parse binary and get xen meta info */ elf_parse_binary(elf); if ( (rc = elf_xen_parse(elf, &dom->parms)) != 0 ) { goto out; } if ( elf_xen_feature_get(XENFEAT_dom0, dom->parms.f_required) ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: Kernel does not" " support unprivileged (DomU) operation", __FUNCTION__); rc = -EINVAL; goto out; } /* find kernel segment */ dom->kernel_seg.vstart = dom->parms.virt_kstart; dom->kernel_seg.vend = dom->parms.virt_kend; dom->guest_type = xc_dom_guest_type(dom, elf); DOMPRINTF("%s: %s: 0x%" PRIx64 " -> 0x%" PRIx64 "", __FUNCTION__, dom->guest_type, dom->kernel_seg.vstart, dom->kernel_seg.vend); rc = 0; out: if ( elf_check_broken(elf) ) DOMPRINTF("%s: ELF broken: %s", __FUNCTION__, elf_check_broken(elf)); return rc; } static elf_errorstatus xc_dom_load_elf_kernel(struct xc_dom_image *dom) { struct elf_binary *elf = dom->private_loader; elf_errorstatus rc; xen_pfn_t pages; elf->dest_base = xc_dom_seg_to_ptr_pages(dom, &dom->kernel_seg, &pages); if ( elf->dest_base == NULL ) { DOMPRINTF("%s: xc_dom_vaddr_to_ptr(dom,dom->kernel_seg)" " => NULL", __FUNCTION__); return -1; } elf->dest_size = pages * XC_DOM_PAGE_SIZE(dom); rc = elf_load_binary(elf); if ( rc < 0 ) { DOMPRINTF("%s: failed to load elf binary", __FUNCTION__); return rc; } return 0; } /* ------------------------------------------------------------------------ */ struct xc_dom_loader elf_loader = { .name = "ELF-generic", .probe = xc_dom_probe_elf_kernel, .parser = xc_dom_parse_elf_kernel, .loader = xc_dom_load_elf_kernel, }; static void __init register_loader(void) { xc_dom_register_loader(&elf_loader); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_armzimageloader.c0000664000175000017500000001633413256712137017565 0ustar smbsmb/* * Xen domain builder -- ARM zImage bits * * Parse and load ARM zImage kernel images. * * Copyright (C) 2012, Citrix Systems. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * */ #include #include #include #include "xg_private.h" #include "xc_dom.h" #include /* XXX ntohl is not the right function... */ struct minimal_dtb_header { uint32_t magic; uint32_t total_size; /* There are other fields but we don't use them yet. */ }; #define DTB_MAGIC 0xd00dfeed /* ------------------------------------------------------------ */ /* 32-bit zImage Support */ /* ------------------------------------------------------------ */ #define ZIMAGE32_MAGIC_OFFSET 0x24 #define ZIMAGE32_START_OFFSET 0x28 #define ZIMAGE32_END_OFFSET 0x2c #define ZIMAGE32_MAGIC 0x016f2818 static int xc_dom_probe_zimage32_kernel(struct xc_dom_image *dom) { uint32_t *zimage; if ( dom->kernel_blob == NULL ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: no kernel image loaded", __FUNCTION__); return -EINVAL; } if ( dom->kernel_size < 0x30 /*sizeof(struct setup_header)*/ ) { xc_dom_printf(dom->xch, "%s: kernel image too small", __FUNCTION__); return -EINVAL; } zimage = (uint32_t *)dom->kernel_blob; if ( zimage[ZIMAGE32_MAGIC_OFFSET/4] != ZIMAGE32_MAGIC ) { xc_dom_printf(dom->xch, "%s: kernel is not an arm32 zImage", __FUNCTION__); return -EINVAL; } return 0; } static int xc_dom_parse_zimage32_kernel(struct xc_dom_image *dom) { uint32_t *zimage; uint32_t start, entry_addr; uint64_t v_start, v_end; uint64_t rambase = dom->rambase_pfn << XC_PAGE_SHIFT; DOMPRINTF_CALLED(dom->xch); zimage = (uint32_t *)dom->kernel_blob; /* Do not load kernel at the very first RAM address */ v_start = rambase + 0x8000; if ( dom->kernel_size > UINT64_MAX - v_start ) { DOMPRINTF("%s: kernel is too large\n", __FUNCTION__); return -EINVAL; } v_end = v_start + dom->kernel_size; /* * If start is invalid then the guest will start at some invalid * address and crash, but this happens in guest context so doesn't * concern us here. */ start = zimage[ZIMAGE32_START_OFFSET/4]; if (start == 0) entry_addr = v_start; else entry_addr = start; /* find kernel segment */ dom->kernel_seg.vstart = v_start; dom->kernel_seg.vend = v_end; dom->parms.virt_entry = entry_addr; dom->parms.virt_base = rambase; dom->guest_type = "xen-3.0-armv7l"; DOMPRINTF("%s: %s: 0x%" PRIx64 " -> 0x%" PRIx64 "", __FUNCTION__, dom->guest_type, dom->kernel_seg.vstart, dom->kernel_seg.vend); return 0; } /* ------------------------------------------------------------ */ /* 64-bit zImage Support */ /* ------------------------------------------------------------ */ #define ZIMAGE64_MAGIC_V0 0x14000008 #define ZIMAGE64_MAGIC_V1 0x644d5241 /* "ARM\x64" */ /* linux/Documentation/arm64/booting.txt */ struct zimage64_hdr { uint32_t magic0; uint32_t res0; uint64_t text_offset; /* Image load offset */ uint64_t res1; uint64_t res2; /* zImage V1 only from here */ uint64_t res3; uint64_t res4; uint64_t res5; uint32_t magic1; uint32_t res6; }; static int xc_dom_probe_zimage64_kernel(struct xc_dom_image *dom) { struct zimage64_hdr *zimage; if ( dom->kernel_blob == NULL ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: no kernel image loaded", __FUNCTION__); return -EINVAL; } if ( dom->kernel_size < sizeof(*zimage) ) { xc_dom_printf(dom->xch, "%s: kernel image too small", __FUNCTION__); return -EINVAL; } zimage = dom->kernel_blob; if ( zimage->magic0 != ZIMAGE64_MAGIC_V0 && zimage->magic1 != ZIMAGE64_MAGIC_V1 ) { xc_dom_printf(dom->xch, "%s: kernel is not an arm64 Image", __FUNCTION__); return -EINVAL; } return 0; } static int xc_dom_parse_zimage64_kernel(struct xc_dom_image *dom) { struct zimage64_hdr *zimage; uint64_t v_start, v_end; uint64_t rambase = dom->rambase_pfn << XC_PAGE_SHIFT; DOMPRINTF_CALLED(dom->xch); zimage = dom->kernel_blob; if ( zimage->text_offset > UINT64_MAX - rambase ) { DOMPRINTF("%s: kernel text offset is too large\n", __FUNCTION__); return -EINVAL; } v_start = rambase + zimage->text_offset; if ( dom->kernel_size > UINT64_MAX - v_start ) { DOMPRINTF("%s: kernel is too large\n", __FUNCTION__); return -EINVAL; } v_end = v_start + dom->kernel_size; dom->kernel_seg.vstart = v_start; dom->kernel_seg.vend = v_end; /* Call the kernel at offset 0 */ dom->parms.virt_entry = v_start; dom->parms.virt_base = rambase; dom->guest_type = "xen-3.0-aarch64"; DOMPRINTF("%s: %s: 0x%" PRIx64 " -> 0x%" PRIx64 "", __FUNCTION__, dom->guest_type, dom->kernel_seg.vstart, dom->kernel_seg.vend); return 0; } /* ------------------------------------------------------------ */ /* Common zImage Support */ /* ------------------------------------------------------------ */ static int xc_dom_load_zimage_kernel(struct xc_dom_image *dom) { void *dst; DOMPRINTF_CALLED(dom->xch); dst = xc_dom_seg_to_ptr(dom, &dom->kernel_seg); if ( dst == NULL ) { DOMPRINTF("%s: xc_dom_seg_to_ptr(dom, &dom->kernel_seg) => NULL", __func__); return -1; } DOMPRINTF("%s: kernel seg %#"PRIx64"-%#"PRIx64, __func__, dom->kernel_seg.vstart, dom->kernel_seg.vend); DOMPRINTF("%s: copy %zd bytes from blob %p to dst %p", __func__, dom->kernel_size, dom->kernel_blob, dst); memcpy(dst, dom->kernel_blob, dom->kernel_size); return 0; } static struct xc_dom_loader zimage32_loader = { .name = "Linux zImage (ARM32)", .probe = xc_dom_probe_zimage32_kernel, .parser = xc_dom_parse_zimage32_kernel, .loader = xc_dom_load_zimage_kernel, }; static struct xc_dom_loader zimage64_loader = { .name = "Linux zImage (ARM64)", .probe = xc_dom_probe_zimage64_kernel, .parser = xc_dom_parse_zimage64_kernel, .loader = xc_dom_load_zimage_kernel, }; static void __init register_loader(void) { xc_dom_register_loader(&zimage32_loader); xc_dom_register_loader(&zimage64_loader); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_hcall_buf.c0000664000175000017500000001142113256712137015472 0ustar smbsmb/* * Copyright (c) 2010, Citrix Systems, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include #include #include "xc_private.h" #include "xg_private.h" xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(HYPERCALL_BUFFER_NULL) = { .hbuf = NULL, .param_shadow = NULL, HYPERCALL_BUFFER_INIT_NO_BOUNCE }; void *xc__hypercall_buffer_alloc_pages(xc_interface *xch, xc_hypercall_buffer_t *b, int nr_pages) { void *p = xencall_alloc_buffer_pages(xch->xcall, nr_pages); if (!p) return NULL; b->hbuf = p; return b->hbuf; } void xc__hypercall_buffer_free_pages(xc_interface *xch, xc_hypercall_buffer_t *b, int nr_pages) { xencall_free_buffer_pages(xch->xcall, b->hbuf, nr_pages); } void *xc__hypercall_buffer_alloc(xc_interface *xch, xc_hypercall_buffer_t *b, size_t size) { void *p = xencall_alloc_buffer(xch->xcall, size); if (!p) return NULL; b->hbuf = p; return b->hbuf; } void xc__hypercall_buffer_free(xc_interface *xch, xc_hypercall_buffer_t *b) { xencall_free_buffer(xch->xcall, b->hbuf); } int xc__hypercall_bounce_pre(xc_interface *xch, xc_hypercall_buffer_t *b) { void *p; /* * Catch hypercall buffer declared other than with DECLARE_HYPERCALL_BOUNCE. */ if ( b->ubuf == (void *)-1 || b->dir == XC_HYPERCALL_BUFFER_BOUNCE_NONE ) abort(); /* * Don't need to bounce a NULL buffer. */ if ( b->ubuf == NULL ) { b->hbuf = NULL; return 0; } p = xc__hypercall_buffer_alloc(xch, b, b->sz); if ( p == NULL ) return -1; if ( b->dir == XC_HYPERCALL_BUFFER_BOUNCE_IN || b->dir == XC_HYPERCALL_BUFFER_BOUNCE_BOTH ) memcpy(b->hbuf, b->ubuf, b->sz); return 0; } void xc__hypercall_bounce_post(xc_interface *xch, xc_hypercall_buffer_t *b) { /* * Catch hypercall buffer declared other than with DECLARE_HYPERCALL_BOUNCE. */ if ( b->ubuf == (void *)-1 || b->dir == XC_HYPERCALL_BUFFER_BOUNCE_NONE ) abort(); if ( b->hbuf == NULL ) return; if ( b->dir == XC_HYPERCALL_BUFFER_BOUNCE_OUT || b->dir == XC_HYPERCALL_BUFFER_BOUNCE_BOTH ) memcpy(b->ubuf, b->hbuf, b->sz); xc__hypercall_buffer_free(xch, b); } struct xc_hypercall_buffer_array { unsigned max_bufs; xc_hypercall_buffer_t *bufs; }; xc_hypercall_buffer_array_t *xc_hypercall_buffer_array_create(xc_interface *xch, unsigned n) { xc_hypercall_buffer_array_t *array; xc_hypercall_buffer_t *bufs = NULL; array = malloc(sizeof(*array)); if ( array == NULL ) goto error; bufs = calloc(n, sizeof(*bufs)); if ( bufs == NULL ) goto error; array->max_bufs = n; array->bufs = bufs; return array; error: free(bufs); free(array); return NULL; } void *xc__hypercall_buffer_array_alloc(xc_interface *xch, xc_hypercall_buffer_array_t *array, unsigned index, xc_hypercall_buffer_t *hbuf, size_t size) { void *buf; if ( index >= array->max_bufs || array->bufs[index].hbuf ) abort(); buf = xc__hypercall_buffer_alloc(xch, hbuf, size); if ( buf ) array->bufs[index] = *hbuf; return buf; } void *xc__hypercall_buffer_array_get(xc_interface *xch, xc_hypercall_buffer_array_t *array, unsigned index, xc_hypercall_buffer_t *hbuf) { if ( index >= array->max_bufs || array->bufs[index].hbuf == NULL ) abort(); *hbuf = array->bufs[index]; return array->bufs[index].hbuf; } void xc_hypercall_buffer_array_destroy(xc_interface *xc, xc_hypercall_buffer_array_t *array) { unsigned i; if ( array == NULL ) return; for (i = 0; i < array->max_bufs; i++ ) xc__hypercall_buffer_free(xc, &array->bufs[i]); free(array->bufs); free(array); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_common.c0000664000175000017500000001152613256712137015555 0ustar smbsmb#include #include "xc_sr_common.h" #include static const char *dhdr_types[] = { [DHDR_TYPE_X86_PV] = "x86 PV", [DHDR_TYPE_X86_HVM] = "x86 HVM", [DHDR_TYPE_X86_PVH] = "x86 PVH", [DHDR_TYPE_ARM] = "ARM", }; const char *dhdr_type_to_str(uint32_t type) { if ( type < ARRAY_SIZE(dhdr_types) && dhdr_types[type] ) return dhdr_types[type]; return "Reserved"; } static const char *mandatory_rec_types[] = { [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", }; const char *rec_type_to_str(uint32_t type) { if ( !(type & REC_TYPE_OPTIONAL) ) { if ( (type < ARRAY_SIZE(mandatory_rec_types)) && (mandatory_rec_types[type]) ) return mandatory_rec_types[type]; } return "Reserved"; } int write_split_record(struct xc_sr_context *ctx, struct xc_sr_record *rec, void *buf, size_t sz) { static const char zeroes[(1u << REC_ALIGN_ORDER) - 1] = { 0 }; xc_interface *xch = ctx->xch; typeof(rec->length) combined_length = rec->length + sz; size_t record_length = ROUNDUP(combined_length, REC_ALIGN_ORDER); struct iovec parts[] = { { &rec->type, sizeof(rec->type) }, { &combined_length, sizeof(combined_length) }, { rec->data, rec->length }, { buf, sz }, { (void*)zeroes, record_length - combined_length }, }; if ( record_length > REC_LENGTH_MAX ) { ERROR("Record (0x%08x, %s) length %#x exceeds max (%#x)", rec->type, rec_type_to_str(rec->type), rec->length, REC_LENGTH_MAX); return -1; } if ( rec->length ) assert(rec->data); if ( sz ) assert(buf); if ( writev_exact(ctx->fd, parts, ARRAY_SIZE(parts)) ) goto err; return 0; err: PERROR("Unable to write record to stream"); return -1; } int read_record(struct xc_sr_context *ctx, int fd, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; struct xc_sr_rhdr rhdr; size_t datasz; if ( read_exact(fd, &rhdr, sizeof(rhdr)) ) { PERROR("Failed to read Record Header from stream"); return -1; } else if ( rhdr.length > REC_LENGTH_MAX ) { ERROR("Record (0x%08x, %s) length %#x exceeds max (%#x)", rhdr.type, rec_type_to_str(rhdr.type), rhdr.length, REC_LENGTH_MAX); return -1; } datasz = ROUNDUP(rhdr.length, REC_ALIGN_ORDER); if ( datasz ) { rec->data = malloc(datasz); if ( !rec->data ) { ERROR("Unable to allocate %zu bytes for record data (0x%08x, %s)", datasz, rhdr.type, rec_type_to_str(rhdr.type)); return -1; } if ( read_exact(fd, rec->data, datasz) ) { free(rec->data); rec->data = NULL; PERROR("Failed to read %zu bytes of data for record (0x%08x, %s)", datasz, rhdr.type, rec_type_to_str(rhdr.type)); return -1; } } else rec->data = NULL; rec->type = rhdr.type; rec->length = rhdr.length; return 0; }; static void __attribute__((unused)) build_assertions(void) { BUILD_BUG_ON(sizeof(struct xc_sr_ihdr) != 24); BUILD_BUG_ON(sizeof(struct xc_sr_dhdr) != 16); BUILD_BUG_ON(sizeof(struct xc_sr_rhdr) != 8); BUILD_BUG_ON(sizeof(struct xc_sr_rec_page_data_header) != 8); BUILD_BUG_ON(sizeof(struct xc_sr_rec_x86_pv_info) != 8); BUILD_BUG_ON(sizeof(struct xc_sr_rec_x86_pv_p2m_frames) != 8); BUILD_BUG_ON(sizeof(struct xc_sr_rec_x86_pv_vcpu_hdr) != 8); BUILD_BUG_ON(sizeof(struct xc_sr_rec_tsc_info) != 24); BUILD_BUG_ON(sizeof(struct xc_sr_rec_hvm_params_entry) != 16); BUILD_BUG_ON(sizeof(struct xc_sr_rec_hvm_params) != 8); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_core.c0000664000175000017500000007077613256712137014525 0ustar smbsmb/* * Elf format, (pfn, gmfn) table, IA64 support. * Copyright (c) 2007 Isaku Yamahata * VA Linux Systems Japan K.K. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ /* * xen dump-core file format follows ELF format specification. * Analisys tools shouldn't depends on the order of sections. * They should follow elf header and check section names. * * +--------------------------------------------------------+ * |ELF header | * +--------------------------------------------------------+ * |section headers | * | null section header | * | .shstrtab | * | .note.Xen | * | .xen_prstatus | * | .xen_shared_info if present | * | .xen_pages | * | .xen_p2m or .xen_pfn | * +--------------------------------------------------------+ * |.note.Xen:note section | * | "Xen" is used as note name, | * | types are defined in xen/include/public/elfnote.h | * | and descriptors are defined in xc_core.h. | * | dumpcore none | * | dumpcore header | * | dumpcore xen version | * | dumpcore format version | * +--------------------------------------------------------+ * |.xen_prstatus | * | vcpu_guest_context_t[nr_vcpus] | * +--------------------------------------------------------+ * |.xen_shared_info if possible | * +--------------------------------------------------------+ * |.xen_pages | * | page * nr_pages | * +--------------------------------------------------------+ * |.xen_p2m or .xen_pfn | * | .xen_p2m: struct xen_dumpcore_p2m[nr_pages] | * | .xen_pfn: uint64_t[nr_pages] | * +--------------------------------------------------------+ * |.shstrtab: section header string table | * +--------------------------------------------------------+ * */ #include "xg_private.h" #include "xc_core.h" #include "xc_dom.h" #include #include /* number of pages to write at a time */ #define DUMP_INCREMENT (4 * 1024) /* string table */ struct xc_core_strtab { char *strings; uint16_t length; uint16_t max; }; static struct xc_core_strtab* xc_core_strtab_init(xc_interface *xch) { struct xc_core_strtab *strtab; char *strings; strtab = malloc(sizeof(*strtab)); if ( strtab == NULL ) return NULL; strings = malloc(PAGE_SIZE); if ( strings == NULL ) { PERROR("Could not allocate string table init"); free(strtab); return NULL; } strtab->strings = strings; strtab->max = PAGE_SIZE; /* index 0 represents none */ strtab->strings[0] = '\0'; strtab->length = 1; return strtab; } static void xc_core_strtab_free(struct xc_core_strtab *strtab) { free(strtab->strings); free(strtab); } static uint16_t xc_core_strtab_get(xc_interface *xch, struct xc_core_strtab *strtab, const char *name) { uint16_t ret = 0; uint16_t len = strlen(name) + 1; if ( strtab->length > UINT16_MAX - len ) { PERROR("too long string table"); errno = E2BIG; return ret; } if ( strtab->length + len > strtab->max ) { char *tmp; if ( strtab->max > UINT16_MAX / 2 ) { PERROR("too long string table"); errno = ENOMEM; return ret; } tmp = realloc(strtab->strings, strtab->max * 2); if ( tmp == NULL ) { PERROR("Could not allocate string table"); return ret; } strtab->strings = tmp; strtab->max *= 2; } ret = strtab->length; strcpy(strtab->strings + strtab->length, name); strtab->length += len; return ret; } /* section headers */ struct xc_core_section_headers { uint16_t num; uint16_t num_max; Elf64_Shdr *shdrs; }; #define SHDR_INIT ((uint16_t)16) #define SHDR_INC ((uint16_t)4) static struct xc_core_section_headers* xc_core_shdr_init(xc_interface *xch) { struct xc_core_section_headers *sheaders; sheaders = malloc(sizeof(*sheaders)); if ( sheaders == NULL ) return NULL; sheaders->num = 0; sheaders->num_max = SHDR_INIT; sheaders->shdrs = malloc(sizeof(sheaders->shdrs[0]) * sheaders->num_max); if ( sheaders->shdrs == NULL ) { free(sheaders); return NULL; } return sheaders; } static void xc_core_shdr_free(struct xc_core_section_headers *sheaders) { free(sheaders->shdrs); free(sheaders); } Elf64_Shdr* xc_core_shdr_get(xc_interface *xch, struct xc_core_section_headers *sheaders) { Elf64_Shdr *shdr; if ( sheaders->num == sheaders->num_max ) { Elf64_Shdr *shdrs; if ( sheaders->num_max > UINT16_MAX - SHDR_INC ) { errno = E2BIG; return NULL; } sheaders->num_max += SHDR_INC; shdrs = realloc(sheaders->shdrs, sizeof(sheaders->shdrs[0]) * sheaders->num_max); if ( shdrs == NULL ) return NULL; sheaders->shdrs = shdrs; } shdr = &sheaders->shdrs[sheaders->num]; sheaders->num++; memset(shdr, 0, sizeof(*shdr)); return shdr; } int xc_core_shdr_set(xc_interface *xch, Elf64_Shdr *shdr, struct xc_core_strtab *strtab, const char *name, uint32_t type, uint64_t offset, uint64_t size, uint64_t addralign, uint64_t entsize) { uint64_t name_idx = xc_core_strtab_get(xch, strtab, name); if ( name_idx == 0 ) return -1; shdr->sh_name = name_idx; shdr->sh_type = type; shdr->sh_offset = offset; shdr->sh_size = size; shdr->sh_addralign = addralign; shdr->sh_entsize = entsize; return 0; } static void xc_core_ehdr_init(Elf64_Ehdr *ehdr) { memset(ehdr, 0, sizeof(*ehdr)); ehdr->e_ident[EI_MAG0] = ELFMAG0; ehdr->e_ident[EI_MAG1] = ELFMAG1; ehdr->e_ident[EI_MAG2] = ELFMAG2; ehdr->e_ident[EI_MAG3] = ELFMAG3; ehdr->e_ident[EI_CLASS] = ELFCLASS64; ehdr->e_ident[EI_DATA] = ELF_ARCH_DATA; ehdr->e_ident[EI_VERSION] = EV_CURRENT; ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV; ehdr->e_ident[EI_ABIVERSION] = EV_CURRENT; ehdr->e_type = ET_CORE; /* e_machine will be filled in later */ ehdr->e_version = EV_CURRENT; ehdr->e_entry = 0; ehdr->e_phoff = 0; ehdr->e_shoff = sizeof(*ehdr); ehdr->e_flags = ELF_CORE_EFLAGS; ehdr->e_ehsize = sizeof(*ehdr); ehdr->e_phentsize = sizeof(Elf64_Phdr); ehdr->e_phnum = 0; ehdr->e_shentsize = sizeof(Elf64_Shdr); /* ehdr->e_shnum and ehdr->e_shstrndx aren't known here yet. * fill it later */ } static int elfnote_fill_xen_version(xc_interface *xch, struct xen_dumpcore_elfnote_xen_version_desc *xen_version) { int rc; memset(xen_version, 0, sizeof(*xen_version)); rc = xc_version(xch, XENVER_version, NULL); if ( rc < 0 ) return rc; xen_version->major_version = rc >> 16; xen_version->minor_version = rc & ((1 << 16) - 1); rc = xc_version(xch, XENVER_extraversion, &xen_version->extra_version); if ( rc < 0 ) return rc; rc = xc_version(xch, XENVER_compile_info, &xen_version->compile_info); if ( rc < 0 ) return rc; rc = xc_version(xch, XENVER_capabilities, &xen_version->capabilities); if ( rc < 0 ) return rc; rc = xc_version(xch, XENVER_changeset, &xen_version->changeset); if ( rc < 0 ) return rc; rc = xc_version(xch, XENVER_platform_parameters, &xen_version->platform_parameters); if ( rc < 0 ) return rc; rc = xc_version(xch, XENVER_pagesize, NULL); if ( rc < 0 ) return rc; xen_version->pagesize = rc; return 0; } static void elfnote_fill_format_version(struct xen_dumpcore_elfnote_format_version_desc *format_version) { format_version->version = XEN_DUMPCORE_FORMAT_VERSION_CURRENT; } static void elfnote_init(struct elfnote *elfnote) { /* elf note section */ memset(elfnote, 0, sizeof(*elfnote)); elfnote->namesz = strlen(XEN_DUMPCORE_ELFNOTE_NAME) + 1; strncpy(elfnote->name, XEN_DUMPCORE_ELFNOTE_NAME, sizeof(elfnote->name)); } static int elfnote_dump_none(xc_interface *xch, void *args, dumpcore_rtn_t dump_rtn) { int sts; struct elfnote elfnote; struct xen_dumpcore_elfnote_none_desc none; elfnote_init(&elfnote); /* Avoid compile warning about constant-zero-sized memset(). */ /*memset(&none, 0, sizeof(none));*/ elfnote.descsz = sizeof(none); elfnote.type = XEN_ELFNOTE_DUMPCORE_NONE; sts = dump_rtn(xch, args, (char*)&elfnote, sizeof(elfnote)); if ( sts != 0 ) return sts; return dump_rtn(xch, args, (char*)&none, sizeof(none)); } static int elfnote_dump_core_header( xc_interface *xch, void *args, dumpcore_rtn_t dump_rtn, const xc_dominfo_t *info, int nr_vcpus, unsigned long nr_pages) { int sts; struct elfnote elfnote; struct xen_dumpcore_elfnote_header_desc header; elfnote_init(&elfnote); memset(&header, 0, sizeof(header)); elfnote.descsz = sizeof(header); elfnote.type = XEN_ELFNOTE_DUMPCORE_HEADER; header.xch_magic = info->hvm ? XC_CORE_MAGIC_HVM : XC_CORE_MAGIC; header.xch_nr_vcpus = nr_vcpus; header.xch_nr_pages = nr_pages; header.xch_page_size = PAGE_SIZE; sts = dump_rtn(xch, args, (char*)&elfnote, sizeof(elfnote)); if ( sts != 0 ) return sts; return dump_rtn(xch, args, (char*)&header, sizeof(header)); } static int elfnote_dump_xen_version(xc_interface *xch, void *args, dumpcore_rtn_t dump_rtn, unsigned int guest_width) { int sts; struct elfnote elfnote; struct xen_dumpcore_elfnote_xen_version_desc xen_version; elfnote_init(&elfnote); memset(&xen_version, 0, sizeof(xen_version)); elfnote.descsz = sizeof(xen_version); elfnote.type = XEN_ELFNOTE_DUMPCORE_XEN_VERSION; elfnote_fill_xen_version(xch, &xen_version); if (guest_width < sizeof(unsigned long)) { // 32 bit elf file format differs in pagesize's alignment char *p = (char *)&xen_version.pagesize; memmove(p - 4, p, sizeof(xen_version.pagesize)); } sts = dump_rtn(xch, args, (char*)&elfnote, sizeof(elfnote)); if ( sts != 0 ) return sts; return dump_rtn(xch, args, (char*)&xen_version, sizeof(xen_version)); } static int elfnote_dump_format_version(xc_interface *xch, void *args, dumpcore_rtn_t dump_rtn) { int sts; struct elfnote elfnote; struct xen_dumpcore_elfnote_format_version_desc format_version; elfnote_init(&elfnote); memset(&format_version, 0, sizeof(format_version)); elfnote.descsz = sizeof(format_version); elfnote.type = XEN_ELFNOTE_DUMPCORE_FORMAT_VERSION; elfnote_fill_format_version(&format_version); sts = dump_rtn(xch, args, (char*)&elfnote, sizeof(elfnote)); if ( sts != 0 ) return sts; return dump_rtn(xch, args, (char*)&format_version, sizeof(format_version)); } int xc_domain_dumpcore_via_callback(xc_interface *xch, uint32_t domid, void *args, dumpcore_rtn_t dump_rtn) { xc_dominfo_t info; shared_info_any_t *live_shinfo = NULL; struct domain_info_context _dinfo = {}; struct domain_info_context *dinfo = &_dinfo; int nr_vcpus = 0; char *dump_mem, *dump_mem_start = NULL; vcpu_guest_context_any_t *ctxt = NULL; struct xc_core_arch_context arch_ctxt; char dummy[PAGE_SIZE]; int dummy_len; int sts = -1; unsigned long i; unsigned long j; unsigned long nr_pages; xc_core_memory_map_t *memory_map = NULL; unsigned int nr_memory_map; unsigned int map_idx; int auto_translated_physmap; xen_pfn_t *p2m = NULL; struct xen_dumpcore_p2m *p2m_array = NULL; uint64_t *pfn_array = NULL; Elf64_Ehdr ehdr; uint64_t filesz; uint64_t offset; uint64_t fixup; struct xc_core_strtab *strtab = NULL; uint16_t strtab_idx; struct xc_core_section_headers *sheaders = NULL; Elf64_Shdr *shdr; if ( xc_domain_get_guest_width(xch, domid, &dinfo->guest_width) != 0 ) { PERROR("Could not get address size for domain"); return sts; } xc_core_arch_context_init(&arch_ctxt); if ( (dump_mem_start = malloc(DUMP_INCREMENT*PAGE_SIZE)) == NULL ) { PERROR("Could not allocate dump_mem"); goto out; } if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 ) { PERROR("Could not get info for domain"); goto out; } /* Map the shared info frame */ live_shinfo = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ, info.shared_info_frame); if ( !live_shinfo && !info.hvm ) { PERROR("Couldn't map live_shinfo"); goto out; } auto_translated_physmap = xc_core_arch_auto_translated_physmap(&info); if ( domid != info.domid ) { PERROR("Domain %d does not exist", domid); goto out; } ctxt = calloc(sizeof(*ctxt), info.max_vcpu_id + 1); if ( !ctxt ) { PERROR("Could not allocate vcpu context array"); goto out; } for ( i = 0; i <= info.max_vcpu_id; i++ ) { if ( xc_vcpu_getcontext(xch, domid, i, &ctxt[nr_vcpus]) == 0 ) { if ( xc_core_arch_context_get(&arch_ctxt, &ctxt[nr_vcpus], xch, domid) ) continue; nr_vcpus++; } } if ( nr_vcpus == 0 ) { PERROR("No VCPU context could be grabbed"); goto out; } /* obtain memory map */ sts = xc_core_arch_memory_map_get(xch, &arch_ctxt, &info, live_shinfo, &memory_map, &nr_memory_map); if ( sts != 0 ) goto out; /* * Note: this is the *current* number of pages and may change under * a live dump-core. We'll just take this value, and if more pages * exist, we'll skip them. If there's less, then we'll just not use * all the array... * * We don't want to use the total potential size of the memory map * since that is usually much higher than info.nr_pages. */ nr_pages = info.nr_pages; if ( !auto_translated_physmap ) { /* obtain p2m table */ p2m_array = malloc(nr_pages * sizeof(p2m_array[0])); if ( p2m_array == NULL ) { PERROR("Could not allocate p2m array"); goto out; } sts = xc_core_arch_map_p2m(xch, dinfo->guest_width, &info, live_shinfo, &p2m, &dinfo->p2m_size); if ( sts != 0 ) goto out; } else { pfn_array = malloc(nr_pages * sizeof(pfn_array[0])); if ( pfn_array == NULL ) { PERROR("Could not allocate pfn array"); goto out; } } /* ehdr.e_shnum and ehdr.e_shstrndx aren't known here yet. fill it later*/ xc_core_ehdr_init(&ehdr); /* create section header */ strtab = xc_core_strtab_init(xch); if ( strtab == NULL ) { PERROR("Could not allocate string table"); goto out; } sheaders = xc_core_shdr_init(xch); if ( sheaders == NULL ) { PERROR("Could not allocate section headers"); goto out; } /* null section */ shdr = xc_core_shdr_get(xch,sheaders); if ( shdr == NULL ) { PERROR("Could not get section header for null section"); goto out; } /* .shstrtab */ shdr = xc_core_shdr_get(xch,sheaders); if ( shdr == NULL ) { PERROR("Could not get section header for shstrtab"); goto out; } strtab_idx = shdr - sheaders->shdrs; /* strtab_shdr.sh_offset, strtab_shdr.sh_size aren't unknown. * fill it later */ sts = xc_core_shdr_set(xch, shdr, strtab, ELF_SHSTRTAB, SHT_STRTAB, 0, 0, 0, 0); if ( sts != 0 ) goto out; /* elf note section */ /* here the number of section header is unknown. fix up offset later. */ offset = sizeof(ehdr); filesz = sizeof(struct xen_dumpcore_elfnote_none) + /* none */ sizeof(struct xen_dumpcore_elfnote_header) + /* core header */ sizeof(struct xen_dumpcore_elfnote_xen_version) + /* xen version */ sizeof(struct xen_dumpcore_elfnote_format_version);/* format version */ shdr = xc_core_shdr_get(xch,sheaders); if ( shdr == NULL ) { PERROR("Could not get section header for note section"); goto out; } sts = xc_core_shdr_set(xch, shdr, strtab, XEN_DUMPCORE_SEC_NOTE, SHT_NOTE, offset, filesz, 0, 0); if ( sts != 0 ) goto out; offset += filesz; /* prstatus */ shdr = xc_core_shdr_get(xch,sheaders); if ( shdr == NULL ) { PERROR("Could not get section header for .xen_prstatus"); goto out; } filesz = sizeof(*ctxt) * nr_vcpus; sts = xc_core_shdr_set(xch, shdr, strtab, XEN_DUMPCORE_SEC_PRSTATUS, SHT_PROGBITS, offset, filesz, __alignof__(*ctxt), sizeof(*ctxt)); if ( sts != 0 ) goto out; offset += filesz; /* arch context */ sts = xc_core_arch_context_get_shdr(xch, &arch_ctxt, sheaders, strtab, &filesz, offset); if ( sts != 0 ) goto out; offset += filesz; /* shared_info */ if ( live_shinfo != NULL ) { shdr = xc_core_shdr_get(xch,sheaders); if ( shdr == NULL ) { PERROR("Could not get section header for .xen_shared_info"); goto out; } filesz = PAGE_SIZE; sts = xc_core_shdr_set(xch, shdr, strtab, XEN_DUMPCORE_SEC_SHARED_INFO, SHT_PROGBITS, offset, filesz, __alignof__(*live_shinfo), PAGE_SIZE); if ( sts != 0 ) goto out; offset += filesz; } /* * pages and p2m/pfn are the last section to allocate section headers * so that we know the number of section headers here. * 2 = pages section and p2m/pfn table section */ fixup = (sheaders->num + 2) * sizeof(*shdr); /* zeroth section should have zero offset */ for ( i = 1; i < sheaders->num; i++ ) sheaders->shdrs[i].sh_offset += fixup; offset += fixup; dummy_len = ROUNDUP(offset, PAGE_SHIFT) - offset; /* padding length */ offset += dummy_len; /* pages */ shdr = xc_core_shdr_get(xch,sheaders); if ( shdr == NULL ) { PERROR("could not get section headers for .xen_pages"); goto out; } filesz = (uint64_t)nr_pages * PAGE_SIZE; sts = xc_core_shdr_set(xch, shdr, strtab, XEN_DUMPCORE_SEC_PAGES, SHT_PROGBITS, offset, filesz, PAGE_SIZE, PAGE_SIZE); if ( sts != 0 ) goto out; offset += filesz; /* p2m/pfn table */ shdr = xc_core_shdr_get(xch,sheaders); if ( shdr == NULL ) { PERROR("Could not get section header for .xen_{p2m, pfn} table"); goto out; } if ( !auto_translated_physmap ) { filesz = (uint64_t)nr_pages * sizeof(p2m_array[0]); sts = xc_core_shdr_set(xch, shdr, strtab, XEN_DUMPCORE_SEC_P2M, SHT_PROGBITS, offset, filesz, __alignof__(p2m_array[0]), sizeof(p2m_array[0])); } else { filesz = (uint64_t)nr_pages * sizeof(pfn_array[0]); sts = xc_core_shdr_set(xch, shdr, strtab, XEN_DUMPCORE_SEC_PFN, SHT_PROGBITS, offset, filesz, __alignof__(pfn_array[0]), sizeof(pfn_array[0])); } if ( sts != 0 ) goto out; offset += filesz; /* fixing up section header string table section header */ filesz = strtab->length; sheaders->shdrs[strtab_idx].sh_offset = offset; sheaders->shdrs[strtab_idx].sh_size = filesz; /* write out elf header */ ehdr.e_shnum = sheaders->num; ehdr.e_shstrndx = strtab_idx; ehdr.e_machine = ELF_ARCH_MACHINE; sts = dump_rtn(xch, args, (char*)&ehdr, sizeof(ehdr)); if ( sts != 0 ) goto out; /* section headers */ sts = dump_rtn(xch, args, (char*)sheaders->shdrs, sheaders->num * sizeof(sheaders->shdrs[0])); if ( sts != 0 ) goto out; /* elf note section: xen core header */ sts = elfnote_dump_none(xch, args, dump_rtn); if ( sts != 0 ) goto out; /* elf note section: xen core header */ sts = elfnote_dump_core_header(xch, args, dump_rtn, &info, nr_vcpus, nr_pages); if ( sts != 0 ) goto out; /* elf note section: xen version */ sts = elfnote_dump_xen_version(xch, args, dump_rtn, dinfo->guest_width); if ( sts != 0 ) goto out; /* elf note section: format version */ sts = elfnote_dump_format_version(xch, args, dump_rtn); if ( sts != 0 ) goto out; /* prstatus: .xen_prstatus */ sts = dump_rtn(xch, args, (char *)ctxt, sizeof(*ctxt) * nr_vcpus); if ( sts != 0 ) goto out; if ( live_shinfo != NULL ) { /* shared_info: .xen_shared_info */ sts = dump_rtn(xch, args, (char*)live_shinfo, PAGE_SIZE); if ( sts != 0 ) goto out; } /* arch specific context */ sts = xc_core_arch_context_dump(xch, &arch_ctxt, args, dump_rtn); if ( sts != 0 ) goto out; /* Pad the output data to page alignment. */ memset(dummy, 0, PAGE_SIZE); sts = dump_rtn(xch, args, dummy, dummy_len); if ( sts != 0 ) goto out; /* dump pages: .xen_pages */ j = 0; dump_mem = dump_mem_start; for ( map_idx = 0; map_idx < nr_memory_map; map_idx++ ) { uint64_t pfn_start; uint64_t pfn_end; pfn_start = memory_map[map_idx].addr >> PAGE_SHIFT; pfn_end = pfn_start + (memory_map[map_idx].size >> PAGE_SHIFT); for ( i = pfn_start; i < pfn_end; i++ ) { uint64_t gmfn; void *vaddr; if ( j >= nr_pages ) { /* * When live dump-mode (-L option) is specified, * guest domain may increase memory. */ IPRINTF("exceeded nr_pages (%ld) losing pages", nr_pages); goto copy_done; } if ( !auto_translated_physmap ) { if ( dinfo->guest_width >= sizeof(unsigned long) ) { if ( dinfo->guest_width == sizeof(unsigned long) ) gmfn = p2m[i]; else gmfn = ((uint64_t *)p2m)[i]; if ( gmfn == INVALID_PFN ) continue; } else { gmfn = ((uint32_t *)p2m)[i]; if ( gmfn == (uint32_t)INVALID_PFN ) continue; } p2m_array[j].pfn = i; p2m_array[j].gmfn = gmfn; } else { if ( !xc_core_arch_gpfn_may_present(&arch_ctxt, i) ) continue; gmfn = i; pfn_array[j] = i; } vaddr = xc_map_foreign_range( xch, domid, PAGE_SIZE, PROT_READ, gmfn); if ( vaddr == NULL ) continue; memcpy(dump_mem, vaddr, PAGE_SIZE); munmap(vaddr, PAGE_SIZE); dump_mem += PAGE_SIZE; if ( (j + 1) % DUMP_INCREMENT == 0 ) { sts = dump_rtn( xch, args, dump_mem_start, dump_mem - dump_mem_start); if ( sts != 0 ) goto out; dump_mem = dump_mem_start; } j++; } } copy_done: sts = dump_rtn(xch, args, dump_mem_start, dump_mem - dump_mem_start); if ( sts != 0 ) goto out; if ( j < nr_pages ) { /* When live dump-mode (-L option) is specified, * guest domain may reduce memory. pad with zero pages. */ DPRINTF("j (%ld) != nr_pages (%ld)", j, nr_pages); memset(dump_mem_start, 0, PAGE_SIZE); for (; j < nr_pages; j++) { sts = dump_rtn(xch, args, dump_mem_start, PAGE_SIZE); if ( sts != 0 ) goto out; if ( !auto_translated_physmap ) { p2m_array[j].pfn = XC_CORE_INVALID_PFN; p2m_array[j].gmfn = XC_CORE_INVALID_GMFN; } else pfn_array[j] = XC_CORE_INVALID_PFN; } } /* p2m/pfn table: .xen_p2m/.xen_pfn */ if ( !auto_translated_physmap ) sts = dump_rtn( xch, args, (char *)p2m_array, sizeof(p2m_array[0]) * nr_pages); else sts = dump_rtn( xch, args, (char *)pfn_array, sizeof(pfn_array[0]) * nr_pages); if ( sts != 0 ) goto out; /* elf section header string table: .shstrtab */ sts = dump_rtn(xch, args, strtab->strings, strtab->length); if ( sts != 0 ) goto out; sts = 0; out: if ( memory_map != NULL ) free(memory_map); if ( p2m != NULL ) munmap(p2m, PAGE_SIZE * P2M_FL_ENTRIES); if ( p2m_array != NULL ) free(p2m_array); if ( pfn_array != NULL ) free(pfn_array); if ( sheaders != NULL ) xc_core_shdr_free(sheaders); if ( strtab != NULL ) xc_core_strtab_free(strtab); if ( ctxt != NULL ) free(ctxt); if ( dump_mem_start != NULL ) free(dump_mem_start); if ( live_shinfo != NULL ) munmap(live_shinfo, PAGE_SIZE); xc_core_arch_context_free(&arch_ctxt); return sts; } /* Callback args for writing to a local dump file. */ struct dump_args { int fd; }; /* Callback routine for writing to a local dump file. */ static int local_file_dump(xc_interface *xch, void *args, char *buffer, unsigned int length) { struct dump_args *da = args; if ( write_exact(da->fd, buffer, length) == -1 ) { PERROR("Failed to write buffer"); return -errno; } if ( length >= (DUMP_INCREMENT * PAGE_SIZE) ) { // Now dumping pages -- make sure we discard clean pages from // the cache after each write discard_file_cache(xch, da->fd, 0 /* no flush */); } return 0; } int xc_domain_dumpcore(xc_interface *xch, uint32_t domid, const char *corename) { struct dump_args da; int sts; if ( (da.fd = open(corename, O_CREAT|O_RDWR|O_TRUNC, S_IWUSR|S_IRUSR)) < 0 ) { PERROR("Could not open corefile %s", corename); return -errno; } sts = xc_domain_dumpcore_via_callback( xch, domid, &da, &local_file_dump); /* flush and discard any remaining portion of the file from cache */ discard_file_cache(xch, da.fd, 1/* flush first*/); close(da.fd); return sts; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_kexec.c0000664000175000017500000000772113256712137014662 0ustar smbsmb/****************************************************************************** * xc_kexec.c * * API for loading and executing kexec images. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * Copyright (C) 2013 Citrix Systems R&D Ltd. */ #include "xc_private.h" int xc_kexec_exec(xc_interface *xch, int type) { DECLARE_HYPERCALL_BUFFER(xen_kexec_exec_t, exec); int ret = -1; exec = xc_hypercall_buffer_alloc(xch, exec, sizeof(*exec)); if ( exec == NULL ) { PERROR("Could not alloc bounce buffer for kexec_exec hypercall"); goto out; } exec->type = type; ret = xencall2(xch->xcall, __HYPERVISOR_kexec_op, KEXEC_CMD_kexec, HYPERCALL_BUFFER_AS_ARG(exec)); out: xc_hypercall_buffer_free(xch, exec); return ret; } int xc_kexec_get_range(xc_interface *xch, int range, int nr, uint64_t *size, uint64_t *start) { DECLARE_HYPERCALL_BUFFER(xen_kexec_range_t, get_range); int ret = -1; get_range = xc_hypercall_buffer_alloc(xch, get_range, sizeof(*get_range)); if ( get_range == NULL ) { PERROR("Could not alloc bounce buffer for kexec_get_range hypercall"); goto out; } get_range->range = range; get_range->nr = nr; ret = xencall2(xch->xcall, __HYPERVISOR_kexec_op, KEXEC_CMD_kexec_get_range, HYPERCALL_BUFFER_AS_ARG(get_range)); *size = get_range->size; *start = get_range->start; out: xc_hypercall_buffer_free(xch, get_range); return ret; } int xc_kexec_load(xc_interface *xch, uint8_t type, uint16_t arch, uint64_t entry_maddr, uint32_t nr_segments, xen_kexec_segment_t *segments) { int ret = -1; DECLARE_HYPERCALL_BOUNCE(segments, sizeof(*segments) * nr_segments, XC_HYPERCALL_BUFFER_BOUNCE_IN); DECLARE_HYPERCALL_BUFFER(xen_kexec_load_t, load); if ( xc_hypercall_bounce_pre(xch, segments) ) { PERROR("Could not allocate bounce buffer for kexec load hypercall"); goto out; } load = xc_hypercall_buffer_alloc(xch, load, sizeof(*load)); if ( load == NULL ) { PERROR("Could not allocate buffer for kexec load hypercall"); goto out; } load->type = type; load->arch = arch; load->entry_maddr = entry_maddr; load->nr_segments = nr_segments; set_xen_guest_handle(load->segments.h, segments); ret = xencall2(xch->xcall, __HYPERVISOR_kexec_op, KEXEC_CMD_kexec_load, HYPERCALL_BUFFER_AS_ARG(load)); out: xc_hypercall_buffer_free(xch, load); xc_hypercall_bounce_post(xch, segments); return ret; } int xc_kexec_unload(xc_interface *xch, int type) { DECLARE_HYPERCALL_BUFFER(xen_kexec_unload_t, unload); int ret = -1; unload = xc_hypercall_buffer_alloc(xch, unload, sizeof(*unload)); if ( unload == NULL ) { PERROR("Could not alloc buffer for kexec unload hypercall"); goto out; } unload->type = type; ret = xencall2(xch->xcall, __HYPERVISOR_kexec_op, KEXEC_CMD_kexec_unload, HYPERCALL_BUFFER_AS_ARG(unload)); out: xc_hypercall_buffer_free(xch, unload); return ret; } int xc_kexec_status(xc_interface *xch, int type) { DECLARE_HYPERCALL_BUFFER(xen_kexec_status_t, status); int ret = -1; status = xc_hypercall_buffer_alloc(xch, status, sizeof(*status)); if ( status == NULL ) { PERROR("Could not alloc buffer for kexec status hypercall"); goto out; } status->type = type; ret = xencall2(xch->xcall, __HYPERVISOR_kexec_op, KEXEC_CMD_kexec_status, HYPERCALL_BUFFER_AS_ARG(status)); out: xc_hypercall_buffer_free(xch, status); return ret; } xen-4.9.2/tools/libxc/xc_dom_decompress_lz4.c0000664000175000017500000000510413256712137017350 0ustar smbsmb#include #include #include #include #include "xg_private.h" #include "xc_dom_decompress.h" #define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #define likely(a) a #define unlikely(a) a static inline uint_fast16_t le16_to_cpup(const unsigned char *buf) { return buf[0] | (buf[1] << 8); } static inline uint_fast32_t le32_to_cpup(const unsigned char *buf) { return le16_to_cpup(buf) | ((uint32_t)le16_to_cpup(buf + 2) << 16); } #include "../../xen/include/xen/lz4.h" #include "../../xen/common/decompress.h" #ifndef __MINIOS__ #include "../../xen/common/lz4/decompress.c" #define ARCHIVE_MAGICNUMBER 0x184C2102 int xc_try_lz4_decode( struct xc_dom_image *dom, void **blob, size_t *psize) { int ret = -1; unsigned char *inp = *blob, *output, *outp; ssize_t size = *psize - 4; size_t out_len, dest_len, chunksize; const char *msg; if (size < 4) { msg = "input too small"; goto exit_0; } out_len = get_unaligned_le32(inp + size); if (xc_dom_kernel_check_size(dom, out_len)) { msg = "Decompressed image too large"; goto exit_0; } output = malloc(out_len); if (!output) { msg = "Could not allocate output buffer"; goto exit_0; } outp = output; chunksize = get_unaligned_le32(inp); if (chunksize == ARCHIVE_MAGICNUMBER) { inp += 4; size -= 4; } else { msg = "invalid header"; goto exit_2; } for (;;) { if (size < 4) { msg = "missing data"; goto exit_2; } chunksize = get_unaligned_le32(inp); if (chunksize == ARCHIVE_MAGICNUMBER) { inp += 4; size -= 4; continue; } inp += 4; size -= 4; if (chunksize > size) { msg = "insufficient input data"; goto exit_2; } dest_len = out_len - (outp - output); ret = lz4_decompress_unknownoutputsize(inp, chunksize, outp, &dest_len); if (ret < 0) { msg = "decoding failed"; goto exit_2; } ret = -1; outp += dest_len; size -= chunksize; if (size == 0) { if ( xc_dom_register_external(dom, output, out_len) ) { msg = "Error registering stream output"; goto exit_2; } *blob = output; *psize = out_len; return 0; } if (size < 0) { msg = "data corrupted"; goto exit_2; } inp += chunksize; } exit_2: free(output); exit_0: DOMPRINTF("LZ4 decompression error: %s\n", msg); return ret; } #else /* __MINIOS__ */ #include "../../xen/common/unlz4.c" int xc_try_lz4_decode( struct xc_dom_image *dom, void **blob, size_t *size) { return xc_dom_decompress_unsafe(unlz4, dom, blob, size); } #endif xen-4.9.2/tools/libxc/include/0000775000175000017500000000000013256712137014341 5ustar smbsmbxen-4.9.2/tools/libxc/include/xc_dom.h0000664000175000017500000003471613256712137015776 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #ifndef _XC_DOM_H #define _XC_DOM_H #include #include #define INVALID_PFN ((xen_pfn_t)-1) #define X86_HVM_NR_SPECIAL_PAGES 8 #define X86_HVM_END_SPECIAL_REGION 0xff000u /* --- typedefs and structs ---------------------------------------- */ typedef uint64_t xen_vaddr_t; typedef uint64_t xen_paddr_t; #define PRIpfn PRI_xen_pfn struct xc_dom_seg { xen_vaddr_t vstart; xen_vaddr_t vend; xen_pfn_t pfn; xen_pfn_t pages; }; struct xc_dom_mem { struct xc_dom_mem *next; void *ptr; enum { XC_DOM_MEM_TYPE_MALLOC_INTERNAL, XC_DOM_MEM_TYPE_MALLOC_EXTERNAL, XC_DOM_MEM_TYPE_MMAP, } type; size_t len; unsigned char memory[0]; }; struct xc_dom_phys { struct xc_dom_phys *next; void *ptr; xen_pfn_t first; xen_pfn_t count; }; struct xc_dom_image { /* files */ void *kernel_blob; size_t kernel_size; void *ramdisk_blob; size_t ramdisk_size; void *devicetree_blob; size_t devicetree_size; size_t max_kernel_size; size_t max_ramdisk_size; size_t max_devicetree_size; /* arguments and parameters */ char *cmdline; size_t cmdline_size; uint32_t f_requested[XENFEAT_NR_SUBMAPS]; /* info from (elf) kernel image */ struct elf_dom_parms parms; char *guest_type; /* memory layout */ struct xc_dom_seg kernel_seg; /* If ramdisk_seg.vstart is non zero then the ramdisk will be * loaded at that address, otherwise it will automatically placed. * * If automatic placement is used and the ramdisk is gzip * compressed then it will be decompressed as it is loaded. If the * ramdisk has been explicitly placed then it is loaded as is * otherwise decompressing risks undoing the manual placement. */ struct xc_dom_seg ramdisk_seg; struct xc_dom_seg p2m_seg; struct xc_dom_seg pgtables_seg; struct xc_dom_seg devicetree_seg; struct xc_dom_seg start_info_seg; /* HVMlite only */ xen_pfn_t start_info_pfn; xen_pfn_t console_pfn; xen_pfn_t xenstore_pfn; xen_pfn_t shared_info_pfn; xen_pfn_t bootstack_pfn; xen_pfn_t pfn_alloc_end; xen_vaddr_t virt_alloc_end; xen_vaddr_t bsd_symtab_start; /* * initrd parameters as specified in start_info page * Depending on capabilities of the booted kernel this may be a virtual * address or a pfn. Type is neutral and large enough to hold a virtual * address of a 64 bit kernel even with 32 bit toolstack. */ uint64_t initrd_start; uint64_t initrd_len; unsigned int alloc_bootstack; xen_vaddr_t virt_pgtab_end; /* other state info */ uint32_t f_active[XENFEAT_NR_SUBMAPS]; /* * p2m_host maps guest physical addresses an offset from * rambase_pfn (see below) into gfns. * * For a pure PV guest this means that it maps GPFNs into MFNs for * a hybrid guest this means that it maps GPFNs to GPFNS. * * Note that the input is offset by rambase. */ xen_pfn_t *p2m_host; void *p2m_guest; /* physical memory * * An x86 PV guest has one or more blocks of physical RAM, * consisting of total_pages starting at rambase_pfn. The start * address and size of each block is controlled by vNUMA * structures. * * An ARM guest has GUEST_RAM_BANKS regions of RAM, with * rambank_size[i] pages in each. The lowest RAM address * (corresponding to the base of the p2m arrays above) is stored * in rambase_pfn. */ xen_pfn_t rambase_pfn; xen_pfn_t total_pages; xen_pfn_t p2m_size; /* number of pfns covered by p2m */ struct xc_dom_phys *phys_pages; #if defined (__arm__) || defined(__aarch64__) xen_pfn_t rambank_size[GUEST_RAM_BANKS]; #endif /* malloc memory pool */ struct xc_dom_mem *memblocks; /* memory footprint stats */ size_t alloc_malloc; size_t alloc_mem_map; size_t alloc_file_map; size_t alloc_domU_map; /* misc xen domain config stuff */ unsigned long flags; unsigned int console_evtchn; unsigned int xenstore_evtchn; domid_t console_domid; domid_t xenstore_domid; xen_pfn_t shared_info_mfn; xc_interface *xch; domid_t guest_domid; int claim_enabled; /* 0 by default, 1 enables it */ int xen_version; xen_capabilities_info_t xen_caps; /* kernel loader, arch hooks */ struct xc_dom_loader *kernel_loader; void *private_loader; /* vNUMA information */ xen_vmemrange_t *vmemranges; unsigned int nr_vmemranges; unsigned int *vnode_to_pnode; unsigned int nr_vnodes; /* domain type/architecture specific data */ void *arch_private; /* kernel loader */ struct xc_dom_arch *arch_hooks; /* allocate up to pfn_alloc_end */ int (*allocate) (struct xc_dom_image * dom); /* Container type (HVM or PV). */ enum { XC_DOM_PV_CONTAINER, XC_DOM_HVM_CONTAINER, } container_type; /* HVM specific fields. */ xen_pfn_t target_pages; xen_paddr_t mmio_start; xen_paddr_t mmio_size; xen_paddr_t lowmem_end; xen_paddr_t highmem_end; xen_pfn_t vga_hole_size; /* If unset disables the setup of the IOREQ pages. */ bool device_model; /* BIOS/Firmware passed to HVMLOADER */ struct xc_hvm_firmware_module system_firmware_module; /* Extra ACPI tables */ #define MAX_ACPI_MODULES 4 struct xc_hvm_firmware_module acpi_modules[MAX_ACPI_MODULES]; /* Extra SMBIOS structures passed to HVMLOADER */ struct xc_hvm_firmware_module smbios_module; }; /* --- pluggable kernel loader ------------------------------------- */ struct xc_dom_loader { char *name; /* Sadly the error returns from these functions are not consistent: */ elf_negerrnoval (*probe) (struct xc_dom_image * dom); elf_negerrnoval (*parser) (struct xc_dom_image * dom); elf_errorstatus (*loader) (struct xc_dom_image * dom); struct xc_dom_loader *next; }; #define __init __attribute__ ((constructor)) void xc_dom_register_loader(struct xc_dom_loader *loader); /* --- arch specific hooks ----------------------------------------- */ struct xc_dom_arch { /* pagetable setup */ int (*alloc_magic_pages) (struct xc_dom_image * dom); int (*alloc_pgtables) (struct xc_dom_image * dom); int (*alloc_p2m_list) (struct xc_dom_image * dom); int (*setup_pgtables) (struct xc_dom_image * dom); /* arch-specific data structs setup */ int (*start_info) (struct xc_dom_image * dom); int (*shared_info) (struct xc_dom_image * dom, void *shared_info); int (*vcpu) (struct xc_dom_image * dom); int (*bootearly) (struct xc_dom_image * dom); int (*bootlate) (struct xc_dom_image * dom); /* arch-specific memory initialization. */ int (*meminit) (struct xc_dom_image * dom); char *guest_type; char *native_protocol; int page_shift; int sizeof_pfn; int p2m_base_supported; int arch_private_size; struct xc_dom_arch *next; }; void xc_dom_register_arch_hooks(struct xc_dom_arch *hooks); #define XC_DOM_PAGE_SHIFT(dom) ((dom)->arch_hooks->page_shift) #define XC_DOM_PAGE_SIZE(dom) (1LL << (dom)->arch_hooks->page_shift) /* --- main functions ---------------------------------------------- */ struct xc_dom_image *xc_dom_allocate(xc_interface *xch, const char *cmdline, const char *features); void xc_dom_release_phys(struct xc_dom_image *dom); void xc_dom_release(struct xc_dom_image *dom); int xc_dom_rambase_init(struct xc_dom_image *dom, uint64_t rambase); int xc_dom_mem_init(struct xc_dom_image *dom, unsigned int mem_mb); /* Set this larger if you have enormous ramdisks/kernels. Note that * you should trust all kernels not to be maliciously large (e.g. to * exhaust all dom0 memory) if you do this (see CVE-2012-4544 / * XSA-25). You can also set the default independently for * ramdisks/kernels in xc_dom_allocate() or call * xc_dom_{kernel,ramdisk}_max_size. */ #ifndef XC_DOM_DECOMPRESS_MAX #define XC_DOM_DECOMPRESS_MAX (1024*1024*1024) /* 1GB */ #endif int xc_dom_kernel_check_size(struct xc_dom_image *dom, size_t sz); int xc_dom_kernel_max_size(struct xc_dom_image *dom, size_t sz); int xc_dom_ramdisk_check_size(struct xc_dom_image *dom, size_t sz); int xc_dom_ramdisk_max_size(struct xc_dom_image *dom, size_t sz); int xc_dom_devicetree_max_size(struct xc_dom_image *dom, size_t sz); size_t xc_dom_check_gzip(xc_interface *xch, void *blob, size_t ziplen); int xc_dom_do_gunzip(xc_interface *xch, void *src, size_t srclen, void *dst, size_t dstlen); int xc_dom_try_gunzip(struct xc_dom_image *dom, void **blob, size_t * size); int xc_dom_kernel_file(struct xc_dom_image *dom, const char *filename); int xc_dom_ramdisk_file(struct xc_dom_image *dom, const char *filename); int xc_dom_kernel_mem(struct xc_dom_image *dom, const void *mem, size_t memsize); int xc_dom_ramdisk_mem(struct xc_dom_image *dom, const void *mem, size_t memsize); int xc_dom_devicetree_file(struct xc_dom_image *dom, const char *filename); int xc_dom_devicetree_mem(struct xc_dom_image *dom, const void *mem, size_t memsize); int xc_dom_parse_image(struct xc_dom_image *dom); int xc_dom_set_arch_hooks(struct xc_dom_image *dom); int xc_dom_build_image(struct xc_dom_image *dom); int xc_dom_update_guest_p2m(struct xc_dom_image *dom); int xc_dom_boot_xen_init(struct xc_dom_image *dom, xc_interface *xch, domid_t domid); int xc_dom_boot_mem_init(struct xc_dom_image *dom); void *xc_dom_boot_domU_map(struct xc_dom_image *dom, xen_pfn_t pfn, xen_pfn_t count); int xc_dom_boot_image(struct xc_dom_image *dom); int xc_dom_compat_check(struct xc_dom_image *dom); int xc_dom_gnttab_init(struct xc_dom_image *dom); int xc_dom_gnttab_hvm_seed(xc_interface *xch, domid_t domid, xen_pfn_t console_gmfn, xen_pfn_t xenstore_gmfn, domid_t console_domid, domid_t xenstore_domid); int xc_dom_gnttab_seed(xc_interface *xch, domid_t domid, xen_pfn_t console_gmfn, xen_pfn_t xenstore_gmfn, domid_t console_domid, domid_t xenstore_domid); bool xc_dom_translated(const struct xc_dom_image *dom); /* --- debugging bits ---------------------------------------------- */ int xc_dom_loginit(xc_interface *xch); void xc_dom_printf(xc_interface *xch, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); void xc_dom_panic_func(xc_interface *xch, const char *file, int line, xc_error_code err, const char *fmt, ...) __attribute__ ((format(printf, 5, 6))); #define xc_dom_panic(xch, err, fmt, args...) \ xc_dom_panic_func(xch, __FILE__, __LINE__, err, fmt, ## args) #define xc_dom_trace(mark) \ xc_dom_printf("%s:%d: trace %s\n", __FILE__, __LINE__, mark) void xc_dom_log_memory_footprint(struct xc_dom_image *dom); /* --- simple memory pool ------------------------------------------ */ void *xc_dom_malloc(struct xc_dom_image *dom, size_t size); int xc_dom_register_external(struct xc_dom_image *dom, void *ptr, size_t size); void *xc_dom_malloc_page_aligned(struct xc_dom_image *dom, size_t size); void *xc_dom_malloc_filemap(struct xc_dom_image *dom, const char *filename, size_t * size, const size_t max_size); char *xc_dom_strdup(struct xc_dom_image *dom, const char *str); /* --- alloc memory pool ------------------------------------------- */ xen_pfn_t xc_dom_alloc_page(struct xc_dom_image *dom, char *name); int xc_dom_alloc_segment(struct xc_dom_image *dom, struct xc_dom_seg *seg, char *name, xen_vaddr_t start, xen_vaddr_t size); /* --- misc bits --------------------------------------------------- */ void *xc_dom_pfn_to_ptr(struct xc_dom_image *dom, xen_pfn_t first, xen_pfn_t count); void *xc_dom_pfn_to_ptr_retcount(struct xc_dom_image *dom, xen_pfn_t first, xen_pfn_t count, xen_pfn_t *count_out); void xc_dom_unmap_one(struct xc_dom_image *dom, xen_pfn_t pfn); void xc_dom_unmap_all(struct xc_dom_image *dom); static inline void *xc_dom_seg_to_ptr_pages(struct xc_dom_image *dom, struct xc_dom_seg *seg, xen_pfn_t *pages_out) { void *retval; retval = xc_dom_pfn_to_ptr(dom, seg->pfn, seg->pages); *pages_out = retval ? seg->pages : 0; return retval; } static inline void *xc_dom_seg_to_ptr(struct xc_dom_image *dom, struct xc_dom_seg *seg) { xen_pfn_t dummy; return xc_dom_seg_to_ptr_pages(dom, seg, &dummy); } static inline void *xc_dom_vaddr_to_ptr(struct xc_dom_image *dom, xen_vaddr_t vaddr, size_t *safe_region_out) { unsigned int page_size = XC_DOM_PAGE_SIZE(dom); xen_pfn_t page = (vaddr - dom->parms.virt_base) / page_size; unsigned int offset = (vaddr - dom->parms.virt_base) % page_size; xen_pfn_t safe_region_count; void *ptr; *safe_region_out = 0; ptr = xc_dom_pfn_to_ptr_retcount(dom, page, 0, &safe_region_count); if ( ptr == NULL ) return ptr; *safe_region_out = (safe_region_count << XC_DOM_PAGE_SHIFT(dom)) - offset; return ptr + offset; } static inline xen_pfn_t xc_dom_p2m(struct xc_dom_image *dom, xen_pfn_t pfn) { if ( xc_dom_translated(dom) ) return pfn; if (pfn < dom->rambase_pfn || pfn >= dom->rambase_pfn + dom->total_pages) return INVALID_MFN; return dom->p2m_host[pfn - dom->rambase_pfn]; } #endif /* _XC_DOM_H */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/include/xenguest.h0000664000175000017500000002357513256712137016370 0ustar smbsmb/****************************************************************************** * xenguest.h * * A library for guest domain management in Xen. * * Copyright (c) 2003-2004, K A Fraser. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #ifndef XENGUEST_H #define XENGUEST_H #define XC_NUMA_NO_NODE (~0U) #define XCFLAGS_LIVE (1 << 0) #define XCFLAGS_DEBUG (1 << 1) #define XCFLAGS_HVM (1 << 2) #define XCFLAGS_STDVGA (1 << 3) #define XCFLAGS_CHECKPOINT_COMPRESS (1 << 4) #define X86_64_B_SIZE 64 #define X86_32_B_SIZE 32 /* * User not using xc_suspend_* / xc_await_suspent may not want to * include the full libxenevtchn API here. */ struct xenevtchn_handle; /* callbacks provided by xc_domain_save */ struct save_callbacks { /* Called after expiration of checkpoint interval, * to suspend the guest. */ int (*suspend)(void* data); /* Called after the guest's dirty pages have been * copied into an output buffer. * Callback function resumes the guest & the device model, * returns to xc_domain_save. * xc_domain_save then flushes the output buffer, while the * guest continues to run. */ int (*postcopy)(void* data); /* Called after the memory checkpoint has been flushed * out into the network. Typical actions performed in this * callback include: * (a) send the saved device model state (for HVM guests), * (b) wait for checkpoint ack * (c) release the network output buffer pertaining to the acked checkpoint. * (c) sleep for the checkpoint interval. * * returns: * 0: terminate checkpointing gracefully * 1: take another checkpoint */ int (*checkpoint)(void* data); /* * Called after the checkpoint callback. * * returns: * 0: terminate checkpointing gracefully * 1: take another checkpoint */ int (*wait_checkpoint)(void* data); /* Enable qemu-dm logging dirty pages to xen */ int (*switch_qemu_logdirty)(int domid, unsigned enable, void *data); /* HVM only */ /* to be provided as the last argument to each callback function */ void* data; }; typedef enum { XC_MIG_STREAM_NONE, /* plain stream */ XC_MIG_STREAM_REMUS, XC_MIG_STREAM_COLO, } xc_migration_stream_t; /** * This function will save a running domain. * * @parm xch a handle to an open hypervisor interface * @parm fd the file descriptor to save a domain to * @parm dom the id of the domain * @param stream_type XC_MIG_STREAM_NONE if the far end of the stream * doesn't use checkpointing * @return 0 on success, -1 on failure */ int xc_domain_save(xc_interface *xch, int io_fd, uint32_t dom, uint32_t max_iters, uint32_t max_factor, uint32_t flags /* XCFLAGS_xxx */, struct save_callbacks* callbacks, int hvm, xc_migration_stream_t stream_type, int recv_fd); /* callbacks provided by xc_domain_restore */ struct restore_callbacks { /* Called after a new checkpoint to suspend the guest. */ int (*suspend)(void* data); /* Called after the secondary vm is ready to resume. * Callback function resumes the guest & the device model, * returns to xc_domain_restore. */ int (*postcopy)(void* data); /* A checkpoint record has been found in the stream. * returns: */ #define XGR_CHECKPOINT_ERROR 0 /* Terminate processing */ #define XGR_CHECKPOINT_SUCCESS 1 /* Continue reading more data from the stream */ #define XGR_CHECKPOINT_FAILOVER 2 /* Failover and resume VM */ int (*checkpoint)(void* data); /* * Called after the checkpoint callback. * * returns: * 0: terminate checkpointing gracefully * 1: take another checkpoint */ int (*wait_checkpoint)(void* data); /* * callback to send store gfn and console gfn to xl * if we want to resume vm before xc_domain_save() * exits. */ void (*restore_results)(xen_pfn_t store_gfn, xen_pfn_t console_gfn, void *data); /* to be provided as the last argument to each callback function */ void* data; }; /** * This function will restore a saved domain. * * Domain is restored in a suspended state ready to be unpaused. * * @parm xch a handle to an open hypervisor interface * @parm fd the file descriptor to restore a domain from * @parm dom the id of the domain * @parm store_evtchn the store event channel for this domain to use * @parm store_mfn returned with the mfn of the store page * @parm hvm non-zero if this is a HVM restore * @parm pae non-zero if this HVM domain has PAE support enabled * @parm superpages non-zero to allocate guest memory with superpages * @parm stream_type non-zero if the far end of the stream is using checkpointing * @parm callbacks non-NULL to receive a callback to restore toolstack * specific data * @return 0 on success, -1 on failure */ int xc_domain_restore(xc_interface *xch, int io_fd, uint32_t dom, unsigned int store_evtchn, unsigned long *store_mfn, domid_t store_domid, unsigned int console_evtchn, unsigned long *console_mfn, domid_t console_domid, unsigned int hvm, unsigned int pae, int superpages, xc_migration_stream_t stream_type, struct restore_callbacks *callbacks, int send_back_fd); /** * This function will create a domain for a paravirtualized Linux * using file names pointing to kernel and ramdisk * * @parm xch a handle to an open hypervisor interface * @parm domid the id of the domain * @parm mem_mb memory size in megabytes * @parm image_name name of the kernel image file * @parm ramdisk_name name of the ramdisk image file * @parm cmdline command line string * @parm flags domain creation flags * @parm store_evtchn the store event channel for this domain to use * @parm store_mfn returned with the mfn of the store page * @parm console_evtchn the console event channel for this domain to use * @parm conole_mfn returned with the mfn of the console page * @return 0 on success, -1 on failure */ int xc_linux_build(xc_interface *xch, uint32_t domid, unsigned int mem_mb, const char *image_name, const char *ramdisk_name, const char *cmdline, const char *features, unsigned long flags, unsigned int store_evtchn, unsigned long *store_mfn, unsigned int console_evtchn, unsigned long *console_mfn); struct xc_hvm_firmware_module { uint8_t *data; uint32_t length; uint64_t guest_addr_out; }; /* * Sets *lockfd to -1. * Has deallocated everything even on error. */ int xc_suspend_evtchn_release(xc_interface *xch, struct xenevtchn_handle *xce, int domid, int suspend_evtchn, int *lockfd); /** * This function eats the initial notification. * xce must not be used for anything else * See xc_suspend_evtchn_init_sane re lockfd. */ int xc_suspend_evtchn_init_exclusive(xc_interface *xch, struct xenevtchn_handle *xce, int domid, int port, int *lockfd); /* xce must not be used for anything else */ int xc_await_suspend(xc_interface *xch, struct xenevtchn_handle *xce, int suspend_evtchn); /** * The port will be signaled immediately after this call * The caller should check the domain status and look for the next event * On success, *lockfd will be set to >=0 and *lockfd must be preserved * and fed to xc_suspend_evtchn_release. (On error *lockfd is * undefined and xc_suspend_evtchn_release is not allowed.) */ int xc_suspend_evtchn_init_sane(xc_interface *xch, struct xenevtchn_handle *xce, int domid, int port, int *lockfd); int xc_mark_page_online(xc_interface *xch, unsigned long start, unsigned long end, uint32_t *status); int xc_mark_page_offline(xc_interface *xch, unsigned long start, unsigned long end, uint32_t *status); int xc_query_page_offline_status(xc_interface *xch, unsigned long start, unsigned long end, uint32_t *status); int xc_exchange_page(xc_interface *xch, int domid, xen_pfn_t mfn); /** * Memory related information, such as PFN types, the P2M table, * the guest word width and the guest page table levels. */ struct xc_domain_meminfo { unsigned int pt_levels; unsigned int guest_width; xen_pfn_t *pfn_type; xen_pfn_t *p2m_table; unsigned long p2m_size; }; int xc_map_domain_meminfo(xc_interface *xch, int domid, struct xc_domain_meminfo *minfo); int xc_unmap_domain_meminfo(xc_interface *xch, struct xc_domain_meminfo *mem); /** * This function map m2p table * @parm xch a handle to an open hypervisor interface * @parm max_mfn the max pfn * @parm prot the flags to map, such as read/write etc * @parm mfn0 return the first mfn, can be NULL * @return mapped m2p table on success, NULL on failure */ xen_pfn_t *xc_map_m2p(xc_interface *xch, unsigned long max_mfn, int prot, unsigned long *mfn0); #endif /* XENGUEST_H */ xen-4.9.2/tools/libxc/include/xenctrl_compat.h0000664000175000017500000001557613256712137017552 0ustar smbsmb/* * Compat shims for use of 3rd party consumers of libxenctrl * functionality which has been split into separate libraries. * * New code should use the separate libraries. * * Each interface must be opted-into separately by defining: * * XC_WANT_COMPAT_EVTCHN_API * - Functions relating to /dev/xen/evtchn */ #ifndef XENCTRL_COMPAT_H #define XENCTRL_COMPAT_H #ifdef XC_WANT_COMPAT_MAP_FOREIGN_API /** * Memory maps a range within one domain to a local address range. Mappings * should be unmapped with munmap and should follow the same rules as mmap * regarding page alignment. Returns NULL on failure. * * @parm xch a handle on an open hypervisor interface * @parm dom the domain to map memory from * @parm size the amount of memory to map (in multiples of page size) * @parm prot same flag as in mmap(). * @parm mfn the frame address to map. */ void *xc_map_foreign_range(xc_interface *xch, uint32_t dom, int size, int prot, unsigned long mfn ); void *xc_map_foreign_pages(xc_interface *xch, uint32_t dom, int prot, const xen_pfn_t *arr, int num ); /* Nothing within the library itself other than the compat wrapper * itself should be using this, everything inside has access to * xenforeignmemory_map(). */ #if !defined(XC_INTERNAL_COMPAT_MAP_FOREIGN_API) || \ defined(XC_BUILDING_COMPAT_MAP_FOREIGN_API) /** * Like xc_map_foreign_pages(), except it can succeed partially. * When a page cannot be mapped, its respective field in @err is * set to the corresponding errno value. */ void *xc_map_foreign_bulk(xc_interface *xch, uint32_t dom, int prot, const xen_pfn_t *arr, int *err, unsigned int num); #endif #endif #ifdef XC_WANT_COMPAT_EVTCHN_API typedef struct xenevtchn_handle xc_evtchn; typedef xc_evtchn_port_or_error_t evtchn_port_or_error_t; xc_evtchn *xc_evtchn_open(xentoollog_logger *logger, unsigned open_flags); int xc_evtchn_close(xc_evtchn *xce); int xc_evtchn_fd(xc_evtchn *xce); int xc_evtchn_notify(xc_evtchn *xce, evtchn_port_t port); xc_evtchn_port_or_error_t xc_evtchn_bind_unbound_port(xc_evtchn *xce, int domid); xc_evtchn_port_or_error_t xc_evtchn_bind_interdomain(xc_evtchn *xce, int domid, evtchn_port_t remote_port); xc_evtchn_port_or_error_t xc_evtchn_bind_virq(xc_evtchn *xce, unsigned int virq); int xc_evtchn_unbind(xc_evtchn *xce, evtchn_port_t port); xc_evtchn_port_or_error_t xc_evtchn_pending(xc_evtchn *xce); int xc_evtchn_unmask(xc_evtchn *xce, evtchn_port_t port); #endif /* XC_WANT_COMPAT_EVTCHN_API */ #ifdef XC_WANT_COMPAT_GNTTAB_API typedef struct xengntdev_handle xc_gnttab; xc_gnttab *xc_gnttab_open(xentoollog_logger *logger, unsigned open_flags); int xc_gnttab_close(xc_gnttab *xcg); void *xc_gnttab_map_grant_ref(xc_gnttab *xcg, uint32_t domid, uint32_t ref, int prot); void *xc_gnttab_map_grant_refs(xc_gnttab *xcg, uint32_t count, uint32_t *domids, uint32_t *refs, int prot); void *xc_gnttab_map_domain_grant_refs(xc_gnttab *xcg, uint32_t count, uint32_t domid, uint32_t *refs, int prot); void *xc_gnttab_map_grant_ref_notify(xc_gnttab *xcg, uint32_t domid, uint32_t ref, int prot, uint32_t notify_offset, evtchn_port_t notify_port); int xc_gnttab_munmap(xc_gnttab *xcg, void *start_address, uint32_t count); int xc_gnttab_set_max_grants(xc_gnttab *xcg, uint32_t count); typedef struct xengntdev_handle xc_gntshr; xc_gntshr *xc_gntshr_open(xentoollog_logger *logger, unsigned open_flags); int xc_gntshr_close(xc_gntshr *xcg); void *xc_gntshr_share_pages(xc_gntshr *xcg, uint32_t domid, int count, uint32_t *refs, int writable); void *xc_gntshr_share_page_notify(xc_gntshr *xcg, uint32_t domid, uint32_t *ref, int writable, uint32_t notify_offset, evtchn_port_t notify_port); int xc_gntshr_munmap(xc_gntshr *xcg, void *start_address, uint32_t count); #endif /* XC_WANT_COMPAT_GNTTAB_API */ #ifdef XC_WANT_COMPAT_DEVICEMODEL_API int xc_hvm_create_ioreq_server( xc_interface *xch, domid_t domid, int handle_bufioreq, ioservid_t *id); int xc_hvm_get_ioreq_server_info( xc_interface *xch, domid_t domid, ioservid_t id, xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, evtchn_port_t *bufioreq_port); int xc_hvm_map_io_range_to_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id, int is_mmio, uint64_t start, uint64_t end); int xc_hvm_unmap_io_range_from_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id, int is_mmio, uint64_t start, uint64_t end); int xc_hvm_map_pcidev_to_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id, uint16_t segment, uint8_t bus, uint8_t device, uint8_t function); int xc_hvm_unmap_pcidev_from_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id, uint16_t segment, uint8_t bus, uint8_t device, uint8_t function); int xc_hvm_destroy_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id); int xc_hvm_set_ioreq_server_state( xc_interface *xch, domid_t domid, ioservid_t id, int enabled); int xc_hvm_set_pci_intx_level( xc_interface *xch, domid_t domid, uint16_t segment, uint8_t bus, uint8_t device, uint8_t intx, unsigned int level); int xc_hvm_set_isa_irq_level( xc_interface *xch, domid_t domid, uint8_t irq, unsigned int level); int xc_hvm_set_pci_link_route( xc_interface *xch, domid_t domid, uint8_t link, uint8_t irq); int xc_hvm_inject_msi( xc_interface *xch, domid_t domid, uint64_t msi_addr, uint32_t msi_data); int xc_hvm_track_dirty_vram( xc_interface *xch, domid_t domid, uint64_t first_pfn, uint32_t nr, unsigned long *dirty_bitmap); int xc_hvm_modified_memory( xc_interface *xch, domid_t domid, uint64_t first_pfn, uint32_t nr); int xc_hvm_set_mem_type( xc_interface *xch, domid_t domid, hvmmem_type_t type, uint64_t first_pfn, uint32_t nr); int xc_hvm_inject_trap( xc_interface *xch, domid_t domid, int vcpu, uint8_t vector, uint8_t type, uint32_t error_code, uint8_t insn_len, uint64_t cr2); #endif /* XC_WANT_COMPAT_DEVICEMODEL_API */ #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/include/xenctrl.h0000664000175000017500000027767513256712137016221 0ustar smbsmb/****************************************************************************** * xenctrl.h * * A library for low-level access to the Xen control interfaces. * * Copyright (c) 2003-2004, K A Fraser. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #ifndef XENCTRL_H #define XENCTRL_H /* Tell the Xen public headers we are a user-space tools build. */ #ifndef __XEN_TOOLS__ #define __XEN_TOOLS__ 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xentoollog.h" #if defined(__i386__) || defined(__x86_64__) #include #include #include #endif #define XC_PAGE_SHIFT 12 #define XC_PAGE_SIZE (1UL << XC_PAGE_SHIFT) #define XC_PAGE_MASK (~(XC_PAGE_SIZE-1)) #define INVALID_MFN (~0UL) /* * DEFINITIONS FOR CPU BARRIERS */ #define xen_barrier() asm volatile ( "" : : : "memory") #if defined(__i386__) #define xen_mb() asm volatile ( "lock; addl $0,0(%%esp)" : : : "memory" ) #define xen_rmb() xen_barrier() #define xen_wmb() xen_barrier() #elif defined(__x86_64__) #define xen_mb() asm volatile ( "mfence" : : : "memory") #define xen_rmb() xen_barrier() #define xen_wmb() xen_barrier() #elif defined(__arm__) #define xen_mb() asm volatile ("dmb" : : : "memory") #define xen_rmb() asm volatile ("dmb" : : : "memory") #define xen_wmb() asm volatile ("dmb" : : : "memory") #elif defined(__aarch64__) #define xen_mb() asm volatile ("dmb sy" : : : "memory") #define xen_rmb() asm volatile ("dmb sy" : : : "memory") #define xen_wmb() asm volatile ("dmb sy" : : : "memory") #else #error "Define barriers" #endif #define XENCTRL_HAS_XC_INTERFACE 1 /* In Xen 4.0 and earlier, xc_interface_open and xc_evtchn_open would * both return ints being the file descriptor. In 4.1 and later, they * return an xc_interface* and xc_evtchn*, respectively - ie, a * pointer to an opaque struct. This #define is provided in 4.1 and * later, allowing out-of-tree callers to more easily distinguish * between, and be compatible with, both versions. */ /* * GENERAL * * Unless otherwise specified, each function here returns zero or a * non-null pointer on success; or in case of failure, sets errno and * returns -1 or a null pointer. * * Unless otherwise specified, errors result in a call to the error * handler function, which by default prints a message to the * FILE* passed as the caller_data, which by default is stderr. * (This is described below as "logging errors".) * * The error handler can safely trash errno, as libxc saves it across * the callback. */ typedef struct xc_interface_core xc_interface; enum xc_error_code { XC_ERROR_NONE = 0, XC_INTERNAL_ERROR = 1, XC_INVALID_KERNEL = 2, XC_INVALID_PARAM = 3, XC_OUT_OF_MEMORY = 4, /* new codes need to be added to xc_error_level_to_desc too */ }; typedef enum xc_error_code xc_error_code; /* * INITIALIZATION FUNCTIONS */ /** * This function opens a handle to the hypervisor interface. This function can * be called multiple times within a single process. Multiple processes can * have an open hypervisor interface at the same time. * * Note: * After fork a child process must not use any opened xc interface * handle inherited from their parent. They must open a new handle if * they want to interact with xc. * * Each call to this function should have a corresponding call to * xc_interface_close(). * * This function can fail if the caller does not have superuser permission or * if a Xen-enabled kernel is not currently running. * * @return a handle to the hypervisor interface */ xc_interface *xc_interface_open(xentoollog_logger *logger, xentoollog_logger *dombuild_logger, unsigned open_flags); /* if logger==NULL, will log to stderr * if dombuild_logger=NULL, will log to a file */ /* * Note: if XC_OPENFLAG_NON_REENTRANT is passed then libxc must not be * called reentrantly and the calling application is responsible for * providing mutual exclusion surrounding all libxc calls itself. * * In particular xc_{get,clear}_last_error only remain valid for the * duration of the critical section containing the call which failed. */ enum xc_open_flags { XC_OPENFLAG_DUMMY = 1<<0, /* do not actually open a xenctrl interface */ XC_OPENFLAG_NON_REENTRANT = 1<<1, /* assume library is only every called from a single thread */ }; /** * This function closes an open hypervisor interface. * * This function can fail if the handle does not represent an open interface or * if there were problems closing the interface. In the latter case * the interface is still closed. * * @parm xch a handle to an open hypervisor interface * @return 0 on success, -1 otherwise. */ int xc_interface_close(xc_interface *xch); /* * HYPERCALL SAFE MEMORY BUFFER * * Ensure that memory which is passed to a hypercall has been * specially allocated in order to be safe to access from the * hypervisor. * * Each user data pointer is shadowed by an xc_hypercall_buffer data * structure. You should never define an xc_hypercall_buffer type * directly, instead use the DECLARE_HYPERCALL_BUFFER* macros below. * * The strucuture should be considered opaque and all access should be * via the macros and helper functions defined below. * * Once the buffer is declared the user is responsible for explicitly * allocating and releasing the memory using * xc_hypercall_buffer_alloc(_pages) and * xc_hypercall_buffer_free(_pages). * * Once the buffer has been allocated the user can initialise the data * via the normal pointer. The xc_hypercall_buffer structure is * transparently referenced by the helper macros (such as * xen_set_guest_handle) in order to check at compile time that the * correct type of memory is being used. */ struct xc_hypercall_buffer { /* Hypercall safe memory buffer. */ void *hbuf; /* * Reference to xc_hypercall_buffer passed as argument to the * current function. */ struct xc_hypercall_buffer *param_shadow; /* * Direction of copy for bounce buffering. */ int dir; /* Used iff dir != 0. */ void *ubuf; size_t sz; }; typedef struct xc_hypercall_buffer xc_hypercall_buffer_t; /* * Construct the name of the hypercall buffer for a given variable. * For internal use only */ #define XC__HYPERCALL_BUFFER_NAME(_name) xc__hypercall_buffer_##_name /* * Returns the hypercall_buffer associated with a variable. */ #define HYPERCALL_BUFFER(_name) \ ({ xc_hypercall_buffer_t _hcbuf_buf1; \ typeof(XC__HYPERCALL_BUFFER_NAME(_name)) *_hcbuf_buf2 = \ &XC__HYPERCALL_BUFFER_NAME(_name); \ (void)(&_hcbuf_buf1 == _hcbuf_buf2); \ (_hcbuf_buf2)->param_shadow ? \ (_hcbuf_buf2)->param_shadow : (_hcbuf_buf2); \ }) #define HYPERCALL_BUFFER_INIT_NO_BOUNCE .dir = 0, .sz = 0, .ubuf = (void *)-1 /* * Defines a hypercall buffer and user pointer with _name of _type. * * The user accesses the data as normal via _name which will be * transparently converted to the hypercall buffer as necessary. */ #define DECLARE_HYPERCALL_BUFFER(_type, _name) \ _type *(_name) = NULL; \ xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(_name) = { \ .hbuf = NULL, \ .param_shadow = NULL, \ HYPERCALL_BUFFER_INIT_NO_BOUNCE \ } /* * Like DECLARE_HYPERCALL_BUFFER() but using an already allocated * hypercall buffer, _hbuf. * * Useful when a hypercall buffer is passed to a function and access * via the user pointer is required. * * See DECLARE_HYPERCALL_BUFFER_ARGUMENT() if the user pointer is not * required. */ #define DECLARE_HYPERCALL_BUFFER_SHADOW(_type, _name, _hbuf) \ _type *(_name) = (_hbuf)->hbuf; \ __attribute__((unused)) \ xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(_name) = { \ .hbuf = (void *)-1, \ .param_shadow = (_hbuf), \ HYPERCALL_BUFFER_INIT_NO_BOUNCE \ } /* * Declare the necessary data structure to allow a hypercall buffer * passed as an argument to a function to be used in the normal way. */ #define DECLARE_HYPERCALL_BUFFER_ARGUMENT(_name) \ xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(_name) = { \ .hbuf = (void *)-1, \ .param_shadow = (_name), \ HYPERCALL_BUFFER_INIT_NO_BOUNCE \ } /* * Get the hypercall buffer data pointer in a form suitable for use * directly as a hypercall argument. */ #define HYPERCALL_BUFFER_AS_ARG(_name) \ ({ xc_hypercall_buffer_t _hcbuf_arg1; \ typeof(XC__HYPERCALL_BUFFER_NAME(_name)) *_hcbuf_arg2 = \ HYPERCALL_BUFFER(_name); \ (void)(&_hcbuf_arg1 == _hcbuf_arg2); \ (unsigned long)(_hcbuf_arg2)->hbuf; \ }) /* * Set a xen_guest_handle in a type safe manner, ensuring that the * data pointer has been correctly allocated. */ #define set_xen_guest_handle_impl(_hnd, _val, _byte_off) \ do { \ xc_hypercall_buffer_t _hcbuf_hnd1; \ typeof(XC__HYPERCALL_BUFFER_NAME(_val)) *_hcbuf_hnd2 = \ HYPERCALL_BUFFER(_val); \ (void) (&_hcbuf_hnd1 == _hcbuf_hnd2); \ set_xen_guest_handle_raw(_hnd, \ (_hcbuf_hnd2)->hbuf + (_byte_off)); \ } while (0) #undef set_xen_guest_handle #define set_xen_guest_handle(_hnd, _val) \ set_xen_guest_handle_impl(_hnd, _val, 0) #define set_xen_guest_handle_offset(_hnd, _val, _off) \ set_xen_guest_handle_impl(_hnd, _val, \ ((sizeof(*_val)*(_off)))) /* Use with set_xen_guest_handle in place of NULL */ extern xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(HYPERCALL_BUFFER_NULL); /* * Allocate and free hypercall buffers with byte granularity. */ void *xc__hypercall_buffer_alloc(xc_interface *xch, xc_hypercall_buffer_t *b, size_t size); #define xc_hypercall_buffer_alloc(_xch, _name, _size) xc__hypercall_buffer_alloc(_xch, HYPERCALL_BUFFER(_name), _size) void xc__hypercall_buffer_free(xc_interface *xch, xc_hypercall_buffer_t *b); #define xc_hypercall_buffer_free(_xch, _name) xc__hypercall_buffer_free(_xch, HYPERCALL_BUFFER(_name)) /* * Allocate and free hypercall buffers with page alignment. */ void *xc__hypercall_buffer_alloc_pages(xc_interface *xch, xc_hypercall_buffer_t *b, int nr_pages); #define xc_hypercall_buffer_alloc_pages(_xch, _name, _nr) xc__hypercall_buffer_alloc_pages(_xch, HYPERCALL_BUFFER(_name), _nr) void xc__hypercall_buffer_free_pages(xc_interface *xch, xc_hypercall_buffer_t *b, int nr_pages); #define xc_hypercall_buffer_free_pages(_xch, _name, _nr) \ do { \ if ( _name ) \ xc__hypercall_buffer_free_pages(_xch, HYPERCALL_BUFFER(_name), \ _nr); \ } while (0) /* * Array of hypercall buffers. * * Create an array with xc_hypercall_buffer_array_create() and * populate it by declaring one hypercall buffer in a loop and * allocating the buffer with xc_hypercall_buffer_array_alloc(). * * To access a previously allocated buffers, declare a new hypercall * buffer and call xc_hypercall_buffer_array_get(). * * Destroy the array with xc_hypercall_buffer_array_destroy() to free * the array and all its allocated hypercall buffers. */ struct xc_hypercall_buffer_array; typedef struct xc_hypercall_buffer_array xc_hypercall_buffer_array_t; xc_hypercall_buffer_array_t *xc_hypercall_buffer_array_create(xc_interface *xch, unsigned n); void *xc__hypercall_buffer_array_alloc(xc_interface *xch, xc_hypercall_buffer_array_t *array, unsigned index, xc_hypercall_buffer_t *hbuf, size_t size); #define xc_hypercall_buffer_array_alloc(_xch, _array, _index, _name, _size) \ xc__hypercall_buffer_array_alloc(_xch, _array, _index, HYPERCALL_BUFFER(_name), _size) void *xc__hypercall_buffer_array_get(xc_interface *xch, xc_hypercall_buffer_array_t *array, unsigned index, xc_hypercall_buffer_t *hbuf); #define xc_hypercall_buffer_array_get(_xch, _array, _index, _name, _size) \ xc__hypercall_buffer_array_get(_xch, _array, _index, HYPERCALL_BUFFER(_name)) void xc_hypercall_buffer_array_destroy(xc_interface *xc, xc_hypercall_buffer_array_t *array); /* * CPUMAP handling */ typedef uint8_t *xc_cpumap_t; /* return maximum number of cpus the hypervisor supports */ int xc_get_max_cpus(xc_interface *xch); /* return the number of online cpus */ int xc_get_online_cpus(xc_interface *xch); /* return array size for cpumap */ int xc_get_cpumap_size(xc_interface *xch); /* allocate a cpumap */ xc_cpumap_t xc_cpumap_alloc(xc_interface *xch); /* clear an CPU from the cpumap. */ void xc_cpumap_clearcpu(int cpu, xc_cpumap_t map); /* set an CPU in the cpumap. */ void xc_cpumap_setcpu(int cpu, xc_cpumap_t map); /* Test whether the CPU in cpumap is set. */ int xc_cpumap_testcpu(int cpu, xc_cpumap_t map); /* * NODEMAP handling */ typedef uint8_t *xc_nodemap_t; /* return maximum number of NUMA nodes the hypervisor supports */ int xc_get_max_nodes(xc_interface *xch); /* return array size for nodemap */ int xc_get_nodemap_size(xc_interface *xch); /* allocate a nodemap */ xc_nodemap_t xc_nodemap_alloc(xc_interface *xch); /* * DOMAIN DEBUGGING FUNCTIONS */ typedef struct xc_core_header { unsigned int xch_magic; unsigned int xch_nr_vcpus; unsigned int xch_nr_pages; unsigned int xch_ctxt_offset; unsigned int xch_index_offset; unsigned int xch_pages_offset; } xc_core_header_t; #define XC_CORE_MAGIC 0xF00FEBED #define XC_CORE_MAGIC_HVM 0xF00FEBEE /* * DOMAIN MANAGEMENT FUNCTIONS */ typedef struct xc_dominfo { uint32_t domid; uint32_t ssidref; unsigned int dying:1, crashed:1, shutdown:1, paused:1, blocked:1, running:1, hvm:1, debugged:1, xenstore:1, hap:1; unsigned int shutdown_reason; /* only meaningful if shutdown==1 */ unsigned long nr_pages; /* current number, not maximum */ unsigned long nr_outstanding_pages; unsigned long nr_shared_pages; unsigned long nr_paged_pages; unsigned long shared_info_frame; uint64_t cpu_time; unsigned long max_memkb; unsigned int nr_online_vcpus; unsigned int max_vcpu_id; xen_domain_handle_t handle; unsigned int cpupool; } xc_dominfo_t; typedef xen_domctl_getdomaininfo_t xc_domaininfo_t; typedef union { #if defined(__i386__) || defined(__x86_64__) vcpu_guest_context_x86_64_t x64; vcpu_guest_context_x86_32_t x32; #endif vcpu_guest_context_t c; } vcpu_guest_context_any_t; typedef union { #if defined(__i386__) || defined(__x86_64__) shared_info_x86_64_t x64; shared_info_x86_32_t x32; #endif shared_info_t s; } shared_info_any_t; #if defined(__i386__) || defined(__x86_64__) typedef union { start_info_x86_64_t x64; start_info_x86_32_t x32; start_info_t s; } start_info_any_t; #endif typedef struct xc_vcpu_extstate { uint64_t xfeature_mask; uint64_t size; void *buffer; } xc_vcpu_extstate_t; typedef struct xen_arch_domainconfig xc_domain_configuration_t; int xc_domain_create(xc_interface *xch, uint32_t ssidref, xen_domain_handle_t handle, uint32_t flags, uint32_t *pdomid, xc_domain_configuration_t *config); /* Functions to produce a dump of a given domain * xc_domain_dumpcore - produces a dump to a specified file * xc_domain_dumpcore_via_callback - produces a dump, using a specified * callback function */ int xc_domain_dumpcore(xc_interface *xch, uint32_t domid, const char *corename); /* Define the callback function type for xc_domain_dumpcore_via_callback. * * This function is called by the coredump code for every "write", * and passes an opaque object for the use of the function and * created by the caller of xc_domain_dumpcore_via_callback. */ typedef int (dumpcore_rtn_t)(xc_interface *xch, void *arg, char *buffer, unsigned int length); int xc_domain_dumpcore_via_callback(xc_interface *xch, uint32_t domid, void *arg, dumpcore_rtn_t dump_rtn); /* * This function sets the maximum number of vcpus that a domain may create. * * @parm xch a handle to an open hypervisor interface. * @parm domid the domain id in which vcpus are to be created. * @parm max the maximum number of vcpus that the domain may create. * @return 0 on success, -1 on failure. */ int xc_domain_max_vcpus(xc_interface *xch, uint32_t domid, unsigned int max); /** * This function pauses a domain. A paused domain still exists in memory * however it does not receive any timeslices from the hypervisor. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id to pause * @return 0 on success, -1 on failure. */ int xc_domain_pause(xc_interface *xch, uint32_t domid); /** * This function unpauses a domain. The domain should have been previously * paused. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id to unpause * return 0 on success, -1 on failure */ int xc_domain_unpause(xc_interface *xch, uint32_t domid); /** * This function will destroy a domain. Destroying a domain removes the domain * completely from memory. This function should be called after sending the * domain a SHUTDOWN control message to free up the domain resources. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id to destroy * @return 0 on success, -1 on failure */ int xc_domain_destroy(xc_interface *xch, uint32_t domid); /** * This function resumes a suspended domain. The domain should have * been previously suspended. * * Note that there are 'xc_domain_suspend' as suspending a domain * is quite the endeavour. * * For the purpose of this explanation there are three guests: * PV (using hypercalls for privilgied operations), HVM * (fully hardware virtualized guests using emulated devices for everything), * and PVHVM (PV aware with hardware virtualisation). * * HVM guest are the simplest - they suspend via S3 / S4 and resume from * S3 / S4. Upon resume they have to re-negotiate with the emulated devices. * * PV and PVHVM communicate via hypercalls for suspend (and resume). * For suspend the toolstack initiates the process by writing an value * in XenBus "control/shutdown" with the string "suspend". * * The PV guest stashes anything it deems neccessary in 'struct * start_info' in case of failure (PVHVM may ignore this) and calls * the SCHEDOP_shutdown::SHUTDOWN_suspend hypercall (for PV as * argument it passes the MFN to 'struct start_info'). * * And then the guest is suspended. * * The checkpointing or notifying a guest that the suspend failed or * cancelled (in case of checkpoint) is by having the * SCHEDOP_shutdown::SHUTDOWN_suspend hypercall return a non-zero * value. * * The PV and PVHVM resume path are similar. For PV it would be * similar to bootup - figure out where the 'struct start_info' is (or * if the suspend was cancelled aka checkpointed - reuse the saved * values). * * From here on they differ depending whether the guest is PV or PVHVM * in specifics but follow overall the same path: * - PV: Bringing up the vCPUS, * - PVHVM: Setup vector callback, * - Bring up vCPU runstates, * - Remap the grant tables if checkpointing or setup from scratch, * * * If the resume was not checkpointing (or if suspend was succesful) we would * setup the PV timers and the different PV events. Lastly the PV drivers * re-negotiate with the backend. * * This function would return before the guest started resuming. That is * the guest would be in non-running state and its vCPU context would be * in the the SCHEDOP_shutdown::SHUTDOWN_suspend hypercall return path * (for PV and PVHVM). For HVM it would be in would be in QEMU emulated * BIOS handling S3 suspend. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id to resume * @parm fast use cooperative resume (guest must support this) * return 0 on success, -1 on failure */ int xc_domain_resume(xc_interface *xch, uint32_t domid, int fast); /** * This function will shutdown a domain. This is intended for use in * fully-virtualized domains where this operation is analogous to the * sched_op operations in a paravirtualized domain. The caller is * expected to give the reason for the shutdown. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id to destroy * @parm reason is the reason (SHUTDOWN_xxx) for the shutdown * @return 0 on success, -1 on failure */ int xc_domain_shutdown(xc_interface *xch, uint32_t domid, int reason); int xc_watchdog(xc_interface *xch, uint32_t id, uint32_t timeout); /** * This function explicitly sets the host NUMA nodes the domain will * have affinity with. * * @parm xch a handle to an open hypervisor interface. * @parm domid the domain id one wants to set the affinity of. * @parm nodemap the map of the affine nodes. * @return 0 on success, -1 on failure. */ int xc_domain_node_setaffinity(xc_interface *xch, uint32_t domind, xc_nodemap_t nodemap); /** * This function retrieves the host NUMA nodes the domain has * affinity with. * * @parm xch a handle to an open hypervisor interface. * @parm domid the domain id one wants to get the node affinity of. * @parm nodemap the map of the affine nodes. * @return 0 on success, -1 on failure. */ int xc_domain_node_getaffinity(xc_interface *xch, uint32_t domind, xc_nodemap_t nodemap); /** * This function specifies the CPU affinity for a vcpu. * * There are two kinds of affinity. Soft affinity is on what CPUs a vcpu * prefers to run. Hard affinity is on what CPUs a vcpu is allowed to run. * If flags contains XEN_VCPUAFFINITY_SOFT, the soft affinity it is set to * what cpumap_soft_inout contains. If flags contains XEN_VCPUAFFINITY_HARD, * the hard affinity is set to what cpumap_hard_inout contains. Both flags * can be set at the same time, in which case both soft and hard affinity are * set to what the respective parameter contains. * * The function also returns the effective hard or/and soft affinity, still * via the cpumap_soft_inout and cpumap_hard_inout parameters. Effective * affinity is, in case of soft affinity, the intersection of soft affinity, * hard affinity and the cpupool's online CPUs for the domain, and is returned * in cpumap_soft_inout, if XEN_VCPUAFFINITY_SOFT is set in flags. In case of * hard affinity, it is the intersection between hard affinity and the * cpupool's online CPUs, and is returned in cpumap_hard_inout, if * XEN_VCPUAFFINITY_HARD is set in flags. If both flags are set, both soft * and hard affinity are returned in the respective parameter. * * We do report it back as effective affinity is what the Xen scheduler will * actually use, and we thus allow checking whether or not that matches with, * or at least is good enough for, the caller's purposes. * * @param xch a handle to an open hypervisor interface. * @param domid the id of the domain to which the vcpu belongs * @param vcpu the vcpu id wihin the domain * @param cpumap_hard_inout specifies(/returns) the (effective) hard affinity * @param cpumap_soft_inout specifies(/returns) the (effective) soft affinity * @param flags what we want to set */ int xc_vcpu_setaffinity(xc_interface *xch, uint32_t domid, int vcpu, xc_cpumap_t cpumap_hard_inout, xc_cpumap_t cpumap_soft_inout, uint32_t flags); /** * This function retrieves hard and soft CPU affinity of a vcpu, * depending on what flags are set. * * Soft affinity is returned in cpumap_soft if XEN_VCPUAFFINITY_SOFT is set. * Hard affinity is returned in cpumap_hard if XEN_VCPUAFFINITY_HARD is set. * * @param xch a handle to an open hypervisor interface. * @param domid the id of the domain to which the vcpu belongs * @param vcpu the vcpu id wihin the domain * @param cpumap_hard is where hard affinity is returned * @param cpumap_soft is where soft affinity is returned * @param flags what we want get */ int xc_vcpu_getaffinity(xc_interface *xch, uint32_t domid, int vcpu, xc_cpumap_t cpumap_hard, xc_cpumap_t cpumap_soft, uint32_t flags); /** * This function will return the guest_width (in bytes) for the * specified domain. * * @param xch a handle to an open hypervisor interface. * @param domid the domain id one wants the address size width of. * @param addr_size the address size. */ int xc_domain_get_guest_width(xc_interface *xch, uint32_t domid, unsigned int *guest_width); /** * This function will return information about one or more domains. It is * designed to iterate over the list of domains. If a single domain is * requested, this function will return the next domain in the list - if * one exists. It is, therefore, important in this case to make sure the * domain requested was the one returned. * * @parm xch a handle to an open hypervisor interface * @parm first_domid the first domain to enumerate information from. Domains * are currently enumerate in order of creation. * @parm max_doms the number of elements in info * @parm info an array of max_doms size that will contain the information for * the enumerated domains. * @return the number of domains enumerated or -1 on error */ int xc_domain_getinfo(xc_interface *xch, uint32_t first_domid, unsigned int max_doms, xc_dominfo_t *info); /** * This function will set the execution context for the specified vcpu. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain to set the vcpu context for * @parm vcpu the vcpu number for the context * @parm ctxt pointer to the the cpu context with the values to set * @return the number of domains enumerated or -1 on error */ int xc_vcpu_setcontext(xc_interface *xch, uint32_t domid, uint32_t vcpu, vcpu_guest_context_any_t *ctxt); /** * This function will return information about one or more domains, using a * single hypercall. The domain information will be stored into the supplied * array of xc_domaininfo_t structures. * * @parm xch a handle to an open hypervisor interface * @parm first_domain the first domain to enumerate information from. * Domains are currently enumerate in order of creation. * @parm max_domains the number of elements in info * @parm info an array of max_doms size that will contain the information for * the enumerated domains. * @return the number of domains enumerated or -1 on error */ int xc_domain_getinfolist(xc_interface *xch, uint32_t first_domain, unsigned int max_domains, xc_domaininfo_t *info); /** * This function set p2m for broken page * &parm xch a handle to an open hypervisor interface * @parm domid the domain id which broken page belong to * @parm pfn the pfn number of the broken page * @return 0 on success, -1 on failure */ int xc_set_broken_page_p2m(xc_interface *xch, uint32_t domid, unsigned long pfn); /** * This function returns information about the context of a hvm domain * @parm xch a handle to an open hypervisor interface * @parm domid the domain to get information from * @parm ctxt_buf a pointer to a structure to store the execution context of * the hvm domain * @parm size the size of ctxt_buf in bytes * @return 0 on success, -1 on failure */ int xc_domain_hvm_getcontext(xc_interface *xch, uint32_t domid, uint8_t *ctxt_buf, uint32_t size); /** * This function returns one element of the context of a hvm domain * @parm xch a handle to an open hypervisor interface * @parm domid the domain to get information from * @parm typecode which type of elemnt required * @parm instance which instance of the type * @parm ctxt_buf a pointer to a structure to store the execution context of * the hvm domain * @parm size the size of ctxt_buf (must be >= HVM_SAVE_LENGTH(typecode)) * @return 0 on success, -1 on failure */ int xc_domain_hvm_getcontext_partial(xc_interface *xch, uint32_t domid, uint16_t typecode, uint16_t instance, void *ctxt_buf, uint32_t size); /** * This function will set the context for hvm domain * * @parm xch a handle to an open hypervisor interface * @parm domid the domain to set the hvm domain context for * @parm hvm_ctxt pointer to the the hvm context with the values to set * @parm size the size of hvm_ctxt in bytes * @return 0 on success, -1 on failure */ int xc_domain_hvm_setcontext(xc_interface *xch, uint32_t domid, uint8_t *hvm_ctxt, uint32_t size); /** * This function will return guest IO ABI protocol * * @parm xch a handle to an open hypervisor interface * @parm domid the domain to get IO ABI protocol for * @return guest protocol on success, NULL on failure */ const char *xc_domain_get_native_protocol(xc_interface *xch, uint32_t domid); /** * This function returns information about the execution context of a * particular vcpu of a domain. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain to get information from * @parm vcpu the vcpu number * @parm ctxt a pointer to a structure to store the execution context of the * domain * @return 0 on success, -1 on failure */ int xc_vcpu_getcontext(xc_interface *xch, uint32_t domid, uint32_t vcpu, vcpu_guest_context_any_t *ctxt); /** * This function returns information about the XSAVE state of a particular * vcpu of a domain. If extstate->size and extstate->xfeature_mask are 0, * the call is considered a query to retrieve them and the buffer is not * filled. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain to get information from * @parm vcpu the vcpu number * @parm extstate a pointer to a structure to store the XSAVE state of the * domain * @return 0 on success, negative error code on failure */ int xc_vcpu_get_extstate(xc_interface *xch, uint32_t domid, uint32_t vcpu, xc_vcpu_extstate_t *extstate); typedef xen_domctl_getvcpuinfo_t xc_vcpuinfo_t; int xc_vcpu_getinfo(xc_interface *xch, uint32_t domid, uint32_t vcpu, xc_vcpuinfo_t *info); long long xc_domain_get_cpu_usage(xc_interface *xch, domid_t domid, int vcpu); int xc_domain_sethandle(xc_interface *xch, uint32_t domid, xen_domain_handle_t handle); typedef xen_domctl_shadow_op_stats_t xc_shadow_op_stats_t; int xc_shadow_control(xc_interface *xch, uint32_t domid, unsigned int sop, xc_hypercall_buffer_t *dirty_bitmap, unsigned long pages, unsigned long *mb, uint32_t mode, xc_shadow_op_stats_t *stats); int xc_sched_credit_domain_set(xc_interface *xch, uint32_t domid, struct xen_domctl_sched_credit *sdom); int xc_sched_credit_domain_get(xc_interface *xch, uint32_t domid, struct xen_domctl_sched_credit *sdom); int xc_sched_credit_params_set(xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_credit_schedule *schedule); int xc_sched_credit_params_get(xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_credit_schedule *schedule); int xc_sched_credit2_params_set(xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_credit2_schedule *schedule); int xc_sched_credit2_params_get(xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_credit2_schedule *schedule); int xc_sched_credit2_domain_set(xc_interface *xch, uint32_t domid, struct xen_domctl_sched_credit2 *sdom); int xc_sched_credit2_domain_get(xc_interface *xch, uint32_t domid, struct xen_domctl_sched_credit2 *sdom); int xc_sched_rtds_domain_set(xc_interface *xch, uint32_t domid, struct xen_domctl_sched_rtds *sdom); int xc_sched_rtds_domain_get(xc_interface *xch, uint32_t domid, struct xen_domctl_sched_rtds *sdom); int xc_sched_rtds_vcpu_set(xc_interface *xch, uint32_t domid, struct xen_domctl_schedparam_vcpu *vcpus, uint32_t num_vcpus); int xc_sched_rtds_vcpu_get(xc_interface *xch, uint32_t domid, struct xen_domctl_schedparam_vcpu *vcpus, uint32_t num_vcpus); int xc_sched_arinc653_schedule_set( xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_arinc653_schedule *schedule); int xc_sched_arinc653_schedule_get( xc_interface *xch, uint32_t cpupool_id, struct xen_sysctl_arinc653_schedule *schedule); /** * This function sends a trigger to a domain. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id to send trigger * @parm trigger the trigger type * @parm vcpu the vcpu number to send trigger * return 0 on success, -1 on failure */ int xc_domain_send_trigger(xc_interface *xch, uint32_t domid, uint32_t trigger, uint32_t vcpu); /** * This function enables or disable debugging of a domain. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id to send trigger * @parm enable true to enable debugging * return 0 on success, -1 on failure */ int xc_domain_setdebugging(xc_interface *xch, uint32_t domid, unsigned int enable); /** * This function audits the (top level) p2m of a domain * and returns the different error counts, if any. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id whose top level p2m we * want to audit * @parm orphans count of m2p entries for valid * domain pages containing an invalid value * @parm m2p_bad count of m2p entries mismatching the * associated p2m entry for this domain * @parm p2m_bad count of p2m entries for this domain * mismatching the associated m2p entry * return 0 on success, -1 on failure * errno values on failure include: * -ENOSYS: not implemented * -EFAULT: could not copy results back to guest */ int xc_domain_p2m_audit(xc_interface *xch, uint32_t domid, uint64_t *orphans, uint64_t *m2p_bad, uint64_t *p2m_bad); /** * This function sets or clears the requirement that an access memory * event listener is required on the domain. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id to send trigger * @parm enable true to require a listener * return 0 on success, -1 on failure */ int xc_domain_set_access_required(xc_interface *xch, uint32_t domid, unsigned int required); /** * This function sets the handler of global VIRQs sent by the hypervisor * * @parm xch a handle to an open hypervisor interface * @parm domid the domain id which will handle the VIRQ * @parm virq the virq number (VIRQ_*) * return 0 on success, -1 on failure */ int xc_domain_set_virq_handler(xc_interface *xch, uint32_t domid, int virq); /** * Set the maximum event channel port a domain may bind. * * This does not affect ports that are already bound. * * @param xch a handle to an open hypervisor interface * @param domid the domain id * @param max_port maximum port number */ int xc_domain_set_max_evtchn(xc_interface *xch, uint32_t domid, uint32_t max_port); /* * CPUPOOL MANAGEMENT FUNCTIONS */ typedef struct xc_cpupoolinfo { uint32_t cpupool_id; uint32_t sched_id; uint32_t n_dom; xc_cpumap_t cpumap; } xc_cpupoolinfo_t; #define XC_CPUPOOL_POOLID_ANY 0xFFFFFFFF /** * Create a new cpupool. * * @parm xc_handle a handle to an open hypervisor interface * @parm ppoolid pointer to the new cpupool id (in/out) * @parm sched_id id of scheduler to use for pool * return 0 on success, -1 on failure */ int xc_cpupool_create(xc_interface *xch, uint32_t *ppoolid, uint32_t sched_id); /** * Destroy a cpupool. Pool must be unused and have no cpu assigned. * * @parm xc_handle a handle to an open hypervisor interface * @parm poolid id of the cpupool to destroy * return 0 on success, -1 on failure */ int xc_cpupool_destroy(xc_interface *xch, uint32_t poolid); /** * Get cpupool info. Returns info for up to the specified number of cpupools * starting at the given id. * @parm xc_handle a handle to an open hypervisor interface * @parm poolid lowest id for which info is returned * return cpupool info ptr (to be freed via xc_cpupool_infofree) */ xc_cpupoolinfo_t *xc_cpupool_getinfo(xc_interface *xch, uint32_t poolid); /** * Free cpupool info. Used to free info obtained via xc_cpupool_getinfo. * @parm xc_handle a handle to an open hypervisor interface * @parm info area to free */ void xc_cpupool_infofree(xc_interface *xch, xc_cpupoolinfo_t *info); /** * Add cpu to a cpupool. cpu may be -1 indicating the first unassigned. * * @parm xc_handle a handle to an open hypervisor interface * @parm poolid id of the cpupool * @parm cpu cpu number to add * return 0 on success, -1 on failure */ int xc_cpupool_addcpu(xc_interface *xch, uint32_t poolid, int cpu); /** * Remove cpu from cpupool. cpu may be -1 indicating the last cpu of the pool. * * @parm xc_handle a handle to an open hypervisor interface * @parm poolid id of the cpupool * @parm cpu cpu number to remove * return 0 on success, -1 on failure */ int xc_cpupool_removecpu(xc_interface *xch, uint32_t poolid, int cpu); /** * Move domain to another cpupool. * * @parm xc_handle a handle to an open hypervisor interface * @parm poolid id of the destination cpupool * @parm domid id of the domain to move * return 0 on success, -1 on failure */ int xc_cpupool_movedomain(xc_interface *xch, uint32_t poolid, uint32_t domid); /** * Return map of cpus not in any cpupool. * * @parm xc_handle a handle to an open hypervisor interface * return cpumap array on success, NULL else */ xc_cpumap_t xc_cpupool_freeinfo(xc_interface *xch); /* * EVENT CHANNEL FUNCTIONS * * None of these do any logging. */ /* A port identifier is guaranteed to fit in 31 bits. */ typedef int xc_evtchn_port_or_error_t; /** * This function allocates an unbound port. Ports are named endpoints used for * interdomain communication. This function is most useful in opening a * well-known port within a domain to receive events on. * * NOTE: If you are allocating a *local* unbound port, you probably want to * use xc_evtchn_bind_unbound_port(). This function is intended for allocating * ports *only* during domain creation. * * @parm xch a handle to an open hypervisor interface * @parm dom the ID of the local domain (the 'allocatee') * @parm remote_dom the ID of the domain who will later bind * @return allocated port (in @dom) on success, -1 on failure */ xc_evtchn_port_or_error_t xc_evtchn_alloc_unbound(xc_interface *xch, uint32_t dom, uint32_t remote_dom); int xc_evtchn_reset(xc_interface *xch, uint32_t dom); typedef struct evtchn_status xc_evtchn_status_t; int xc_evtchn_status(xc_interface *xch, xc_evtchn_status_t *status); int xc_physdev_pci_access_modify(xc_interface *xch, uint32_t domid, int bus, int dev, int func, int enable); int xc_readconsolering(xc_interface *xch, char *buffer, unsigned int *pnr_chars, int clear, int incremental, uint32_t *pindex); int xc_send_debug_keys(xc_interface *xch, char *keys); typedef xen_sysctl_physinfo_t xc_physinfo_t; typedef xen_sysctl_cputopo_t xc_cputopo_t; typedef xen_sysctl_numainfo_t xc_numainfo_t; typedef xen_sysctl_meminfo_t xc_meminfo_t; typedef xen_sysctl_pcitopoinfo_t xc_pcitopoinfo_t; typedef uint32_t xc_cpu_to_node_t; typedef uint32_t xc_cpu_to_socket_t; typedef uint32_t xc_cpu_to_core_t; typedef uint64_t xc_node_to_memsize_t; typedef uint64_t xc_node_to_memfree_t; typedef uint32_t xc_node_to_node_dist_t; int xc_physinfo(xc_interface *xch, xc_physinfo_t *info); int xc_cputopoinfo(xc_interface *xch, unsigned *max_cpus, xc_cputopo_t *cputopo); int xc_numainfo(xc_interface *xch, unsigned *max_nodes, xc_meminfo_t *meminfo, uint32_t *distance); int xc_pcitopoinfo(xc_interface *xch, unsigned num_devs, physdev_pci_device_t *devs, uint32_t *nodes); int xc_sched_id(xc_interface *xch, int *sched_id); int xc_machphys_mfn_list(xc_interface *xch, unsigned long max_extents, xen_pfn_t *extent_start); typedef xen_sysctl_cpuinfo_t xc_cpuinfo_t; int xc_getcpuinfo(xc_interface *xch, int max_cpus, xc_cpuinfo_t *info, int *nr_cpus); int xc_domain_setmaxmem(xc_interface *xch, uint32_t domid, uint64_t max_memkb); int xc_domain_set_memmap_limit(xc_interface *xch, uint32_t domid, unsigned long map_limitkb); int xc_domain_setvnuma(xc_interface *xch, uint32_t domid, uint32_t nr_vnodes, uint32_t nr_regions, uint32_t nr_vcpus, xen_vmemrange_t *vmemrange, unsigned int *vdistance, unsigned int *vcpu_to_vnode, unsigned int *vnode_to_pnode); /* * Retrieve vnuma configuration * domid: IN, target domid * nr_vnodes: IN/OUT, number of vnodes, not NULL * nr_vmemranges: IN/OUT, number of vmemranges, not NULL * nr_vcpus: IN/OUT, number of vcpus, not NULL * vmemranges: OUT, an array which has length of nr_vmemranges * vdistance: OUT, an array which has length of nr_vnodes * nr_vnodes * vcpu_to_vnode: OUT, an array which has length of nr_vcpus */ int xc_domain_getvnuma(xc_interface *xch, uint32_t domid, uint32_t *nr_vnodes, uint32_t *nr_vmemranges, uint32_t *nr_vcpus, xen_vmemrange_t *vmemrange, unsigned int *vdistance, unsigned int *vcpu_to_vnode); int xc_domain_soft_reset(xc_interface *xch, uint32_t domid); #if defined(__i386__) || defined(__x86_64__) /* * PC BIOS standard E820 types and structure. */ #define E820_RAM 1 #define E820_RESERVED 2 #define E820_ACPI 3 #define E820_NVS 4 #define E820_UNUSABLE 5 #define E820MAX (128) struct e820entry { uint64_t addr; uint64_t size; uint32_t type; } __attribute__((packed)); int xc_domain_set_memory_map(xc_interface *xch, uint32_t domid, struct e820entry entries[], uint32_t nr_entries); int xc_get_machine_memory_map(xc_interface *xch, struct e820entry entries[], uint32_t max_entries); #endif int xc_reserved_device_memory_map(xc_interface *xch, uint32_t flags, uint16_t seg, uint8_t bus, uint8_t devfn, struct xen_reserved_device_memory entries[], uint32_t *max_entries); int xc_domain_set_time_offset(xc_interface *xch, uint32_t domid, int32_t time_offset_seconds); int xc_domain_set_tsc_info(xc_interface *xch, uint32_t domid, uint32_t tsc_mode, uint64_t elapsed_nsec, uint32_t gtsc_khz, uint32_t incarnation); int xc_domain_get_tsc_info(xc_interface *xch, uint32_t domid, uint32_t *tsc_mode, uint64_t *elapsed_nsec, uint32_t *gtsc_khz, uint32_t *incarnation); int xc_domain_disable_migrate(xc_interface *xch, uint32_t domid); int xc_domain_maximum_gpfn(xc_interface *xch, domid_t domid, xen_pfn_t *gpfns); int xc_domain_nr_gpfns(xc_interface *xch, domid_t domid, xen_pfn_t *gpfns); int xc_domain_increase_reservation(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start); int xc_domain_increase_reservation_exact(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start); int xc_domain_decrease_reservation(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, xen_pfn_t *extent_start); int xc_domain_decrease_reservation_exact(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, xen_pfn_t *extent_start); int xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid, unsigned int space, unsigned long idx, xen_pfn_t gpfn); int xc_domain_populate_physmap(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start); int xc_domain_populate_physmap_exact(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start); int xc_domain_claim_pages(xc_interface *xch, uint32_t domid, unsigned long nr_pages); int xc_domain_memory_exchange_pages(xc_interface *xch, int domid, unsigned long nr_in_extents, unsigned int in_order, xen_pfn_t *in_extents, unsigned long nr_out_extents, unsigned int out_order, xen_pfn_t *out_extents); int xc_domain_set_pod_target(xc_interface *xch, uint32_t domid, uint64_t target_pages, uint64_t *tot_pages, uint64_t *pod_cache_pages, uint64_t *pod_entries); int xc_domain_get_pod_target(xc_interface *xch, uint32_t domid, uint64_t *tot_pages, uint64_t *pod_cache_pages, uint64_t *pod_entries); int xc_domain_ioport_permission(xc_interface *xch, uint32_t domid, uint32_t first_port, uint32_t nr_ports, uint32_t allow_access); int xc_domain_irq_permission(xc_interface *xch, uint32_t domid, uint8_t pirq, uint8_t allow_access); int xc_domain_iomem_permission(xc_interface *xch, uint32_t domid, unsigned long first_mfn, unsigned long nr_mfns, uint8_t allow_access); int xc_domain_pin_memory_cacheattr(xc_interface *xch, uint32_t domid, uint64_t start, uint64_t end, uint32_t type); unsigned long xc_make_page_below_4G(xc_interface *xch, uint32_t domid, unsigned long mfn); typedef xen_sysctl_perfc_desc_t xc_perfc_desc_t; typedef xen_sysctl_perfc_val_t xc_perfc_val_t; int xc_perfc_reset(xc_interface *xch); int xc_perfc_query_number(xc_interface *xch, int *nbr_desc, int *nbr_val); int xc_perfc_query(xc_interface *xch, xc_hypercall_buffer_t *desc, xc_hypercall_buffer_t *val); typedef xen_sysctl_lockprof_data_t xc_lockprof_data_t; int xc_lockprof_reset(xc_interface *xch); int xc_lockprof_query_number(xc_interface *xch, uint32_t *n_elems); int xc_lockprof_query(xc_interface *xch, uint32_t *n_elems, uint64_t *time, xc_hypercall_buffer_t *data); void *xc_memalign(xc_interface *xch, size_t alignment, size_t size); /** * Avoid using this function, as it does not work for all cases (such * as 4M superpages, or guests using PSE36). Only used for debugging. * * Translates a virtual address in the context of a given domain and * vcpu returning the GFN containing the address (that is, an MFN for * PV guests, a PFN for HVM guests). Returns 0 for failure. * * @parm xch a handle on an open hypervisor interface * @parm dom the domain to perform the translation in * @parm vcpu the vcpu to perform the translation on * @parm virt the virtual address to translate */ unsigned long xc_translate_foreign_address(xc_interface *xch, uint32_t dom, int vcpu, unsigned long long virt); /** * DEPRECATED. Avoid using this, as it does not correctly account for PFNs * without a backing MFN. */ int xc_get_pfn_list(xc_interface *xch, uint32_t domid, uint64_t *pfn_buf, unsigned long max_pfns); int xc_copy_to_domain_page(xc_interface *xch, uint32_t domid, unsigned long dst_pfn, const char *src_page); int xc_clear_domain_pages(xc_interface *xch, uint32_t domid, unsigned long dst_pfn, int num); static inline int xc_clear_domain_page(xc_interface *xch, uint32_t domid, unsigned long dst_pfn) { return xc_clear_domain_pages(xch, domid, dst_pfn, 1); } int xc_mmuext_op(xc_interface *xch, struct mmuext_op *op, unsigned int nr_ops, domid_t dom); /* System wide memory properties */ int xc_maximum_ram_page(xc_interface *xch, unsigned long *max_mfn); /* Get current total pages allocated to a domain. */ long xc_get_tot_pages(xc_interface *xch, uint32_t domid); /** * This function retrieves the the number of bytes available * in the heap in a specific range of address-widths and nodes. * * @parm xch a handle to an open hypervisor interface * @parm domid the domain to query * @parm min_width the smallest address width to query (0 if don't care) * @parm max_width the largest address width to query (0 if don't care) * @parm node the node to query (-1 for all) * @parm *bytes caller variable to put total bytes counted * @return 0 on success, <0 on failure. */ int xc_availheap(xc_interface *xch, int min_width, int max_width, int node, uint64_t *bytes); /* * Trace Buffer Operations */ /** * xc_tbuf_enable - enable tracing buffers * * @parm xch a handle to an open hypervisor interface * @parm cnt size of tracing buffers to create (in pages) * @parm mfn location to store mfn of the trace buffers to * @parm size location to store the size (in bytes) of a trace buffer to * * Gets the machine address of the trace pointer area and the size of the * per CPU buffers. */ int xc_tbuf_enable(xc_interface *xch, unsigned long pages, unsigned long *mfn, unsigned long *size); /* * Disable tracing buffers. */ int xc_tbuf_disable(xc_interface *xch); /** * This function sets the size of the trace buffers. Setting the size * is currently a one-shot operation that may be performed either at boot * time or via this interface, not both. The buffer size must be set before * enabling tracing. * * @parm xch a handle to an open hypervisor interface * @parm size the size in pages per cpu for the trace buffers * @return 0 on success, -1 on failure. */ int xc_tbuf_set_size(xc_interface *xch, unsigned long size); /** * This function retrieves the current size of the trace buffers. * Note that the size returned is in terms of bytes, not pages. * @parm xch a handle to an open hypervisor interface * @parm size will contain the size in bytes for the trace buffers * @return 0 on success, -1 on failure. */ int xc_tbuf_get_size(xc_interface *xch, unsigned long *size); int xc_tbuf_set_cpu_mask(xc_interface *xch, xc_cpumap_t mask); int xc_tbuf_set_evt_mask(xc_interface *xch, uint32_t mask); int xc_domctl(xc_interface *xch, struct xen_domctl *domctl); int xc_sysctl(xc_interface *xch, struct xen_sysctl *sysctl); int xc_version(xc_interface *xch, int cmd, void *arg); int xc_flask_op(xc_interface *xch, xen_flask_op_t *op); /* * Subscribe to domain suspend via evtchn. * Returns -1 on failure, in which case errno will be set appropriately. * Just calls XEN_DOMCTL_subscribe - see the caveats for that domctl * (in its doc comment in domctl.h). */ int xc_domain_subscribe_for_suspend( xc_interface *xch, domid_t domid, evtchn_port_t port); /************************** * GRANT TABLE OPERATIONS * **************************/ /* * These functions sometimes log messages as above, but not always. */ int xc_gnttab_op(xc_interface *xch, int cmd, void * op, int op_size, int count); /* Logs iff hypercall bounce fails, otherwise doesn't. */ int xc_gnttab_get_version(xc_interface *xch, int domid); /* Never logs */ grant_entry_v1_t *xc_gnttab_map_table_v1(xc_interface *xch, int domid, int *gnt_num); grant_entry_v2_t *xc_gnttab_map_table_v2(xc_interface *xch, int domid, int *gnt_num); /* Sometimes these don't set errno [fixme], and sometimes they don't log. */ int xc_physdev_map_pirq(xc_interface *xch, int domid, int index, int *pirq); int xc_physdev_map_pirq_msi(xc_interface *xch, int domid, int index, int *pirq, int devfn, int bus, int entry_nr, uint64_t table_base); int xc_physdev_unmap_pirq(xc_interface *xch, int domid, int pirq); /* * LOGGING AND ERROR REPORTING */ #define XC_MAX_ERROR_MSG_LEN 1024 typedef struct xc_error { enum xc_error_code code; char message[XC_MAX_ERROR_MSG_LEN]; } xc_error; /* * Convert an error code or level into a text description. Return values * are pointers to fixed strings and do not need to be freed. * Do not fail, but return pointers to generic strings if fed bogus input. */ const char *xc_error_code_to_desc(int code); /* * Convert an errno value to a text description. */ const char *xc_strerror(xc_interface *xch, int errcode); /* * Return a pointer to the last error with level XC_REPORT_ERROR. This * pointer and the data pointed to are only valid until the next call * to libxc in the same thread. */ const xc_error *xc_get_last_error(xc_interface *handle); /* * Clear the last error */ void xc_clear_last_error(xc_interface *xch); int xc_hvm_param_set(xc_interface *handle, domid_t dom, uint32_t param, uint64_t value); int xc_hvm_param_get(xc_interface *handle, domid_t dom, uint32_t param, uint64_t *value); /* Deprecated: use xc_hvm_param_set/get() instead. */ int xc_set_hvm_param(xc_interface *handle, domid_t dom, int param, unsigned long value); int xc_get_hvm_param(xc_interface *handle, domid_t dom, int param, unsigned long *value); /* HVM guest pass-through */ int xc_assign_device(xc_interface *xch, uint32_t domid, uint32_t machine_sbdf, uint32_t flag); int xc_get_device_group(xc_interface *xch, uint32_t domid, uint32_t machine_sbdf, uint32_t max_sdevs, uint32_t *num_sdevs, uint32_t *sdev_array); int xc_test_assign_device(xc_interface *xch, uint32_t domid, uint32_t machine_sbdf); int xc_deassign_device(xc_interface *xch, uint32_t domid, uint32_t machine_sbdf); int xc_assign_dt_device(xc_interface *xch, uint32_t domid, char *path); int xc_test_assign_dt_device(xc_interface *xch, uint32_t domid, char *path); int xc_deassign_dt_device(xc_interface *xch, uint32_t domid, char *path); int xc_domain_memory_mapping(xc_interface *xch, uint32_t domid, unsigned long first_gfn, unsigned long first_mfn, unsigned long nr_mfns, uint32_t add_mapping); int xc_domain_ioport_mapping(xc_interface *xch, uint32_t domid, uint32_t first_gport, uint32_t first_mport, uint32_t nr_ports, uint32_t add_mapping); int xc_domain_update_msi_irq( xc_interface *xch, uint32_t domid, uint32_t gvec, uint32_t pirq, uint32_t gflags, uint64_t gtable); int xc_domain_unbind_msi_irq(xc_interface *xch, uint32_t domid, uint32_t gvec, uint32_t pirq, uint32_t gflags); int xc_domain_bind_pt_irq(xc_interface *xch, uint32_t domid, uint8_t machine_irq, uint8_t irq_type, uint8_t bus, uint8_t device, uint8_t intx, uint8_t isa_irq); int xc_domain_unbind_pt_irq(xc_interface *xch, uint32_t domid, uint8_t machine_irq, uint8_t irq_type, uint8_t bus, uint8_t device, uint8_t intx, uint8_t isa_irq); int xc_domain_bind_pt_pci_irq(xc_interface *xch, uint32_t domid, uint8_t machine_irq, uint8_t bus, uint8_t device, uint8_t intx); int xc_domain_bind_pt_isa_irq(xc_interface *xch, uint32_t domid, uint8_t machine_irq); int xc_domain_bind_pt_spi_irq(xc_interface *xch, uint32_t domid, uint16_t vspi, uint16_t spi); int xc_domain_unbind_pt_spi_irq(xc_interface *xch, uint32_t domid, uint16_t vspi, uint16_t spi); int xc_domain_set_machine_address_size(xc_interface *xch, uint32_t domid, unsigned int width); int xc_domain_get_machine_address_size(xc_interface *xch, uint32_t domid); int xc_domain_suppress_spurious_page_faults(xc_interface *xch, uint32_t domid); /* Set the target domain */ int xc_domain_set_target(xc_interface *xch, uint32_t domid, uint32_t target); /* Control the domain for debug */ int xc_domain_debug_control(xc_interface *xch, uint32_t domid, uint32_t sop, uint32_t vcpu); #if defined(__i386__) || defined(__x86_64__) int xc_cpuid_check(xc_interface *xch, const unsigned int *input, const char **config, char **config_transformed); int xc_cpuid_set(xc_interface *xch, domid_t domid, const unsigned int *input, const char **config, char **config_transformed); int xc_cpuid_apply_policy(xc_interface *xch, domid_t domid, uint32_t *featureset, unsigned int nr_features); void xc_cpuid_to_str(const unsigned int *regs, char **strs); /* some strs[] may be NULL if ENOMEM */ int xc_mca_op(xc_interface *xch, struct xen_mc *mc); #endif struct xc_px_val { uint64_t freq; /* Px core frequency */ uint64_t residency; /* Px residency time */ uint64_t count; /* Px transition count */ }; struct xc_px_stat { uint8_t total; /* total Px states */ uint8_t usable; /* usable Px states */ uint8_t last; /* last Px state */ uint8_t cur; /* current Px state */ uint64_t *trans_pt; /* Px transition table */ struct xc_px_val *pt; }; int xc_pm_get_max_px(xc_interface *xch, int cpuid, int *max_px); int xc_pm_get_pxstat(xc_interface *xch, int cpuid, struct xc_px_stat *pxpt); int xc_pm_reset_pxstat(xc_interface *xch, int cpuid); struct xc_cx_stat { uint32_t nr; /* entry nr in triggers[]/residencies[], incl C0 */ uint32_t last; /* last Cx state */ uint64_t idle_time; /* idle time from boot */ uint64_t *triggers; /* Cx trigger counts */ uint64_t *residencies; /* Cx residencies */ uint32_t nr_pc; /* entry nr in pc[] */ uint32_t nr_cc; /* entry nr in cc[] */ uint64_t *pc; /* 1-biased indexing (i.e. excl C0) */ uint64_t *cc; /* 1-biased indexing (i.e. excl C0) */ }; typedef struct xc_cx_stat xc_cx_stat_t; int xc_pm_get_max_cx(xc_interface *xch, int cpuid, int *max_cx); int xc_pm_get_cxstat(xc_interface *xch, int cpuid, struct xc_cx_stat *cxpt); int xc_pm_reset_cxstat(xc_interface *xch, int cpuid); int xc_cpu_online(xc_interface *xch, int cpu); int xc_cpu_offline(xc_interface *xch, int cpu); /* * cpufreq para name of this structure named * same as sysfs file name of native linux */ typedef xen_userspace_t xc_userspace_t; typedef xen_ondemand_t xc_ondemand_t; struct xc_get_cpufreq_para { /* IN/OUT variable */ uint32_t cpu_num; uint32_t freq_num; uint32_t gov_num; /* for all governors */ /* OUT variable */ uint32_t *affected_cpus; uint32_t *scaling_available_frequencies; char *scaling_available_governors; char scaling_driver[CPUFREQ_NAME_LEN]; uint32_t cpuinfo_cur_freq; uint32_t cpuinfo_max_freq; uint32_t cpuinfo_min_freq; uint32_t scaling_cur_freq; char scaling_governor[CPUFREQ_NAME_LEN]; uint32_t scaling_max_freq; uint32_t scaling_min_freq; /* for specific governor */ union { xc_userspace_t userspace; xc_ondemand_t ondemand; } u; int32_t turbo_enabled; }; int xc_get_cpufreq_para(xc_interface *xch, int cpuid, struct xc_get_cpufreq_para *user_para); int xc_set_cpufreq_gov(xc_interface *xch, int cpuid, char *govname); int xc_set_cpufreq_para(xc_interface *xch, int cpuid, int ctrl_type, int ctrl_value); int xc_get_cpufreq_avgfreq(xc_interface *xch, int cpuid, int *avg_freq); int xc_set_sched_opt_smt(xc_interface *xch, uint32_t value); int xc_set_vcpu_migration_delay(xc_interface *xch, uint32_t value); int xc_get_vcpu_migration_delay(xc_interface *xch, uint32_t *value); int xc_get_cpuidle_max_cstate(xc_interface *xch, uint32_t *value); int xc_set_cpuidle_max_cstate(xc_interface *xch, uint32_t value); int xc_enable_turbo(xc_interface *xch, int cpuid); int xc_disable_turbo(xc_interface *xch, int cpuid); /** * tmem operations */ int xc_tmem_control_oid(xc_interface *xch, int32_t pool_id, uint32_t subop, uint32_t cli_id, uint32_t len, uint32_t arg, struct xen_tmem_oid oid, void *buf); int xc_tmem_control(xc_interface *xch, int32_t pool_id, uint32_t subop, uint32_t cli_id, uint32_t len, uint32_t arg, void *buf); int xc_tmem_auth(xc_interface *xch, int cli_id, char *uuid_str, int enable); int xc_tmem_save(xc_interface *xch, int dom, int live, int fd, int field_marker); int xc_tmem_save_extra(xc_interface *xch, int dom, int fd, int field_marker); void xc_tmem_save_done(xc_interface *xch, int dom); int xc_tmem_restore(xc_interface *xch, int dom, int fd); int xc_tmem_restore_extra(xc_interface *xch, int dom, int fd); /** * altp2m operations */ int xc_altp2m_get_domain_state(xc_interface *handle, domid_t dom, bool *state); int xc_altp2m_set_domain_state(xc_interface *handle, domid_t dom, bool state); int xc_altp2m_set_vcpu_enable_notify(xc_interface *handle, domid_t domid, uint32_t vcpuid, xen_pfn_t gfn); int xc_altp2m_create_view(xc_interface *handle, domid_t domid, xenmem_access_t default_access, uint16_t *view_id); int xc_altp2m_destroy_view(xc_interface *handle, domid_t domid, uint16_t view_id); /* Switch all vCPUs of the domain to the specified altp2m view */ int xc_altp2m_switch_to_view(xc_interface *handle, domid_t domid, uint16_t view_id); int xc_altp2m_set_mem_access(xc_interface *handle, domid_t domid, uint16_t view_id, xen_pfn_t gfn, xenmem_access_t access); int xc_altp2m_change_gfn(xc_interface *handle, domid_t domid, uint16_t view_id, xen_pfn_t old_gfn, xen_pfn_t new_gfn); /** * Mem paging operations. * Paging is supported only on the x86 architecture in 64 bit mode, with * Hardware-Assisted Paging (i.e. Intel EPT, AMD NPT). Moreover, AMD NPT * support is considered experimental. */ int xc_mem_paging_enable(xc_interface *xch, domid_t domain_id, uint32_t *port); int xc_mem_paging_disable(xc_interface *xch, domid_t domain_id); int xc_mem_paging_resume(xc_interface *xch, domid_t domain_id); int xc_mem_paging_nominate(xc_interface *xch, domid_t domain_id, uint64_t gfn); int xc_mem_paging_evict(xc_interface *xch, domid_t domain_id, uint64_t gfn); int xc_mem_paging_prep(xc_interface *xch, domid_t domain_id, uint64_t gfn); int xc_mem_paging_load(xc_interface *xch, domid_t domain_id, uint64_t gfn, void *buffer); /** * Access tracking operations. * Supported only on Intel EPT 64 bit processors. */ /* * Set a range of memory to a specific access. * Allowed types are XENMEM_access_default, XENMEM_access_n, any combination of * XENMEM_access_ + (rwx), and XENMEM_access_rx2rw */ int xc_set_mem_access(xc_interface *xch, domid_t domain_id, xenmem_access_t access, uint64_t first_pfn, uint32_t nr); /* * Set an array of pages to their respective access in the access array. * The nr parameter specifies the size of the pages and access arrays. * The same allowed access types as for xc_set_mem_access() apply. */ int xc_set_mem_access_multi(xc_interface *xch, domid_t domain_id, uint8_t *access, uint64_t *pages, uint32_t nr); /* * Gets the mem access for the given page (returned in access on success) */ int xc_get_mem_access(xc_interface *xch, domid_t domain_id, uint64_t pfn, xenmem_access_t *access); /*** * Monitor control operations. * * Enables the VM event monitor ring and returns the mapped ring page. * This ring is used to deliver mem_access events, as well a set of additional * events that can be enabled with the xc_monitor_* functions. * * Will return NULL on error. * Caller has to unmap this page when done. */ void *xc_monitor_enable(xc_interface *xch, domid_t domain_id, uint32_t *port); int xc_monitor_disable(xc_interface *xch, domid_t domain_id); int xc_monitor_resume(xc_interface *xch, domid_t domain_id); /* * Get a bitmap of supported monitor events in the form * (1 << XEN_DOMCTL_MONITOR_EVENT_*). */ int xc_monitor_get_capabilities(xc_interface *xch, domid_t domain_id, uint32_t *capabilities); int xc_monitor_write_ctrlreg(xc_interface *xch, domid_t domain_id, uint16_t index, bool enable, bool sync, bool onchangeonly); /* * A list of MSR indices can usually be found in /usr/include/asm/msr-index.h. * Please consult the Intel/AMD manuals for more information on * non-architectural indices. */ int xc_monitor_mov_to_msr(xc_interface *xch, domid_t domain_id, uint32_t msr, bool enable); int xc_monitor_singlestep(xc_interface *xch, domid_t domain_id, bool enable); int xc_monitor_software_breakpoint(xc_interface *xch, domid_t domain_id, bool enable); int xc_monitor_descriptor_access(xc_interface *xch, domid_t domain_id, bool enable); int xc_monitor_guest_request(xc_interface *xch, domid_t domain_id, bool enable, bool sync); int xc_monitor_debug_exceptions(xc_interface *xch, domid_t domain_id, bool enable, bool sync); int xc_monitor_cpuid(xc_interface *xch, domid_t domain_id, bool enable); int xc_monitor_privileged_call(xc_interface *xch, domid_t domain_id, bool enable); /** * This function enables / disables emulation for each REP for a * REP-compatible instruction. * * @parm xch a handle to an open hypervisor interface. * @parm domain_id the domain id one wants to get the node affinity of. * @parm enable if 0 optimize when possible, else emulate each REP. * @return 0 on success, -1 on failure. */ int xc_monitor_emulate_each_rep(xc_interface *xch, domid_t domain_id, bool enable); /*** * Memory sharing operations. * * Unles otherwise noted, these calls return 0 on succes, -1 and errno on * failure. * * Sharing is supported only on the x86 architecture in 64 bit mode, with * Hardware-Assisted Paging (i.e. Intel EPT, AMD NPT). Moreover, AMD NPT * support is considered experimental. * Calls below return ENOSYS if not in the x86_64 architecture. * Calls below return ENODEV if the domain does not support HAP. * Calls below return ESRCH if the specified domain does not exist. * Calls below return EPERM if the caller is unprivileged for this domain. */ /* Turn on/off sharing for the domid, depending on the enable flag. * * Returns EXDEV if trying to enable and the domain has had a PCI device * assigned for passthrough (these two features are mutually exclusive). * * When sharing for a domain is turned off, the domain may still reference * shared pages. Unsharing happens lazily. */ int xc_memshr_control(xc_interface *xch, domid_t domid, int enable); /* Create a communication ring in which the hypervisor will place ENOMEM * notifications. * * ENOMEM happens when unsharing pages: a Copy-on-Write duplicate needs to be * allocated, and thus the out-of-memory error occurr. * * For complete examples on how to plumb a notification ring, look into * xenpaging or xen-access. * * On receipt of a notification, the helper should ensure there is memory * available to the domain before retrying. * * If a domain encounters an ENOMEM condition when sharing and this ring * has not been set up, the hypervisor will crash the domain. * * Fails with: * EINVAL if port is NULL * EINVAL if the sharing ring has already been enabled * ENOSYS if no guest gfn has been specified to host the ring via an hvm param * EINVAL if the gfn for the ring has not been populated * ENOENT if the gfn for the ring is paged out, or cannot be unshared * EINVAL if the gfn for the ring cannot be written to * EINVAL if the domain is dying * ENOSPC if an event channel cannot be allocated for the ring * ENOMEM if memory cannot be allocated for internal data structures * EINVAL or EACCESS if the request is denied by the security policy */ int xc_memshr_ring_enable(xc_interface *xch, domid_t domid, uint32_t *port); /* Disable the ring for ENOMEM communication. * May fail with EINVAL if the ring was not enabled in the first place. */ int xc_memshr_ring_disable(xc_interface *xch, domid_t domid); /* * Calls below return EINVAL if sharing has not been enabled for the domain * Calls below return EINVAL if the domain is dying */ /* Once a reponse to an ENOMEM notification is prepared, the tool can * notify the hypervisor to re-schedule the faulting vcpu of the domain with an * event channel kick and/or this call. */ int xc_memshr_domain_resume(xc_interface *xch, domid_t domid); /* Select a page for sharing. * * A 64 bit opaque handle will be stored in handle. The hypervisor ensures * that if the page is modified, the handle will be invalidated, and future * users of it will fail. If the page has already been selected and is still * associated to a valid handle, the existing handle will be returned. * * May fail with: * EINVAL if the gfn is not populated or not sharable (mmio, etc) * ENOMEM if internal data structures cannot be allocated * E2BIG if the page is being referenced by other subsytems (e.g. qemu) * ENOENT or EEXIST if there are internal hypervisor errors. */ int xc_memshr_nominate_gfn(xc_interface *xch, domid_t domid, unsigned long gfn, uint64_t *handle); /* Same as above, but instead of a guest frame number, the input is a grant * reference provided by the guest. * * May fail with EINVAL if the grant reference is invalid. */ int xc_memshr_nominate_gref(xc_interface *xch, domid_t domid, grant_ref_t gref, uint64_t *handle); /* The three calls below may fail with * 10 (or -XENMEM_SHARING_OP_S_HANDLE_INVALID) if the handle passed as source * is invalid. * 9 (or -XENMEM_SHARING_OP_C_HANDLE_INVALID) if the handle passed as client is * invalid. */ /* Share two nominated guest pages. * * If the call succeeds, both pages will point to the same backing frame (or * mfn). The hypervisor will verify the handles are still valid, but it will * not perform any sanity checking on the contens of the pages (the selection * mechanism for sharing candidates is entirely up to the user-space tool). * * After successful sharing, the client handle becomes invalid. Both tuples point to the same mfn with the same handle, the one specified as * source. Either 3-tuple can be specified later for further re-sharing. */ int xc_memshr_share_gfns(xc_interface *xch, domid_t source_domain, unsigned long source_gfn, uint64_t source_handle, domid_t client_domain, unsigned long client_gfn, uint64_t client_handle); /* Same as above, but share two grant references instead. * * May fail with EINVAL if either grant reference is invalid. */ int xc_memshr_share_grefs(xc_interface *xch, domid_t source_domain, grant_ref_t source_gref, uint64_t source_handle, domid_t client_domain, grant_ref_t client_gref, uint64_t client_handle); /* Allows to add to the guest physmap of the client domain a shared frame * directly. * * May additionally fail with * 9 (-XENMEM_SHARING_OP_C_HANDLE_INVALID) if the physmap entry for the gfn is * not suitable. * ENOMEM if internal data structures cannot be allocated. * ENOENT if there is an internal hypervisor error. */ int xc_memshr_add_to_physmap(xc_interface *xch, domid_t source_domain, unsigned long source_gfn, uint64_t source_handle, domid_t client_domain, unsigned long client_gfn); /* Allows to deduplicate a range of memory of a client domain. Using * this function is equivalent of calling xc_memshr_nominate_gfn for each gfn * in the two domains followed by xc_memshr_share_gfns. * * May fail with -EINVAL if the source and client domain have different * memory size or if memory sharing is not enabled on either of the domains. * May also fail with -ENOMEM if there isn't enough memory available to store * the sharing metadata before deduplication can happen. */ int xc_memshr_range_share(xc_interface *xch, domid_t source_domain, domid_t client_domain, uint64_t first_gfn, uint64_t last_gfn); /* Debug calls: return the number of pages referencing the shared frame backing * the input argument. Should be one or greater. * * May fail with EINVAL if there is no backing shared frame for the input * argument. */ int xc_memshr_debug_gfn(xc_interface *xch, domid_t domid, unsigned long gfn); /* May additionally fail with EINVAL if the grant reference is invalid. */ int xc_memshr_debug_gref(xc_interface *xch, domid_t domid, grant_ref_t gref); /* Audits the share subsystem. * * Returns ENOSYS if not supported (may not be compiled into the hypervisor). * * Returns the number of errors found during auditing otherwise. May be (should * be!) zero. * * If debugtrace support has been compiled into the hypervisor and is enabled, * verbose descriptions for the errors are available in the hypervisor console. */ int xc_memshr_audit(xc_interface *xch); /* Stats reporting. * * At any point in time, the following equality should hold for a host: * * Let dominfo(d) be the xc_dominfo_t struct filled by a call to * xc_domain_getinfo(d) * * The summation of dominfo(d)->shr_pages for all domains in the system * should be equal to * xc_sharing_freed_pages + xc_sharing_used_frames */ /* * This function returns the total number of pages freed by using sharing * on the system. For example, if two domains contain a single entry in * their p2m table that points to the same shared page (and no other pages * in the system are shared), then this function should return 1. */ long xc_sharing_freed_pages(xc_interface *xch); /* * This function returns the total number of frames occupied by shared * pages on the system. This is independent of the number of domains * pointing at these frames. For example, in the above scenario this * should return 1. (And dominfo(d) for each of the two domains should return 1 * as well). * * Note that some of these sharing_used_frames may be referenced by * a single domain page, and thus not realize any savings. The same * applies to some of the pages counted in dominfo(d)->shr_pages. */ long xc_sharing_used_frames(xc_interface *xch); /*** End sharing interface ***/ int xc_flask_load(xc_interface *xc_handle, char *buf, uint32_t size); int xc_flask_context_to_sid(xc_interface *xc_handle, char *buf, uint32_t size, uint32_t *sid); int xc_flask_sid_to_context(xc_interface *xc_handle, int sid, char *buf, uint32_t size); int xc_flask_getenforce(xc_interface *xc_handle); int xc_flask_setenforce(xc_interface *xc_handle, int mode); int xc_flask_getbool_byid(xc_interface *xc_handle, int id, char *name, uint32_t size, int *curr, int *pend); int xc_flask_getbool_byname(xc_interface *xc_handle, char *name, int *curr, int *pend); int xc_flask_setbool(xc_interface *xc_handle, char *name, int value, int commit); int xc_flask_add_pirq(xc_interface *xc_handle, unsigned int pirq, char *scontext); int xc_flask_add_ioport(xc_interface *xc_handle, unsigned long low, unsigned long high, char *scontext); int xc_flask_add_iomem(xc_interface *xc_handle, unsigned long low, unsigned long high, char *scontext); int xc_flask_add_device(xc_interface *xc_handle, unsigned long device, char *scontext); int xc_flask_del_pirq(xc_interface *xc_handle, unsigned int pirq); int xc_flask_del_ioport(xc_interface *xc_handle, unsigned long low, unsigned long high); int xc_flask_del_iomem(xc_interface *xc_handle, unsigned long low, unsigned long high); int xc_flask_del_device(xc_interface *xc_handle, unsigned long device); int xc_flask_access(xc_interface *xc_handle, const char *scon, const char *tcon, uint16_t tclass, uint32_t req, uint32_t *allowed, uint32_t *decided, uint32_t *auditallow, uint32_t *auditdeny, uint32_t *seqno); int xc_flask_avc_cachestats(xc_interface *xc_handle, char *buf, int size); int xc_flask_policyvers(xc_interface *xc_handle); int xc_flask_avc_hashstats(xc_interface *xc_handle, char *buf, int size); int xc_flask_getavc_threshold(xc_interface *xc_handle); int xc_flask_setavc_threshold(xc_interface *xc_handle, int threshold); int xc_flask_relabel_domain(xc_interface *xch, int domid, uint32_t sid); struct elf_binary; void xc_elf_set_logfile(xc_interface *xch, struct elf_binary *elf, int verbose); /* Useful for callers who also use libelf. */ /** * Checkpoint Compression */ typedef struct compression_ctx comp_ctx; comp_ctx *xc_compression_create_context(xc_interface *xch, unsigned long p2m_size); void xc_compression_free_context(xc_interface *xch, comp_ctx *ctx); /** * Add a page to compression page buffer, to be compressed later. * * returns 0 if the page was successfully added to the page buffer * * returns -1 if there is no space in buffer. In this case, the * application should call xc_compression_compress_pages to compress * the buffer (or atleast part of it), thereby freeing some space in * the page buffer. * * returns -2 if the pfn is out of bounds, where the bound is p2m_size * parameter passed during xc_compression_create_context. */ int xc_compression_add_page(xc_interface *xch, comp_ctx *ctx, char *page, unsigned long pfn, int israw); /** * Delta compress pages in the compression buffer and inserts the * compressed data into the supplied compression buffer compbuf, whose * size is compbuf_size. * After compression, the pages are copied to the internal LRU cache. * * This function compresses as many pages as possible into the * supplied compression buffer. It maintains an internal iterator to * keep track of pages in the input buffer that are yet to be compressed. * * returns -1 if the compression buffer has run out of space. * returns 1 on success. * returns 0 if no more pages are left to be compressed. * When the return value is non-zero, compbuf_len indicates the actual * amount of data present in compbuf (<=compbuf_size). */ int xc_compression_compress_pages(xc_interface *xch, comp_ctx *ctx, char *compbuf, unsigned long compbuf_size, unsigned long *compbuf_len); /** * Resets the internal page buffer that holds dirty pages before compression. * Also resets the iterators. */ void xc_compression_reset_pagebuf(xc_interface *xch, comp_ctx *ctx); /** * Caller must supply the compression buffer (compbuf), * its size (compbuf_size) and a reference to index variable (compbuf_pos) * that is used internally. Each call pulls out one page from the compressed * chunk and copies it to dest. */ int xc_compression_uncompress_page(xc_interface *xch, char *compbuf, unsigned long compbuf_size, unsigned long *compbuf_pos, char *dest); /* * Execute an image previously loaded with xc_kexec_load(). * * Does not return on success. * * Fails with: * ENOENT if the specified image has not been loaded. */ int xc_kexec_exec(xc_interface *xch, int type); /* * Find the machine address and size of certain memory areas. * * KEXEC_RANGE_MA_CRASH crash area * KEXEC_RANGE_MA_XEN Xen itself * KEXEC_RANGE_MA_CPU CPU note for CPU number 'nr' * KEXEC_RANGE_MA_XENHEAP xenheap * KEXEC_RANGE_MA_EFI_MEMMAP EFI Memory Map * KEXEC_RANGE_MA_VMCOREINFO vmcoreinfo * * Fails with: * EINVAL if the range or CPU number isn't valid. */ int xc_kexec_get_range(xc_interface *xch, int range, int nr, uint64_t *size, uint64_t *start); /* * Load a kexec image into memory. * * The image may be of type KEXEC_TYPE_DEFAULT (executed on request) * or KEXEC_TYPE_CRASH (executed on a crash). * * The image architecture may be a 32-bit variant of the hypervisor * architecture (e.g, EM_386 on a x86-64 hypervisor). * * Fails with: * ENOMEM if there is insufficient memory for the new image. * EINVAL if the image does not fit into the crash area or the entry * point isn't within one of segments. * EBUSY if another image is being executed. */ int xc_kexec_load(xc_interface *xch, uint8_t type, uint16_t arch, uint64_t entry_maddr, uint32_t nr_segments, xen_kexec_segment_t *segments); /* * Unload a kexec image. * * This prevents a KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH image from * being executed. The crash images are not cleared from the crash * region. */ int xc_kexec_unload(xc_interface *xch, int type); /* * Find out whether the image has been succesfully loaded. * * The type can be either KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH. * If zero is returned, that means no image is loaded for the type. * If one is returned, that means an image is loaded for the type. * Otherwise, negative return value indicates error. */ int xc_kexec_status(xc_interface *xch, int type); typedef xenpf_resource_entry_t xc_resource_entry_t; /* * Generic resource operation which contains multiple non-preemptible * resource access entries that passed to xc_resource_op(). */ struct xc_resource_op { uint64_t result; /* on return, check this field first */ uint32_t cpu; /* which cpu to run */ uint32_t nr_entries; /* number of resource entries */ xc_resource_entry_t *entries; }; typedef struct xc_resource_op xc_resource_op_t; int xc_resource_op(xc_interface *xch, uint32_t nr_ops, xc_resource_op_t *ops); #if defined(__i386__) || defined(__x86_64__) enum xc_psr_cmt_type { XC_PSR_CMT_L3_OCCUPANCY, XC_PSR_CMT_TOTAL_MEM_COUNT, XC_PSR_CMT_LOCAL_MEM_COUNT, }; typedef enum xc_psr_cmt_type xc_psr_cmt_type; enum xc_psr_cat_type { XC_PSR_CAT_L3_CBM = 1, XC_PSR_CAT_L3_CBM_CODE = 2, XC_PSR_CAT_L3_CBM_DATA = 3, }; typedef enum xc_psr_cat_type xc_psr_cat_type; int xc_psr_cmt_attach(xc_interface *xch, uint32_t domid); int xc_psr_cmt_detach(xc_interface *xch, uint32_t domid); int xc_psr_cmt_get_domain_rmid(xc_interface *xch, uint32_t domid, uint32_t *rmid); int xc_psr_cmt_get_total_rmid(xc_interface *xch, uint32_t *total_rmid); int xc_psr_cmt_get_l3_upscaling_factor(xc_interface *xch, uint32_t *upscaling_factor); int xc_psr_cmt_get_l3_event_mask(xc_interface *xch, uint32_t *event_mask); int xc_psr_cmt_get_l3_cache_size(xc_interface *xch, uint32_t cpu, uint32_t *l3_cache_size); int xc_psr_cmt_get_data(xc_interface *xch, uint32_t rmid, uint32_t cpu, uint32_t psr_cmt_type, uint64_t *monitor_data, uint64_t *tsc); int xc_psr_cmt_enabled(xc_interface *xch); int xc_psr_cat_set_domain_data(xc_interface *xch, uint32_t domid, xc_psr_cat_type type, uint32_t target, uint64_t data); int xc_psr_cat_get_domain_data(xc_interface *xch, uint32_t domid, xc_psr_cat_type type, uint32_t target, uint64_t *data); int xc_psr_cat_get_l3_info(xc_interface *xch, uint32_t socket, uint32_t *cos_max, uint32_t *cbm_len, bool *cdp_enabled); int xc_get_cpu_levelling_caps(xc_interface *xch, uint32_t *caps); int xc_get_cpu_featureset(xc_interface *xch, uint32_t index, uint32_t *nr_features, uint32_t *featureset); uint32_t xc_get_cpu_featureset_size(void); enum xc_static_cpu_featuremask { XC_FEATUREMASK_KNOWN, XC_FEATUREMASK_SPECIAL, XC_FEATUREMASK_PV, XC_FEATUREMASK_HVM_SHADOW, XC_FEATUREMASK_HVM_HAP, XC_FEATUREMASK_DEEP_FEATURES, }; const uint32_t *xc_get_static_cpu_featuremask(enum xc_static_cpu_featuremask); const uint32_t *xc_get_feature_deep_deps(uint32_t feature); #endif int xc_livepatch_upload(xc_interface *xch, char *name, unsigned char *payload, uint32_t size); int xc_livepatch_get(xc_interface *xch, char *name, xen_livepatch_status_t *status); /* * The heart of this function is to get an array of xen_livepatch_status_t. * * However it is complex because it has to deal with the hypervisor * returning some of the requested data or data being stale * (another hypercall might alter the list). * * The parameters that the function expects to contain data from * the hypervisor are: 'info', 'name', and 'len'. The 'done' and * 'left' are also updated with the number of entries filled out * and respectively the number of entries left to get from hypervisor. * * It is expected that the caller of this function will take the * 'left' and use the value for 'start'. This way we have an * cursor in the array. Note that the 'info','name', and 'len' will * be updated at the subsequent calls. * * The 'max' is to be provided by the caller with the maximum * number of entries that 'info', 'name', and 'len' arrays can * be filled up with. * * Each entry in the 'name' array is expected to be of XEN_LIVEPATCH_NAME_SIZE * length. * * Each entry in the 'info' array is expected to be of xen_livepatch_status_t * structure size. * * Each entry in the 'len' array is expected to be of uint32_t size. * * The return value is zero if the hypercall completed successfully. * Note that the return value is _not_ the amount of entries filled * out - that is saved in 'done'. * * If there was an error performing the operation, the return value * will contain an negative -EXX type value. The 'done' and 'left' * will contain the number of entries that had been succesfully * retrieved (if any). */ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start, xen_livepatch_status_t *info, char *name, uint32_t *len, unsigned int *done, unsigned int *left); /* * The operations are asynchronous and the hypervisor may take a while * to complete them. The `timeout` offers an option to expire the * operation if it could not be completed within the specified time * (in ns). Value of 0 means let hypervisor decide the best timeout. */ int xc_livepatch_apply(xc_interface *xch, char *name, uint32_t timeout); int xc_livepatch_revert(xc_interface *xch, char *name, uint32_t timeout); int xc_livepatch_unload(xc_interface *xch, char *name, uint32_t timeout); int xc_livepatch_replace(xc_interface *xch, char *name, uint32_t timeout); /* * Ensure cache coherency after memory modifications. A call to this function * is only required on ARM as the x86 architecture provides cache coherency * guarantees. Calling this function on x86 is allowed but has no effect. */ int xc_domain_cacheflush(xc_interface *xch, uint32_t domid, xen_pfn_t start_pfn, xen_pfn_t nr_pfns); /* Compat shims */ #include "xenctrl_compat.h" #endif /* XENCTRL_H */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_decompress_unsafe_xz.c0000664000175000017500000000154613256712137020647 0ustar smbsmb#include #include #include #include #include #include #include "xg_private.h" #include "xc_dom_decompress_unsafe.h" // TODO #define XZ_DEC_X86 typedef char bool_t; typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint32_t __le32; static inline u32 cpu_to_le32(const u32 v) { #if BYTE_ORDER == BIG_ENDIAN return (((v & 0x000000ffUL) << 24) | ((v & 0x0000ff00UL) << 8) | ((v & 0x00ff0000UL) >> 8) | ((v & 0xff000000UL) >> 24)); #else return v; #endif } static inline u32 le32_to_cpup(const u32 *p) { return cpu_to_le32(*p); } #define __force #define always_inline #include "../../xen/common/unxz.c" int xc_try_xz_decode( struct xc_dom_image *dom, void **blob, size_t *size) { return xc_dom_decompress_unsafe(unxz, dom, blob, size); } xen-4.9.2/tools/libxc/xc_minios.c0000664000175000017500000000326613256712137015061 0ustar smbsmb/****************************************************************************** * * Copyright 2007-2008 Samuel Thibault . * All rights reserved. * Use is subject to license terms. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #undef NDEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include "xc_private.h" void minios_interface_close_fd(int fd); extern void minios_interface_close_fd(int fd); void minios_interface_close_fd(int fd) { files[fd].type = FTYPE_NONE; } /* Optionally flush file to disk and discard page cache */ void discard_file_cache(xc_interface *xch, int fd, int flush) { if (flush) fsync(fd); } void *xc_memalign(xc_interface *xch, size_t alignment, size_t size) { return memalign(alignment, size); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_linux.c0000664000175000017500000000375313256712137014723 0ustar smbsmb/****************************************************************************** * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" /* Optionally flush file to disk and discard page cache */ void discard_file_cache(xc_interface *xch, int fd, int flush) { off_t cur = 0; int saved_errno = errno; if ( flush && (fsync(fd) < 0) ) { /*PERROR("Failed to flush file: %s", strerror(errno));*/ goto out; } /* * Calculate last page boundary of amount written so far * unless we are flushing in which case entire cache * is discarded. */ if ( !flush ) { if ( (cur = lseek(fd, 0, SEEK_CUR)) == (off_t)-1 ) cur = 0; cur &= ~(XC_PAGE_SIZE-1); } /* Discard from the buffer cache. */ if ( posix_fadvise64(fd, 0, cur, POSIX_FADV_DONTNEED) < 0 ) { /*PERROR("Failed to discard cache: %s", strerror(errno));*/ goto out; } out: errno = saved_errno; } void *xc_memalign(xc_interface *xch, size_t alignment, size_t size) { int ret; void *ptr; ret = posix_memalign(&ptr, alignment, size); if (ret != 0 || !ptr) return NULL; return ptr; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_mem_paging.c0000664000175000017500000000705313256712137015664 0ustar smbsmb/****************************************************************************** * * tools/libxc/xc_mem_paging.c * * Interface to low-level memory paging functionality. * * Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" static int xc_mem_paging_memop(xc_interface *xch, domid_t domain_id, unsigned int op, uint64_t gfn, void *buffer) { xen_mem_paging_op_t mpo; memset(&mpo, 0, sizeof(mpo)); mpo.op = op; mpo.domain = domain_id; mpo.gfn = gfn; mpo.buffer = (unsigned long) buffer; return do_memory_op(xch, XENMEM_paging_op, &mpo, sizeof(mpo)); } int xc_mem_paging_enable(xc_interface *xch, domid_t domain_id, uint32_t *port) { if ( !port ) { errno = EINVAL; return -1; } return xc_vm_event_control(xch, domain_id, XEN_VM_EVENT_ENABLE, XEN_DOMCTL_VM_EVENT_OP_PAGING, port); } int xc_mem_paging_disable(xc_interface *xch, domid_t domain_id) { return xc_vm_event_control(xch, domain_id, XEN_VM_EVENT_DISABLE, XEN_DOMCTL_VM_EVENT_OP_PAGING, NULL); } int xc_mem_paging_resume(xc_interface *xch, domid_t domain_id) { return xc_vm_event_control(xch, domain_id, XEN_VM_EVENT_RESUME, XEN_DOMCTL_VM_EVENT_OP_PAGING, NULL); } int xc_mem_paging_nominate(xc_interface *xch, domid_t domain_id, uint64_t gfn) { return xc_mem_paging_memop(xch, domain_id, XENMEM_paging_op_nominate, gfn, NULL); } int xc_mem_paging_evict(xc_interface *xch, domid_t domain_id, uint64_t gfn) { return xc_mem_paging_memop(xch, domain_id, XENMEM_paging_op_evict, gfn, NULL); } int xc_mem_paging_prep(xc_interface *xch, domid_t domain_id, uint64_t gfn) { return xc_mem_paging_memop(xch, domain_id, XENMEM_paging_op_prep, gfn, NULL); } int xc_mem_paging_load(xc_interface *xch, domid_t domain_id, uint64_t gfn, void *buffer) { int rc, old_errno; errno = EINVAL; if ( !buffer ) return -1; if ( ((unsigned long) buffer) & (XC_PAGE_SIZE - 1) ) return -1; if ( mlock(buffer, XC_PAGE_SIZE) ) return -1; rc = xc_mem_paging_memop(xch, domain_id, XENMEM_paging_op_prep, gfn, buffer); old_errno = errno; munlock(buffer, XC_PAGE_SIZE); errno = old_errno; return rc; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_common_x86.c0000664000175000017500000000234113256712137016255 0ustar smbsmb#include "xc_sr_common_x86.h" int write_tsc_info(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; struct xc_sr_rec_tsc_info tsc = { 0 }; struct xc_sr_record rec = { .type = REC_TYPE_TSC_INFO, .length = sizeof(tsc), .data = &tsc }; if ( xc_domain_get_tsc_info(xch, ctx->domid, &tsc.mode, &tsc.nsec, &tsc.khz, &tsc.incarnation) < 0 ) { PERROR("Unable to obtain TSC information"); return -1; } return write_record(ctx, &rec); } int handle_tsc_info(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; struct xc_sr_rec_tsc_info *tsc = rec->data; if ( rec->length != sizeof(*tsc) ) { ERROR("TSC_INFO record wrong size: length %u, expected %zu", rec->length, sizeof(*tsc)); return -1; } if ( xc_domain_set_tsc_info(xch, ctx->domid, tsc->mode, tsc->nsec, tsc->khz, tsc->incarnation) ) { PERROR("Unable to set TSC information"); return -1; } return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_efi.h0000664000175000017500000001210413256712137014322 0ustar smbsmb/* * Extensible Firmware Interface * Based on 'Extensible Firmware Interface Specification' version 0.9, April 30, 1999 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (C) 1999 VA Linux Systems * Copyright (C) 1999 Walt Drummond * Copyright (C) 1999, 2002-2003 Hewlett-Packard Co. * David Mosberger-Tang * Stephane Eranian */ #ifndef XC_EFI_H #define XC_EFI_H /* definitions from xen/include/asm-ia64/linux-xen/linux/efi.h */ typedef struct { uint8_t b[16]; } efi_guid_t; #define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \ ((efi_guid_t) \ {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ (b) & 0xff, ((b) >> 8) & 0xff, \ (c) & 0xff, ((c) >> 8) & 0xff, \ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) /* * Generic EFI table header */ typedef struct { uint64_t signature; uint32_t revision; uint32_t headersize; uint32_t crc32; uint32_t reserved; } efi_table_hdr_t; /* * Memory map descriptor: */ /* Memory types: */ #define EFI_RESERVED_TYPE 0 #define EFI_LOADER_CODE 1 #define EFI_LOADER_DATA 2 #define EFI_BOOT_SERVICES_CODE 3 #define EFI_BOOT_SERVICES_DATA 4 #define EFI_RUNTIME_SERVICES_CODE 5 #define EFI_RUNTIME_SERVICES_DATA 6 #define EFI_CONVENTIONAL_MEMORY 7 #define EFI_UNUSABLE_MEMORY 8 #define EFI_ACPI_RECLAIM_MEMORY 9 #define EFI_ACPI_MEMORY_NVS 10 #define EFI_MEMORY_MAPPED_IO 11 #define EFI_MEMORY_MAPPED_IO_PORT_SPACE 12 #define EFI_PAL_CODE 13 #define EFI_MAX_MEMORY_TYPE 14 /* Attribute values: */ #define EFI_MEMORY_UC ((uint64_t)0x0000000000000001ULL) /* uncached */ #define EFI_MEMORY_WC ((uint64_t)0x0000000000000002ULL) /* write-coalescing */ #define EFI_MEMORY_WT ((uint64_t)0x0000000000000004ULL) /* write-through */ #define EFI_MEMORY_WB ((uint64_t)0x0000000000000008ULL) /* write-back */ #define EFI_MEMORY_WP ((uint64_t)0x0000000000001000ULL) /* write-protect */ #define EFI_MEMORY_RP ((uint64_t)0x0000000000002000ULL) /* read-protect */ #define EFI_MEMORY_XP ((uint64_t)0x0000000000004000ULL) /* execute-protect */ #define EFI_MEMORY_RUNTIME ((uint64_t)0x8000000000000000ULL) /* range requires runtime mapping */ #define EFI_MEMORY_DESCRIPTOR_VERSION 1 #define EFI_PAGE_SHIFT 12 /* * For current x86 implementations of EFI, there is * additional padding in the mem descriptors. This is not * the case in ia64. Need to have this fixed in the f/w. */ typedef struct { uint32_t type; uint32_t pad; uint64_t phys_addr; uint64_t virt_addr; uint64_t num_pages; uint64_t attribute; #if defined (__i386__) uint64_t pad1; #endif } efi_memory_desc_t; /* * EFI Runtime Services table */ #define EFI_RUNTIME_SERVICES_SIGNATURE ((uint64_t)0x5652453544e5552ULL) #define EFI_RUNTIME_SERVICES_REVISION 0x00010000 typedef struct { efi_table_hdr_t hdr; unsigned long get_time; unsigned long set_time; unsigned long get_wakeup_time; unsigned long set_wakeup_time; unsigned long set_virtual_address_map; unsigned long convert_pointer; unsigned long get_variable; unsigned long get_next_variable; unsigned long set_variable; unsigned long get_next_high_mono_count; unsigned long reset_system; } efi_runtime_services_t; /* * EFI Configuration Table and GUID definitions */ #define NULL_GUID \ EFI_GUID( 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) #define ACPI_20_TABLE_GUID \ EFI_GUID( 0x8868e871, 0xe4f1, 0x11d3, 0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 ) #define SAL_SYSTEM_TABLE_GUID \ EFI_GUID( 0xeb9d2d32, 0x2d88, 0x11d3, 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d ) typedef struct { efi_guid_t guid; unsigned long table; } efi_config_table_t; #define EFI_SYSTEM_TABLE_SIGNATURE ((uint64_t)0x5453595320494249ULL) #define EFI_SYSTEM_TABLE_REVISION ((1 << 16) | 00) typedef struct { efi_table_hdr_t hdr; unsigned long fw_vendor; /* physical addr of CHAR16 vendor string */ uint32_t fw_revision; unsigned long con_in_handle; unsigned long con_in; unsigned long con_out_handle; unsigned long con_out; unsigned long stderr_handle; unsigned long stderr; efi_runtime_services_t *runtime; unsigned long boottime; unsigned long nr_tables; unsigned long tables; } efi_system_table_t; #endif /* XC_EFI_H */ xen-4.9.2/tools/libxc/xc_devicemodel_compat.c0000664000175000017500000001114613256712137017402 0ustar smbsmb/* * Compat shims for use of 3rd party consumers of libxenctrl device model * functionality which has been split into separate libraries. */ #define XC_WANT_COMPAT_DEVICEMODEL_API #include "xc_private.h" int xc_hvm_create_ioreq_server( xc_interface *xch, domid_t domid, int handle_bufioreq, ioservid_t *id) { return xendevicemodel_create_ioreq_server(xch->dmod, domid, handle_bufioreq, id); } int xc_hvm_get_ioreq_server_info( xc_interface *xch, domid_t domid, ioservid_t id, xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, evtchn_port_t *bufioreq_port) { return xendevicemodel_get_ioreq_server_info(xch->dmod, domid, id, ioreq_pfn, bufioreq_pfn, bufioreq_port); } int xc_hvm_map_io_range_to_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id, int is_mmio, uint64_t start, uint64_t end) { return xendevicemodel_map_io_range_to_ioreq_server(xch->dmod, domid, id, is_mmio, start, end); } int xc_hvm_unmap_io_range_from_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id, int is_mmio, uint64_t start, uint64_t end) { return xendevicemodel_unmap_io_range_from_ioreq_server(xch->dmod, domid, id, is_mmio, start, end); } int xc_hvm_map_pcidev_to_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id, uint16_t segment, uint8_t bus, uint8_t device, uint8_t function) { return xendevicemodel_map_pcidev_to_ioreq_server(xch->dmod, domid, id, segment, bus, device, function); } int xc_hvm_unmap_pcidev_from_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id, uint16_t segment, uint8_t bus, uint8_t device, uint8_t function) { return xendevicemodel_unmap_pcidev_from_ioreq_server(xch->dmod, domid, id, segment, bus, device, function); } int xc_hvm_destroy_ioreq_server( xc_interface *xch, domid_t domid, ioservid_t id) { return xendevicemodel_destroy_ioreq_server(xch->dmod, domid, id); } int xc_hvm_set_ioreq_server_state( xc_interface *xch, domid_t domid, ioservid_t id, int enabled) { return xendevicemodel_set_ioreq_server_state(xch->dmod, domid, id, enabled); } int xc_hvm_set_pci_intx_level( xc_interface *xch, domid_t domid, uint16_t segment, uint8_t bus, uint8_t device, uint8_t intx, unsigned int level) { return xendevicemodel_set_pci_intx_level(xch->dmod, domid, segment, bus, device, intx, level); } int xc_hvm_set_isa_irq_level( xc_interface *xch, domid_t domid, uint8_t irq, unsigned int level) { return xendevicemodel_set_isa_irq_level(xch->dmod, domid, irq, level); } int xc_hvm_set_pci_link_route( xc_interface *xch, domid_t domid, uint8_t link, uint8_t irq) { return xendevicemodel_set_pci_link_route(xch->dmod, domid, link, irq); } int xc_hvm_inject_msi( xc_interface *xch, domid_t domid, uint64_t msi_addr, uint32_t msi_data) { return xendevicemodel_inject_msi(xch->dmod, domid, msi_addr, msi_data); } int xc_hvm_track_dirty_vram( xc_interface *xch, domid_t domid, uint64_t first_pfn, uint32_t nr, unsigned long *dirty_bitmap) { return xendevicemodel_track_dirty_vram(xch->dmod, domid, first_pfn, nr, dirty_bitmap); } int xc_hvm_modified_memory( xc_interface *xch, domid_t domid, uint64_t first_pfn, uint32_t nr) { return xendevicemodel_modified_memory(xch->dmod, domid, first_pfn, nr); } int xc_hvm_set_mem_type( xc_interface *xch, domid_t domid, hvmmem_type_t type, uint64_t first_pfn, uint32_t nr) { return xendevicemodel_set_mem_type(xch->dmod, domid, type, first_pfn, nr); } int xc_hvm_inject_trap( xc_interface *xch, domid_t domid, int vcpu, uint8_t vector, uint8_t type, uint32_t error_code, uint8_t insn_len, uint64_t cr2) { return xendevicemodel_inject_event(xch->dmod, domid, vcpu, vector, type, error_code, insn_len, cr2); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_foreign_memory.c0000664000175000017500000000527713256712137016610 0ustar smbsmb/****************************************************************************** * xc_foreign_memory.c * * Functions for mapping foreign domain's memory. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #define XC_BUILDING_COMPAT_MAP_FOREIGN_API #include "xc_private.h" void *xc_map_foreign_pages(xc_interface *xch, uint32_t dom, int prot, const xen_pfn_t *arr, int num) { if (num < 0) { errno = EINVAL; return NULL; } return xenforeignmemory_map(xch->fmem, dom, prot, num, arr, NULL); } void *xc_map_foreign_range(xc_interface *xch, uint32_t dom, int size, int prot, unsigned long mfn) { xen_pfn_t *arr; int num; int i; void *ret; num = (size + XC_PAGE_SIZE - 1) >> XC_PAGE_SHIFT; arr = calloc(num, sizeof(xen_pfn_t)); if ( arr == NULL ) return NULL; for ( i = 0; i < num; i++ ) arr[i] = mfn + i; ret = xc_map_foreign_pages(xch, dom, prot, arr, num); free(arr); return ret; } void *xc_map_foreign_ranges(xc_interface *xch, uint32_t dom, size_t size, int prot, size_t chunksize, privcmd_mmap_entry_t entries[], int nentries) { xen_pfn_t *arr; int num_per_entry; int num; int i; int j; void *ret; num_per_entry = chunksize >> XC_PAGE_SHIFT; num = num_per_entry * nentries; arr = calloc(num, sizeof(xen_pfn_t)); if ( arr == NULL ) return NULL; for ( i = 0; i < nentries; i++ ) for ( j = 0; j < num_per_entry; j++ ) arr[i * num_per_entry + j] = entries[i].mfn + j; ret = xc_map_foreign_pages(xch, dom, prot, arr, num); free(arr); return ret; } void *xc_map_foreign_bulk(xc_interface *xch, uint32_t dom, int prot, const xen_pfn_t *arr, int *err, unsigned int num) { return xenforeignmemory_map(xch->fmem, dom, prot, num, arr, err); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_decompress_unsafe.h0000664000175000017500000000171213256712137020126 0ustar smbsmb#include "xc_dom.h" typedef int decompress_fn(unsigned char *inbuf, unsigned int len, int (*fill)(void*, unsigned int), int (*flush)(void*, unsigned int), unsigned char *outbuf, unsigned int *posp, void (*error)(const char *x)); int xc_dom_decompress_unsafe( decompress_fn fn, struct xc_dom_image *dom, void **blob, size_t *size) __attribute__((visibility("internal"))); int xc_try_bzip2_decode(struct xc_dom_image *dom, void **blob, size_t *size) __attribute__((visibility("internal"))); int xc_try_lzma_decode(struct xc_dom_image *dom, void **blob, size_t *size) __attribute__((visibility("internal"))); int xc_try_lzo1x_decode(struct xc_dom_image *dom, void **blob, size_t *size) __attribute__((visibility("internal"))); int xc_try_xz_decode(struct xc_dom_image *dom, void **blob, size_t *size) __attribute__((visibility("internal"))); xen-4.9.2/tools/libxc/xc_misc.c0000664000175000017500000005623213256712137014517 0ustar smbsmb/****************************************************************************** * xc_misc.c * * Miscellaneous control interface functions. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_bitops.h" #include "xc_private.h" #include int xc_get_max_cpus(xc_interface *xch) { static int max_cpus = 0; xc_physinfo_t physinfo; if ( max_cpus ) return max_cpus; if ( !xc_physinfo(xch, &physinfo) ) { max_cpus = physinfo.max_cpu_id + 1; return max_cpus; } return -1; } int xc_get_online_cpus(xc_interface *xch) { xc_physinfo_t physinfo; if ( !xc_physinfo(xch, &physinfo) ) return physinfo.nr_cpus; return -1; } int xc_get_max_nodes(xc_interface *xch) { static int max_nodes = 0; xc_physinfo_t physinfo; if ( max_nodes ) return max_nodes; if ( !xc_physinfo(xch, &physinfo) ) { max_nodes = physinfo.max_node_id + 1; return max_nodes; } return -1; } int xc_get_cpumap_size(xc_interface *xch) { int max_cpus = xc_get_max_cpus(xch); if ( max_cpus < 0 ) return -1; return (max_cpus + 7) / 8; } int xc_get_nodemap_size(xc_interface *xch) { int max_nodes = xc_get_max_nodes(xch); if ( max_nodes < 0 ) return -1; return (max_nodes + 7) / 8; } xc_cpumap_t xc_cpumap_alloc(xc_interface *xch) { int sz; sz = xc_get_cpumap_size(xch); if (sz <= 0) return NULL; return calloc(1, sz); } /* * xc_bitops.h has macros that do this as well - however they assume that * the bitmask is word aligned but xc_cpumap_t is only guaranteed to be * byte aligned and so we need byte versions for architectures which do * not support misaligned accesses (which is basically everyone * but x86, although even on x86 it can be inefficient). * * NOTE: The xc_bitops macros now use byte alignment. * TODO: Clean up the users of this interface. */ #define BITS_PER_CPUMAP(map) (sizeof(*map) * 8) #define CPUMAP_ENTRY(cpu, map) ((map))[(cpu) / BITS_PER_CPUMAP(map)] #define CPUMAP_SHIFT(cpu, map) ((cpu) % BITS_PER_CPUMAP(map)) void xc_cpumap_clearcpu(int cpu, xc_cpumap_t map) { CPUMAP_ENTRY(cpu, map) &= ~(1U << CPUMAP_SHIFT(cpu, map)); } void xc_cpumap_setcpu(int cpu, xc_cpumap_t map) { CPUMAP_ENTRY(cpu, map) |= (1U << CPUMAP_SHIFT(cpu, map)); } int xc_cpumap_testcpu(int cpu, xc_cpumap_t map) { return (CPUMAP_ENTRY(cpu, map) >> CPUMAP_SHIFT(cpu, map)) & 1; } xc_nodemap_t xc_nodemap_alloc(xc_interface *xch) { int sz; sz = xc_get_nodemap_size(xch); if (sz <= 0) return NULL; return calloc(1, sz); } int xc_readconsolering(xc_interface *xch, char *buffer, unsigned int *pnr_chars, int clear, int incremental, uint32_t *pindex) { int ret; unsigned int nr_chars = *pnr_chars; DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(buffer, nr_chars, XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( xc_hypercall_bounce_pre(xch, buffer) ) return -1; sysctl.cmd = XEN_SYSCTL_readconsole; set_xen_guest_handle(sysctl.u.readconsole.buffer, buffer); sysctl.u.readconsole.count = nr_chars; sysctl.u.readconsole.clear = clear; sysctl.u.readconsole.incremental = 0; if ( pindex ) { sysctl.u.readconsole.index = *pindex; sysctl.u.readconsole.incremental = incremental; } if ( (ret = do_sysctl(xch, &sysctl)) == 0 ) { *pnr_chars = sysctl.u.readconsole.count; if ( pindex ) *pindex = sysctl.u.readconsole.index; } xc_hypercall_bounce_post(xch, buffer); return ret; } int xc_send_debug_keys(xc_interface *xch, char *keys) { int ret, len = strlen(keys); DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(keys, len, XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, keys) ) return -1; sysctl.cmd = XEN_SYSCTL_debug_keys; set_xen_guest_handle(sysctl.u.debug_keys.keys, keys); sysctl.u.debug_keys.nr_keys = len; ret = do_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, keys); return ret; } int xc_physinfo(xc_interface *xch, xc_physinfo_t *put_info) { int ret; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_physinfo; memcpy(&sysctl.u.physinfo, put_info, sizeof(*put_info)); if ( (ret = do_sysctl(xch, &sysctl)) != 0 ) return ret; memcpy(put_info, &sysctl.u.physinfo, sizeof(*put_info)); return 0; } int xc_cputopoinfo(xc_interface *xch, unsigned *max_cpus, xc_cputopo_t *cputopo) { int ret; DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(cputopo, *max_cpus * sizeof(*cputopo), XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( (ret = xc_hypercall_bounce_pre(xch, cputopo)) ) goto out; sysctl.u.cputopoinfo.num_cpus = *max_cpus; set_xen_guest_handle(sysctl.u.cputopoinfo.cputopo, cputopo); sysctl.cmd = XEN_SYSCTL_cputopoinfo; if ( (ret = do_sysctl(xch, &sysctl)) != 0 ) goto out; *max_cpus = sysctl.u.cputopoinfo.num_cpus; out: xc_hypercall_bounce_post(xch, cputopo); return ret; } int xc_numainfo(xc_interface *xch, unsigned *max_nodes, xc_meminfo_t *meminfo, uint32_t *distance) { int ret; DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(meminfo, *max_nodes * sizeof(*meminfo), XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_HYPERCALL_BOUNCE(distance, *max_nodes * *max_nodes * sizeof(*distance), XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( (ret = xc_hypercall_bounce_pre(xch, meminfo)) ) goto out; if ((ret = xc_hypercall_bounce_pre(xch, distance)) ) goto out; sysctl.u.numainfo.num_nodes = *max_nodes; set_xen_guest_handle(sysctl.u.numainfo.meminfo, meminfo); set_xen_guest_handle(sysctl.u.numainfo.distance, distance); sysctl.cmd = XEN_SYSCTL_numainfo; if ( (ret = do_sysctl(xch, &sysctl)) != 0 ) goto out; *max_nodes = sysctl.u.numainfo.num_nodes; out: xc_hypercall_bounce_post(xch, meminfo); xc_hypercall_bounce_post(xch, distance); return ret; } int xc_pcitopoinfo(xc_interface *xch, unsigned num_devs, physdev_pci_device_t *devs, uint32_t *nodes) { int ret = 0; unsigned processed = 0; DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(devs, num_devs * sizeof(*devs), XC_HYPERCALL_BUFFER_BOUNCE_IN); DECLARE_HYPERCALL_BOUNCE(nodes, num_devs* sizeof(*nodes), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); if ( (ret = xc_hypercall_bounce_pre(xch, devs)) ) goto out; if ( (ret = xc_hypercall_bounce_pre(xch, nodes)) ) goto out; sysctl.cmd = XEN_SYSCTL_pcitopoinfo; while ( processed < num_devs ) { sysctl.u.pcitopoinfo.num_devs = num_devs - processed; set_xen_guest_handle_offset(sysctl.u.pcitopoinfo.devs, devs, processed); set_xen_guest_handle_offset(sysctl.u.pcitopoinfo.nodes, nodes, processed); if ( (ret = do_sysctl(xch, &sysctl)) != 0 ) break; processed += sysctl.u.pcitopoinfo.num_devs; } out: xc_hypercall_bounce_post(xch, devs); xc_hypercall_bounce_post(xch, nodes); return ret; } int xc_sched_id(xc_interface *xch, int *sched_id) { int ret; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_sched_id; if ( (ret = do_sysctl(xch, &sysctl)) != 0 ) return ret; *sched_id = sysctl.u.sched_id.sched_id; return 0; } #if defined(__i386__) || defined(__x86_64__) int xc_mca_op(xc_interface *xch, struct xen_mc *mc) { int ret = 0; DECLARE_HYPERCALL_BOUNCE(mc, sizeof(*mc), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); if ( xc_hypercall_bounce_pre(xch, mc) ) { PERROR("Could not bounce xen_mc memory buffer"); return -1; } mc->interface_version = XEN_MCA_INTERFACE_VERSION; ret = xencall1(xch->xcall, __HYPERVISOR_mca, HYPERCALL_BUFFER_AS_ARG(mc)); xc_hypercall_bounce_post(xch, mc); return ret; } #endif int xc_perfc_reset(xc_interface *xch) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_perfc_op; sysctl.u.perfc_op.cmd = XEN_SYSCTL_PERFCOP_reset; set_xen_guest_handle(sysctl.u.perfc_op.desc, HYPERCALL_BUFFER_NULL); set_xen_guest_handle(sysctl.u.perfc_op.val, HYPERCALL_BUFFER_NULL); return do_sysctl(xch, &sysctl); } int xc_perfc_query_number(xc_interface *xch, int *nbr_desc, int *nbr_val) { int rc; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_perfc_op; sysctl.u.perfc_op.cmd = XEN_SYSCTL_PERFCOP_query; set_xen_guest_handle(sysctl.u.perfc_op.desc, HYPERCALL_BUFFER_NULL); set_xen_guest_handle(sysctl.u.perfc_op.val, HYPERCALL_BUFFER_NULL); rc = do_sysctl(xch, &sysctl); if ( nbr_desc ) *nbr_desc = sysctl.u.perfc_op.nr_counters; if ( nbr_val ) *nbr_val = sysctl.u.perfc_op.nr_vals; return rc; } int xc_perfc_query(xc_interface *xch, struct xc_hypercall_buffer *desc, struct xc_hypercall_buffer *val) { DECLARE_SYSCTL; DECLARE_HYPERCALL_BUFFER_ARGUMENT(desc); DECLARE_HYPERCALL_BUFFER_ARGUMENT(val); sysctl.cmd = XEN_SYSCTL_perfc_op; sysctl.u.perfc_op.cmd = XEN_SYSCTL_PERFCOP_query; set_xen_guest_handle(sysctl.u.perfc_op.desc, desc); set_xen_guest_handle(sysctl.u.perfc_op.val, val); return do_sysctl(xch, &sysctl); } int xc_lockprof_reset(xc_interface *xch) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_lockprof_op; sysctl.u.lockprof_op.cmd = XEN_SYSCTL_LOCKPROF_reset; set_xen_guest_handle(sysctl.u.lockprof_op.data, HYPERCALL_BUFFER_NULL); return do_sysctl(xch, &sysctl); } int xc_lockprof_query_number(xc_interface *xch, uint32_t *n_elems) { int rc; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_lockprof_op; sysctl.u.lockprof_op.max_elem = 0; sysctl.u.lockprof_op.cmd = XEN_SYSCTL_LOCKPROF_query; set_xen_guest_handle(sysctl.u.lockprof_op.data, HYPERCALL_BUFFER_NULL); rc = do_sysctl(xch, &sysctl); *n_elems = sysctl.u.lockprof_op.nr_elem; return rc; } int xc_lockprof_query(xc_interface *xch, uint32_t *n_elems, uint64_t *time, struct xc_hypercall_buffer *data) { int rc; DECLARE_SYSCTL; DECLARE_HYPERCALL_BUFFER_ARGUMENT(data); sysctl.cmd = XEN_SYSCTL_lockprof_op; sysctl.u.lockprof_op.cmd = XEN_SYSCTL_LOCKPROF_query; sysctl.u.lockprof_op.max_elem = *n_elems; set_xen_guest_handle(sysctl.u.lockprof_op.data, data); rc = do_sysctl(xch, &sysctl); *n_elems = sysctl.u.lockprof_op.nr_elem; return rc; } int xc_getcpuinfo(xc_interface *xch, int max_cpus, xc_cpuinfo_t *info, int *nr_cpus) { int rc; DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(info, max_cpus*sizeof(*info), XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( xc_hypercall_bounce_pre(xch, info) ) return -1; sysctl.cmd = XEN_SYSCTL_getcpuinfo; sysctl.u.getcpuinfo.max_cpus = max_cpus; set_xen_guest_handle(sysctl.u.getcpuinfo.info, info); rc = do_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, info); if ( nr_cpus ) *nr_cpus = sysctl.u.getcpuinfo.nr_cpus; return rc; } int xc_livepatch_upload(xc_interface *xch, char *name, unsigned char *payload, uint32_t size) { int rc; DECLARE_SYSCTL; DECLARE_HYPERCALL_BUFFER(char, local); DECLARE_HYPERCALL_BOUNCE(name, 0 /* later */, XC_HYPERCALL_BUFFER_BOUNCE_IN); xen_livepatch_name_t def_name = { .pad = { 0, 0, 0 } }; if ( !name || !payload ) { errno = EINVAL; return -1; } def_name.size = strlen(name) + 1; if ( def_name.size > XEN_LIVEPATCH_NAME_SIZE ) { errno = EINVAL; return -1; } HYPERCALL_BOUNCE_SET_SIZE(name, def_name.size); if ( xc_hypercall_bounce_pre(xch, name) ) return -1; local = xc_hypercall_buffer_alloc(xch, local, size); if ( !local ) { xc_hypercall_bounce_post(xch, name); return -1; } memcpy(local, payload, size); sysctl.cmd = XEN_SYSCTL_livepatch_op; sysctl.u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_UPLOAD; sysctl.u.livepatch.pad = 0; sysctl.u.livepatch.u.upload.size = size; set_xen_guest_handle(sysctl.u.livepatch.u.upload.payload, local); sysctl.u.livepatch.u.upload.name = def_name; set_xen_guest_handle(sysctl.u.livepatch.u.upload.name.name, name); rc = do_sysctl(xch, &sysctl); xc_hypercall_buffer_free(xch, local); xc_hypercall_bounce_post(xch, name); return rc; } int xc_livepatch_get(xc_interface *xch, char *name, xen_livepatch_status_t *status) { int rc; DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(name, 0 /*adjust later */, XC_HYPERCALL_BUFFER_BOUNCE_IN); xen_livepatch_name_t def_name = { .pad = { 0, 0, 0 } }; if ( !name ) { errno = EINVAL; return -1; } def_name.size = strlen(name) + 1; if ( def_name.size > XEN_LIVEPATCH_NAME_SIZE ) { errno = EINVAL; return -1; } HYPERCALL_BOUNCE_SET_SIZE(name, def_name.size); if ( xc_hypercall_bounce_pre(xch, name) ) return -1; sysctl.cmd = XEN_SYSCTL_livepatch_op; sysctl.u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_GET; sysctl.u.livepatch.pad = 0; sysctl.u.livepatch.u.get.status.state = 0; sysctl.u.livepatch.u.get.status.rc = 0; sysctl.u.livepatch.u.get.name = def_name; set_xen_guest_handle(sysctl.u.livepatch.u.get.name.name, name); rc = do_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, name); memcpy(status, &sysctl.u.livepatch.u.get.status, sizeof(*status)); return rc; } /* * The heart of this function is to get an array of xen_livepatch_status_t. * * However it is complex because it has to deal with the hypervisor * returning some of the requested data or data being stale * (another hypercall might alter the list). * * The parameters that the function expects to contain data from * the hypervisor are: 'info', 'name', and 'len'. The 'done' and * 'left' are also updated with the number of entries filled out * and respectively the number of entries left to get from hypervisor. * * It is expected that the caller of this function will take the * 'left' and use the value for 'start'. This way we have an * cursor in the array. Note that the 'info','name', and 'len' will * be updated at the subsequent calls. * * The 'max' is to be provided by the caller with the maximum * number of entries that 'info', 'name', and 'len' arrays can * be filled up with. * * Each entry in the 'name' array is expected to be of XEN_LIVEPATCH_NAME_SIZE * length. * * Each entry in the 'info' array is expected to be of xen_livepatch_status_t * structure size. * * Each entry in the 'len' array is expected to be of uint32_t size. * * The return value is zero if the hypercall completed successfully. * Note that the return value is _not_ the amount of entries filled * out - that is saved in 'done'. * * If there was an error performing the operation, the return value * will contain an negative -EXX type value. The 'done' and 'left' * will contain the number of entries that had been succesfully * retrieved (if any). */ int xc_livepatch_list(xc_interface *xch, unsigned int max, unsigned int start, xen_livepatch_status_t *info, char *name, uint32_t *len, unsigned int *done, unsigned int *left) { int rc; DECLARE_SYSCTL; /* The sizes are adjusted later - hence zero. */ DECLARE_HYPERCALL_BOUNCE(info, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_HYPERCALL_BOUNCE(name, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_HYPERCALL_BOUNCE(len, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); uint32_t max_batch_sz, nr; uint32_t version = 0, retries = 0; uint32_t adjust = 0; ssize_t sz; if ( !max || !info || !name || !len ) { errno = EINVAL; return -1; } sysctl.cmd = XEN_SYSCTL_livepatch_op; sysctl.u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_LIST; sysctl.u.livepatch.pad = 0; sysctl.u.livepatch.u.list.version = 0; sysctl.u.livepatch.u.list.idx = start; sysctl.u.livepatch.u.list.pad = 0; max_batch_sz = max; /* Convience value. */ sz = sizeof(*name) * XEN_LIVEPATCH_NAME_SIZE; *done = 0; *left = 0; do { /* * The first time we go in this loop our 'max' may be bigger * than what the hypervisor is comfortable with - hence the first * couple of loops may adjust the number of entries we will * want filled (tracked by 'nr'). * * N.B. This is a do { } while loop and the right hand side of * the conditional when adjusting will evaluate to false (as * *left is set to zero before the loop. Hence we need this * adjust - even if we reset it at the start of the loop. */ if ( adjust ) adjust = 0; /* Used when adjusting the 'max_batch_sz' or 'retries'. */ nr = min(max - *done, max_batch_sz); sysctl.u.livepatch.u.list.nr = nr; /* Fix the size (may vary between hypercalls). */ HYPERCALL_BOUNCE_SET_SIZE(info, nr * sizeof(*info)); HYPERCALL_BOUNCE_SET_SIZE(name, nr * nr); HYPERCALL_BOUNCE_SET_SIZE(len, nr * sizeof(*len)); /* Move the pointer to proper offset into 'info'. */ (HYPERCALL_BUFFER(info))->ubuf = info + *done; (HYPERCALL_BUFFER(name))->ubuf = name + (sz * *done); (HYPERCALL_BUFFER(len))->ubuf = len + *done; /* Allocate memory. */ rc = xc_hypercall_bounce_pre(xch, info); if ( rc ) break; rc = xc_hypercall_bounce_pre(xch, name); if ( rc ) break; rc = xc_hypercall_bounce_pre(xch, len); if ( rc ) break; set_xen_guest_handle(sysctl.u.livepatch.u.list.status, info); set_xen_guest_handle(sysctl.u.livepatch.u.list.name, name); set_xen_guest_handle(sysctl.u.livepatch.u.list.len, len); rc = do_sysctl(xch, &sysctl); /* * From here on we MUST call xc_hypercall_bounce. If rc < 0 we * end up doing it (outside the loop), so using a break is OK. */ if ( rc < 0 && errno == E2BIG ) { if ( max_batch_sz <= 1 ) break; max_batch_sz >>= 1; adjust = 1; /* For the loop conditional to let us loop again. */ /* No memory leaks! */ xc_hypercall_bounce_post(xch, info); xc_hypercall_bounce_post(xch, name); xc_hypercall_bounce_post(xch, len); continue; } else if ( rc < 0 ) /* For all other errors we bail out. */ break; if ( !version ) version = sysctl.u.livepatch.u.list.version; if ( sysctl.u.livepatch.u.list.version != version ) { /* We could make this configurable as parameter? */ if ( retries++ > 3 ) { rc = -1; errno = EBUSY; break; } *done = 0; /* Retry from scratch. */ version = sysctl.u.livepatch.u.list.version; adjust = 1; /* And make sure we continue in the loop. */ /* No memory leaks. */ xc_hypercall_bounce_post(xch, info); xc_hypercall_bounce_post(xch, name); xc_hypercall_bounce_post(xch, len); continue; } /* We should never hit this, but just in case. */ if ( rc > nr ) { errno = EOVERFLOW; /* Overflow! */ rc = -1; break; } *left = sysctl.u.livepatch.u.list.nr; /* Total remaining count. */ /* Copy only up 'rc' of data' - we could add 'min(rc,nr) if desired. */ HYPERCALL_BOUNCE_SET_SIZE(info, (rc * sizeof(*info))); HYPERCALL_BOUNCE_SET_SIZE(name, (rc * sz)); HYPERCALL_BOUNCE_SET_SIZE(len, (rc * sizeof(*len))); /* Bounce the data and free the bounce buffer. */ xc_hypercall_bounce_post(xch, info); xc_hypercall_bounce_post(xch, name); xc_hypercall_bounce_post(xch, len); /* And update how many elements of info we have copied into. */ *done += rc; /* Update idx. */ sysctl.u.livepatch.u.list.idx = *done; } while ( adjust || (*done < max && *left != 0) ); if ( rc < 0 ) { xc_hypercall_bounce_post(xch, len); xc_hypercall_bounce_post(xch, name); xc_hypercall_bounce_post(xch, info); } return rc > 0 ? 0 : rc; } static int _xc_livepatch_action(xc_interface *xch, char *name, unsigned int action, uint32_t timeout) { int rc; DECLARE_SYSCTL; /* The size is figured out when we strlen(name) */ DECLARE_HYPERCALL_BOUNCE(name, 0, XC_HYPERCALL_BUFFER_BOUNCE_IN); xen_livepatch_name_t def_name = { .pad = { 0, 0, 0 } }; def_name.size = strlen(name) + 1; if ( def_name.size > XEN_LIVEPATCH_NAME_SIZE ) { errno = EINVAL; return -1; } HYPERCALL_BOUNCE_SET_SIZE(name, def_name.size); if ( xc_hypercall_bounce_pre(xch, name) ) return -1; sysctl.cmd = XEN_SYSCTL_livepatch_op; sysctl.u.livepatch.cmd = XEN_SYSCTL_LIVEPATCH_ACTION; sysctl.u.livepatch.pad = 0; sysctl.u.livepatch.u.action.cmd = action; sysctl.u.livepatch.u.action.timeout = timeout; sysctl.u.livepatch.u.action.name = def_name; set_xen_guest_handle(sysctl.u.livepatch.u.action.name.name, name); rc = do_sysctl(xch, &sysctl); xc_hypercall_bounce_post(xch, name); return rc; } int xc_livepatch_apply(xc_interface *xch, char *name, uint32_t timeout) { return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_APPLY, timeout); } int xc_livepatch_revert(xc_interface *xch, char *name, uint32_t timeout) { return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_REVERT, timeout); } int xc_livepatch_unload(xc_interface *xch, char *name, uint32_t timeout) { return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_UNLOAD, timeout); } int xc_livepatch_replace(xc_interface *xch, char *name, uint32_t timeout) { return _xc_livepatch_action(xch, name, LIVEPATCH_ACTION_REPLACE, timeout); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_flask.c0000664000175000017500000002565113256712137014665 0ustar smbsmb/****************************************************************************** * xc_flask.c * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include "xc_private.h" #include #include #include #include #include #include #include #include #include #include #define OCON_ISID 0 /* initial SIDs */ #define OCON_PIRQ 1 /* physical irqs */ #define OCON_IOPORT 2 /* io ports */ #define OCON_IOMEM 3 /* io memory */ #define OCON_DEVICE 4 /* pci devices */ #define INITCONTEXTLEN 256 int xc_flask_op(xc_interface *xch, xen_flask_op_t *op) { int ret = -1; DECLARE_HYPERCALL_BOUNCE(op, sizeof(*op), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); op->interface_version = XEN_FLASK_INTERFACE_VERSION; if ( xc_hypercall_bounce_pre(xch, op) ) { PERROR("Could not bounce memory for flask op hypercall"); goto out; } ret = xencall1(xch->xcall, __HYPERVISOR_xsm_op, HYPERCALL_BUFFER_AS_ARG(op)); if ( ret < 0 ) { if ( errno == EACCES ) fprintf(stderr, "XSM operation failed!\n"); } xc_hypercall_bounce_post(xch, op); out: return ret; } int xc_flask_load(xc_interface *xch, char *buf, uint32_t size) { int err; DECLARE_FLASK_OP; DECLARE_HYPERCALL_BOUNCE(buf, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, buf) ) { PERROR("Could not bounce memory for flask op hypercall"); return -1; } op.cmd = FLASK_LOAD; op.u.load.size = size; set_xen_guest_handle(op.u.load.buffer, buf); err = xc_flask_op(xch, &op); xc_hypercall_bounce_post(xch, buf); return err; } int xc_flask_context_to_sid(xc_interface *xch, char *buf, uint32_t size, uint32_t *sid) { int err; DECLARE_FLASK_OP; DECLARE_HYPERCALL_BOUNCE(buf, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, buf) ) { PERROR("Could not bounce memory for flask op hypercall"); return -1; } op.cmd = FLASK_CONTEXT_TO_SID; op.u.sid_context.size = size; set_xen_guest_handle(op.u.sid_context.context, buf); err = xc_flask_op(xch, &op); if ( !err ) *sid = op.u.sid_context.sid; xc_hypercall_bounce_post(xch, buf); return err; } int xc_flask_sid_to_context(xc_interface *xch, int sid, char *buf, uint32_t size) { int err; DECLARE_FLASK_OP; DECLARE_HYPERCALL_BOUNCE(buf, size, XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( xc_hypercall_bounce_pre(xch, buf) ) { PERROR("Could not bounce memory for flask op hypercall"); return -1; } op.cmd = FLASK_SID_TO_CONTEXT; op.u.sid_context.sid = sid; op.u.sid_context.size = size; set_xen_guest_handle(op.u.sid_context.context, buf); err = xc_flask_op(xch, &op); xc_hypercall_bounce_post(xch, buf); return err; } int xc_flask_getenforce(xc_interface *xch) { DECLARE_FLASK_OP; op.cmd = FLASK_GETENFORCE; return xc_flask_op(xch, &op); } int xc_flask_setenforce(xc_interface *xch, int mode) { DECLARE_FLASK_OP; op.cmd = FLASK_SETENFORCE; op.u.enforce.enforcing = mode; return xc_flask_op(xch, &op); } int xc_flask_getbool_byid(xc_interface *xch, int id, char *name, uint32_t size, int *curr, int *pend) { int rv; DECLARE_FLASK_OP; DECLARE_HYPERCALL_BOUNCE(name, size, XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( xc_hypercall_bounce_pre(xch, name) ) { PERROR("Could not bounce memory for flask op hypercall"); return -1; } op.cmd = FLASK_GETBOOL; op.u.boolean.bool_id = id; op.u.boolean.size = size; set_xen_guest_handle(op.u.boolean.name, name); rv = xc_flask_op(xch, &op); xc_hypercall_bounce_post(xch, name); if ( rv ) return rv; if ( curr ) *curr = op.u.boolean.enforcing; if ( pend ) *pend = op.u.boolean.pending; return rv; } int xc_flask_getbool_byname(xc_interface *xch, char *name, int *curr, int *pend) { int rv; DECLARE_FLASK_OP; DECLARE_HYPERCALL_BOUNCE(name, strlen(name), XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, name) ) { PERROR("Could not bounce memory for flask op hypercall"); return -1; } op.cmd = FLASK_GETBOOL; op.u.boolean.bool_id = -1; op.u.boolean.size = strlen(name); set_xen_guest_handle(op.u.boolean.name, name); rv = xc_flask_op(xch, &op); xc_hypercall_bounce_post(xch, name); if ( rv ) return rv; if ( curr ) *curr = op.u.boolean.enforcing; if ( pend ) *pend = op.u.boolean.pending; return rv; } int xc_flask_setbool(xc_interface *xch, char *name, int value, int commit) { int rv; DECLARE_FLASK_OP; DECLARE_HYPERCALL_BOUNCE(name, strlen(name), XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, name) ) { PERROR("Could not bounce memory for flask op hypercall"); return -1; } op.cmd = FLASK_SETBOOL; op.u.boolean.bool_id = -1; op.u.boolean.new_value = value; op.u.boolean.commit = 1; op.u.boolean.size = strlen(name); set_xen_guest_handle(op.u.boolean.name, name); rv = xc_flask_op(xch, &op); xc_hypercall_bounce_post(xch, name); return rv; } static int xc_flask_add(xc_interface *xch, uint32_t ocon, uint64_t low, uint64_t high, char *scontext) { uint32_t sid; int err; DECLARE_FLASK_OP; err = xc_flask_context_to_sid(xch, scontext, strlen(scontext), &sid); if ( err ) return err; op.cmd = FLASK_ADD_OCONTEXT; op.u.ocontext.ocon = ocon; op.u.ocontext.sid = sid; op.u.ocontext.low = low; op.u.ocontext.high = high; return xc_flask_op(xch, &op); } int xc_flask_add_pirq(xc_interface *xch, unsigned int pirq, char *scontext) { return xc_flask_add(xch, OCON_PIRQ, pirq, pirq, scontext); } int xc_flask_add_ioport(xc_interface *xch, unsigned long low, unsigned long high, char *scontext) { return xc_flask_add(xch, OCON_IOPORT, low, high, scontext); } int xc_flask_add_iomem(xc_interface *xch, unsigned long low, unsigned long high, char *scontext) { return xc_flask_add(xch, OCON_IOMEM, low, high, scontext); } int xc_flask_add_device(xc_interface *xch, unsigned long device, char *scontext) { return xc_flask_add(xch, OCON_DEVICE, device, device, scontext); } static int xc_flask_del(xc_interface *xch, uint32_t ocon, uint64_t low, uint64_t high) { DECLARE_FLASK_OP; op.cmd = FLASK_DEL_OCONTEXT; op.u.ocontext.ocon = ocon; op.u.ocontext.low = low; op.u.ocontext.high = high; return xc_flask_op(xch, &op); } int xc_flask_del_pirq(xc_interface *xch, unsigned int pirq) { return xc_flask_del(xch, OCON_PIRQ, pirq, pirq); } int xc_flask_del_ioport(xc_interface *xch, unsigned long low, unsigned long high) { return xc_flask_del(xch, OCON_IOPORT, low, high); } int xc_flask_del_iomem(xc_interface *xch, unsigned long low, unsigned long high) { return xc_flask_del(xch, OCON_IOMEM, low, high); } int xc_flask_del_device(xc_interface *xch, unsigned long device) { return xc_flask_del(xch, OCON_DEVICE, device, device); } int xc_flask_access(xc_interface *xch, const char *scon, const char *tcon, uint16_t tclass, uint32_t req, uint32_t *allowed, uint32_t *decided, uint32_t *auditallow, uint32_t *auditdeny, uint32_t *seqno) { DECLARE_FLASK_OP; int err; err = xc_flask_context_to_sid(xch, (char*)scon, strlen(scon), &op.u.access.ssid); if ( err ) return err; err = xc_flask_context_to_sid(xch, (char*)tcon, strlen(tcon), &op.u.access.tsid); if ( err ) return err; op.cmd = FLASK_ACCESS; op.u.access.tclass = tclass; op.u.access.req = req; err = xc_flask_op(xch, &op); if ( err ) return err; if ( allowed ) *allowed = op.u.access.allowed; if ( decided ) *decided = 0xffffffff; if ( auditallow ) *auditallow = op.u.access.audit_allow; if ( auditdeny ) *auditdeny = op.u.access.audit_deny; if ( seqno ) *seqno = op.u.access.seqno; if ( (op.u.access.allowed & req) != req ) err = -EPERM; return err; } int xc_flask_avc_hashstats(xc_interface *xch, char *buf, int size) { int err; DECLARE_FLASK_OP; op.cmd = FLASK_AVC_HASHSTATS; err = xc_flask_op(xch, &op); snprintf(buf, size, "entries: %d\nbuckets used: %d/%d\nlongest chain: %d\n", op.u.hash_stats.entries, op.u.hash_stats.buckets_used, op.u.hash_stats.buckets_total, op.u.hash_stats.max_chain_len); return err; } int xc_flask_avc_cachestats(xc_interface *xch, char *buf, int size) { int err, n; int i = 0; DECLARE_FLASK_OP; n = snprintf(buf, size, "lookups hits misses allocations reclaims frees\n"); buf += n; size -= n; op.cmd = FLASK_AVC_CACHESTATS; while ( size > 0 ) { op.u.cache_stats.cpu = i; err = xc_flask_op(xch, &op); if ( err && errno == ENOENT ) return 0; if ( err ) return err; n = snprintf(buf, size, "%u %u %u %u %u %u\n", op.u.cache_stats.lookups, op.u.cache_stats.hits, op.u.cache_stats.misses, op.u.cache_stats.allocations, op.u.cache_stats.reclaims, op.u.cache_stats.frees); buf += n; size -= n; i++; } return 0; } int xc_flask_policyvers(xc_interface *xch) { DECLARE_FLASK_OP; op.cmd = FLASK_POLICYVERS; return xc_flask_op(xch, &op); } int xc_flask_getavc_threshold(xc_interface *xch) { DECLARE_FLASK_OP; op.cmd = FLASK_GETAVC_THRESHOLD; return xc_flask_op(xch, &op); } int xc_flask_setavc_threshold(xc_interface *xch, int threshold) { DECLARE_FLASK_OP; op.cmd = FLASK_SETAVC_THRESHOLD; op.u.setavc_threshold.threshold = threshold; return xc_flask_op(xch, &op); } int xc_flask_relabel_domain(xc_interface *xch, int domid, uint32_t sid) { DECLARE_FLASK_OP; op.cmd = FLASK_RELABEL_DOMAIN; op.u.relabel.domid = domid; op.u.relabel.sid = sid; return xc_flask_op(xch, &op); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_restore_x86_hvm.c0000664000175000017500000001447713256712137017337 0ustar smbsmb#include #include #include "xc_sr_common_x86.h" /* * Process an HVM_CONTEXT record from the stream. */ static int handle_hvm_context(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; void *p; p = malloc(rec->length); if ( !p ) { ERROR("Unable to allocate %u bytes for hvm context", rec->length); return -1; } free(ctx->x86_hvm.restore.context); ctx->x86_hvm.restore.context = memcpy(p, rec->data, rec->length); ctx->x86_hvm.restore.contextsz = rec->length; return 0; } /* * Process an HVM_PARAMS record from the stream. */ static int handle_hvm_params(struct xc_sr_context *ctx, struct xc_sr_record *rec) { xc_interface *xch = ctx->xch; struct xc_sr_rec_hvm_params *hdr = rec->data; struct xc_sr_rec_hvm_params_entry *entry = hdr->param; unsigned int i; int rc; if ( rec->length < sizeof(*hdr) ) { ERROR("HVM_PARAMS record truncated: length %u, header size %zu", rec->length, sizeof(*hdr)); return -1; } if ( rec->length != (sizeof(*hdr) + hdr->count * sizeof(*entry)) ) { ERROR("HVM_PARAMS record truncated: header %zu, count %u, " "expected len %zu, got %u", sizeof(*hdr), hdr->count, hdr->count * sizeof(*entry), rec->length); return -1; } /* * Tolerate empty records. Older sending sides used to accidentally * generate them. */ if ( hdr->count == 0 ) { DBGPRINTF("Skipping empty HVM_PARAMS record\n"); return 0; } for ( i = 0; i < hdr->count; i++, entry++ ) { switch ( entry->index ) { case HVM_PARAM_CONSOLE_PFN: ctx->restore.console_gfn = entry->value; xc_clear_domain_page(xch, ctx->domid, entry->value); break; case HVM_PARAM_STORE_PFN: ctx->restore.xenstore_gfn = entry->value; xc_clear_domain_page(xch, ctx->domid, entry->value); break; case HVM_PARAM_IOREQ_PFN: case HVM_PARAM_BUFIOREQ_PFN: xc_clear_domain_page(xch, ctx->domid, entry->value); break; } rc = xc_hvm_param_set(xch, ctx->domid, entry->index, entry->value); if ( rc < 0 ) { PERROR("set HVM param %"PRId64" = 0x%016"PRIx64, entry->index, entry->value); return rc; } } return 0; } /* restore_ops function. */ static bool x86_hvm_pfn_is_valid(const struct xc_sr_context *ctx, xen_pfn_t pfn) { return true; } /* restore_ops function. */ static xen_pfn_t x86_hvm_pfn_to_gfn(const struct xc_sr_context *ctx, xen_pfn_t pfn) { return pfn; } /* restore_ops function. */ static void x86_hvm_set_gfn(struct xc_sr_context *ctx, xen_pfn_t pfn, xen_pfn_t gfn) { /* no op */ } /* restore_ops function. */ static void x86_hvm_set_page_type(struct xc_sr_context *ctx, xen_pfn_t pfn, xen_pfn_t type) { /* no-op */ } /* restore_ops function. */ static int x86_hvm_localise_page(struct xc_sr_context *ctx, uint32_t type, void *page) { /* no-op */ return 0; } /* * restore_ops function. Confirms the stream matches the domain. */ static int x86_hvm_setup(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; if ( ctx->restore.guest_type != DHDR_TYPE_X86_HVM ) { ERROR("Unable to restore %s domain into an x86_hvm domain", dhdr_type_to_str(ctx->restore.guest_type)); return -1; } else if ( ctx->restore.guest_page_size != PAGE_SIZE ) { ERROR("Invalid page size %u for x86_hvm domains", ctx->restore.guest_page_size); return -1; } return 0; } /* * restore_ops function. */ static int x86_hvm_process_record(struct xc_sr_context *ctx, struct xc_sr_record *rec) { switch ( rec->type ) { case REC_TYPE_TSC_INFO: return handle_tsc_info(ctx, rec); case REC_TYPE_HVM_CONTEXT: return handle_hvm_context(ctx, rec); case REC_TYPE_HVM_PARAMS: return handle_hvm_params(ctx, rec); default: return RECORD_NOT_PROCESSED; } } /* * restore_ops function. Sets extra hvm parameters and seeds the grant table. */ static int x86_hvm_stream_complete(struct xc_sr_context *ctx) { xc_interface *xch = ctx->xch; int rc; rc = xc_hvm_param_set(xch, ctx->domid, HVM_PARAM_STORE_EVTCHN, ctx->restore.xenstore_evtchn); if ( rc ) { PERROR("Failed to set HVM_PARAM_STORE_EVTCHN"); return rc; } rc = xc_hvm_param_set(xch, ctx->domid, HVM_PARAM_CONSOLE_EVTCHN, ctx->restore.console_evtchn); if ( rc ) { PERROR("Failed to set HVM_PARAM_CONSOLE_EVTCHN"); return rc; } rc = xc_domain_hvm_setcontext(xch, ctx->domid, ctx->x86_hvm.restore.context, ctx->x86_hvm.restore.contextsz); if ( rc < 0 ) { PERROR("Unable to restore HVM context"); return rc; } rc = xc_dom_gnttab_hvm_seed(xch, ctx->domid, ctx->restore.console_gfn, ctx->restore.xenstore_gfn, ctx->restore.console_domid, ctx->restore.xenstore_domid); if ( rc ) { PERROR("Failed to seed grant table"); return rc; } return rc; } static int x86_hvm_cleanup(struct xc_sr_context *ctx) { free(ctx->x86_hvm.restore.context); return 0; } struct xc_sr_restore_ops restore_ops_x86_hvm = { .pfn_is_valid = x86_hvm_pfn_is_valid, .pfn_to_gfn = x86_hvm_pfn_to_gfn, .set_gfn = x86_hvm_set_gfn, .set_page_type = x86_hvm_set_page_type, .localise_page = x86_hvm_localise_page, .setup = x86_hvm_setup, .process_record = x86_hvm_process_record, .stream_complete = x86_hvm_stream_complete, .cleanup = x86_hvm_cleanup, }; /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_core_x86.c0000664000175000017500000001560113256712137015214 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (c) 2007 Isaku Yamahata * VA Linux Systems Japan K.K. * */ #include "xg_private.h" #include "xc_core.h" #include #define GET_FIELD(_p, _f) ((dinfo->guest_width==8) ? ((_p)->x64._f) : ((_p)->x32._f)) int xc_core_arch_gpfn_may_present(struct xc_core_arch_context *arch_ctxt, unsigned long pfn) { if ((pfn >= 0xa0 && pfn < 0xc0) /* VGA hole */ || (pfn >= (HVM_BELOW_4G_MMIO_START >> PAGE_SHIFT) && pfn < (1ULL<<32) >> PAGE_SHIFT)) /* MMIO */ return 0; return 1; } int xc_core_arch_auto_translated_physmap(const xc_dominfo_t *info) { return info->hvm; } int xc_core_arch_memory_map_get(xc_interface *xch, struct xc_core_arch_context *unused, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xc_core_memory_map_t **mapp, unsigned int *nr_entries) { xen_pfn_t p2m_size = 0; xc_core_memory_map_t *map; if ( xc_domain_nr_gpfns(xch, info->domid, &p2m_size) < 0 ) return -1; map = malloc(sizeof(*map)); if ( map == NULL ) { PERROR("Could not allocate memory"); return -1; } map->addr = 0; map->size = ((uint64_t)p2m_size) << PAGE_SHIFT; *mapp = map; *nr_entries = 1; return 0; } static int xc_core_arch_map_p2m_rw(xc_interface *xch, struct domain_info_context *dinfo, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xen_pfn_t **live_p2m, unsigned long *pfnp, int rw) { /* Double and single indirect references to the live P2M table */ xen_pfn_t *live_p2m_frame_list_list = NULL; xen_pfn_t *live_p2m_frame_list = NULL; /* Copies of the above. */ xen_pfn_t *p2m_frame_list_list = NULL; xen_pfn_t *p2m_frame_list = NULL; uint32_t dom = info->domid; int ret = -1; int err; int i; if ( xc_domain_nr_gpfns(xch, info->domid, &dinfo->p2m_size) < 0 ) { ERROR("Could not get maximum GPFN!"); goto out; } if ( dinfo->p2m_size < info->nr_pages ) { ERROR("p2m_size < nr_pages -1 (%lx < %lx", dinfo->p2m_size, info->nr_pages - 1); goto out; } live_p2m_frame_list_list = xc_map_foreign_range(xch, dom, PAGE_SIZE, PROT_READ, GET_FIELD(live_shinfo, arch.pfn_to_mfn_frame_list_list)); if ( !live_p2m_frame_list_list ) { PERROR("Couldn't map p2m_frame_list_list (errno %d)", errno); goto out; } /* Get a local copy of the live_P2M_frame_list_list */ if ( !(p2m_frame_list_list = malloc(PAGE_SIZE)) ) { ERROR("Couldn't allocate p2m_frame_list_list array"); goto out; } memcpy(p2m_frame_list_list, live_p2m_frame_list_list, PAGE_SIZE); /* Canonicalize guest's unsigned long vs ours */ if ( dinfo->guest_width > sizeof(unsigned long) ) for ( i = 0; i < PAGE_SIZE/sizeof(unsigned long); i++ ) if ( i < PAGE_SIZE/dinfo->guest_width ) p2m_frame_list_list[i] = ((uint64_t *)p2m_frame_list_list)[i]; else p2m_frame_list_list[i] = 0; else if ( dinfo->guest_width < sizeof(unsigned long) ) for ( i = PAGE_SIZE/sizeof(unsigned long) - 1; i >= 0; i-- ) p2m_frame_list_list[i] = ((uint32_t *)p2m_frame_list_list)[i]; live_p2m_frame_list = xc_map_foreign_pages(xch, dom, PROT_READ, p2m_frame_list_list, P2M_FLL_ENTRIES); if ( !live_p2m_frame_list ) { PERROR("Couldn't map p2m_frame_list"); goto out; } /* Get a local copy of the live_P2M_frame_list */ if ( !(p2m_frame_list = malloc(P2M_TOOLS_FL_SIZE)) ) { ERROR("Couldn't allocate p2m_frame_list array"); goto out; } memset(p2m_frame_list, 0, P2M_TOOLS_FL_SIZE); memcpy(p2m_frame_list, live_p2m_frame_list, P2M_GUEST_FL_SIZE); /* Canonicalize guest's unsigned long vs ours */ if ( dinfo->guest_width > sizeof(unsigned long) ) for ( i = 0; i < P2M_FL_ENTRIES; i++ ) p2m_frame_list[i] = ((uint64_t *)p2m_frame_list)[i]; else if ( dinfo->guest_width < sizeof(unsigned long) ) for ( i = P2M_FL_ENTRIES - 1; i >= 0; i-- ) p2m_frame_list[i] = ((uint32_t *)p2m_frame_list)[i]; *live_p2m = xc_map_foreign_pages(xch, dom, rw ? (PROT_READ | PROT_WRITE) : PROT_READ, p2m_frame_list, P2M_FL_ENTRIES); if ( !*live_p2m ) { PERROR("Couldn't map p2m table"); goto out; } *pfnp = dinfo->p2m_size; ret = 0; out: err = errno; if ( live_p2m_frame_list_list ) munmap(live_p2m_frame_list_list, PAGE_SIZE); if ( live_p2m_frame_list ) munmap(live_p2m_frame_list, P2M_FLL_ENTRIES * PAGE_SIZE); free(p2m_frame_list_list); free(p2m_frame_list); errno = err; return ret; } int xc_core_arch_map_p2m(xc_interface *xch, unsigned int guest_width, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xen_pfn_t **live_p2m, unsigned long *pfnp) { struct domain_info_context _dinfo = { .guest_width = guest_width }; struct domain_info_context *dinfo = &_dinfo; return xc_core_arch_map_p2m_rw(xch, dinfo, info, live_shinfo, live_p2m, pfnp, 0); } int xc_core_arch_map_p2m_writable(xc_interface *xch, unsigned int guest_width, xc_dominfo_t *info, shared_info_any_t *live_shinfo, xen_pfn_t **live_p2m, unsigned long *pfnp) { struct domain_info_context _dinfo = { .guest_width = guest_width }; struct domain_info_context *dinfo = &_dinfo; return xc_core_arch_map_p2m_rw(xch, dinfo, info, live_shinfo, live_p2m, pfnp, 1); } int xc_core_arch_get_scratch_gpfn(xc_interface *xch, domid_t domid, xen_pfn_t *gpfn) { return xc_domain_nr_gpfns(xch, domid, gpfn); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_elf.h0000664000175000017500000000123113256712137014324 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include xen-4.9.2/tools/libxc/xc_cpupool.c0000664000175000017500000001370513256712137015243 0ustar smbsmb/****************************************************************************** * xc_cpupool.c * * API for manipulating and obtaining information on cpupools. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (c) 2009, J Gross. */ #include #include #include "xc_private.h" static int do_sysctl_save(xc_interface *xch, struct xen_sysctl *sysctl) { int ret; do { ret = do_sysctl(xch, sysctl); } while ( (ret < 0) && (errno == EAGAIN) ); return ret; } int xc_cpupool_create(xc_interface *xch, uint32_t *ppoolid, uint32_t sched_id) { int err; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_cpupool_op; sysctl.u.cpupool_op.op = XEN_SYSCTL_CPUPOOL_OP_CREATE; sysctl.u.cpupool_op.cpupool_id = (*ppoolid == XC_CPUPOOL_POOLID_ANY) ? XEN_SYSCTL_CPUPOOL_PAR_ANY : *ppoolid; sysctl.u.cpupool_op.sched_id = sched_id; if ( (err = do_sysctl_save(xch, &sysctl)) != 0 ) return err; *ppoolid = sysctl.u.cpupool_op.cpupool_id; return 0; } int xc_cpupool_destroy(xc_interface *xch, uint32_t poolid) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_cpupool_op; sysctl.u.cpupool_op.op = XEN_SYSCTL_CPUPOOL_OP_DESTROY; sysctl.u.cpupool_op.cpupool_id = poolid; return do_sysctl_save(xch, &sysctl); } xc_cpupoolinfo_t *xc_cpupool_getinfo(xc_interface *xch, uint32_t poolid) { int err = 0; xc_cpupoolinfo_t *info = NULL; int local_size; DECLARE_SYSCTL; DECLARE_HYPERCALL_BUFFER(uint8_t, local); local_size = xc_get_cpumap_size(xch); if (local_size <= 0) { PERROR("Could not get number of cpus"); return NULL; } local = xc_hypercall_buffer_alloc(xch, local, local_size); if ( local == NULL ) { PERROR("Could not allocate locked memory for xc_cpupool_getinfo"); return NULL; } sysctl.cmd = XEN_SYSCTL_cpupool_op; sysctl.u.cpupool_op.op = XEN_SYSCTL_CPUPOOL_OP_INFO; sysctl.u.cpupool_op.cpupool_id = poolid; set_xen_guest_handle(sysctl.u.cpupool_op.cpumap.bitmap, local); sysctl.u.cpupool_op.cpumap.nr_bits = local_size * 8; err = do_sysctl_save(xch, &sysctl); if ( err < 0 ) goto out; info = calloc(1, sizeof(xc_cpupoolinfo_t)); if ( !info ) goto out; info->cpumap = xc_cpumap_alloc(xch); if (!info->cpumap) { free(info); info = NULL; goto out; } info->cpupool_id = sysctl.u.cpupool_op.cpupool_id; info->sched_id = sysctl.u.cpupool_op.sched_id; info->n_dom = sysctl.u.cpupool_op.n_dom; memcpy(info->cpumap, local, local_size); out: xc_hypercall_buffer_free(xch, local); return info; } void xc_cpupool_infofree(xc_interface *xch, xc_cpupoolinfo_t *info) { free(info->cpumap); free(info); } int xc_cpupool_addcpu(xc_interface *xch, uint32_t poolid, int cpu) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_cpupool_op; sysctl.u.cpupool_op.op = XEN_SYSCTL_CPUPOOL_OP_ADDCPU; sysctl.u.cpupool_op.cpupool_id = poolid; sysctl.u.cpupool_op.cpu = (cpu < 0) ? XEN_SYSCTL_CPUPOOL_PAR_ANY : cpu; return do_sysctl_save(xch, &sysctl); } /* * The hypervisor might return EADDRINUSE when trying to remove a cpu from a * cpupool when a domain running in this cpupool has pinned a vcpu * temporarily. Do some retries in this case, perhaps the situation * cleans up. */ #define NUM_RMCPU_BUSY_RETRIES 5 int xc_cpupool_removecpu(xc_interface *xch, uint32_t poolid, int cpu) { unsigned retries; int err = 0; DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_cpupool_op; sysctl.u.cpupool_op.op = XEN_SYSCTL_CPUPOOL_OP_RMCPU; sysctl.u.cpupool_op.cpupool_id = poolid; sysctl.u.cpupool_op.cpu = (cpu < 0) ? XEN_SYSCTL_CPUPOOL_PAR_ANY : cpu; for ( retries = 0; retries < NUM_RMCPU_BUSY_RETRIES; retries++ ) { err = do_sysctl_save(xch, &sysctl); if ( err == 0 || errno != EADDRINUSE ) break; } return err; } int xc_cpupool_movedomain(xc_interface *xch, uint32_t poolid, uint32_t domid) { DECLARE_SYSCTL; sysctl.cmd = XEN_SYSCTL_cpupool_op; sysctl.u.cpupool_op.op = XEN_SYSCTL_CPUPOOL_OP_MOVEDOMAIN; sysctl.u.cpupool_op.cpupool_id = poolid; sysctl.u.cpupool_op.domid = domid; return do_sysctl_save(xch, &sysctl); } xc_cpumap_t xc_cpupool_freeinfo(xc_interface *xch) { int err = -1; xc_cpumap_t cpumap = NULL; int mapsize; DECLARE_SYSCTL; DECLARE_HYPERCALL_BUFFER(uint8_t, local); mapsize = xc_get_cpumap_size(xch); if (mapsize <= 0) return NULL; local = xc_hypercall_buffer_alloc(xch, local, mapsize); if ( local == NULL ) { PERROR("Could not allocate locked memory for xc_cpupool_freeinfo"); return NULL; } sysctl.cmd = XEN_SYSCTL_cpupool_op; sysctl.u.cpupool_op.op = XEN_SYSCTL_CPUPOOL_OP_FREEINFO; set_xen_guest_handle(sysctl.u.cpupool_op.cpumap.bitmap, local); sysctl.u.cpupool_op.cpumap.nr_bits = mapsize * 8; err = do_sysctl_save(xch, &sysctl); if ( err < 0 ) goto out; cpumap = xc_cpumap_alloc(xch); if (cpumap == NULL) goto out; memcpy(cpumap, local, mapsize); out: xc_hypercall_buffer_free(xch, local); return cpumap; } xen-4.9.2/tools/libxc/xc_core_x86.h0000664000175000017500000000374613256712137015230 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (c) 2007 Isaku Yamahata * VA Linux Systems Japan K.K. * */ #ifndef XC_CORE_X86_H #define XC_CORE_X86_H #define ELF_ARCH_DATA ELFDATA2LSB #define ELF_ARCH_MACHINE (dinfo->guest_width == 8 ? EM_X86_64 : EM_386) struct xc_core_arch_context { /* nothing */ }; #define xc_core_arch_context_init(arch_ctxt) do {} while (0) #define xc_core_arch_context_free(arch_ctxt) do {} while (0) #define xc_core_arch_context_get(arch_ctxt, ctxt, xch, domid) \ (0) #define xc_core_arch_context_dump(xch, arch_ctxt, args, dump_rtn) (0) int xc_core_arch_gpfn_may_present(struct xc_core_arch_context *arch_ctxt, unsigned long pfn); static inline int xc_core_arch_context_get_shdr(xc_interface *xch, struct xc_core_arch_context *arch_ctxt, struct xc_core_section_headers *sheaders, struct xc_core_strtab *strtab, uint64_t *filesz, uint64_t offset) { *filesz = 0; return 0; } #endif /* XC_CORE_X86_H */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_sr_common_x86_pv.h0000664000175000017500000000503713256712137016774 0ustar smbsmb#ifndef __COMMON_X86_PV_H #define __COMMON_X86_PV_H #include "xc_sr_common_x86.h" /* Virtual address ranges reserved for hypervisor. */ #define HYPERVISOR_VIRT_START_X86_64 0xFFFF800000000000ULL #define HYPERVISOR_VIRT_END_X86_64 0xFFFF87FFFFFFFFFFULL #define HYPERVISOR_VIRT_START_X86_32 0x00000000F5800000ULL #define HYPERVISOR_VIRT_END_X86_32 0x00000000FFFFFFFFULL /* * Convert an mfn to a pfn, given Xen's m2p table. * * Caller must ensure that the requested mfn is in range. */ xen_pfn_t mfn_to_pfn(struct xc_sr_context *ctx, xen_pfn_t mfn); /* * Query whether a particular mfn is valid in the physmap of a guest. */ bool mfn_in_pseudophysmap(struct xc_sr_context *ctx, xen_pfn_t mfn); /* * Debug a particular mfn by walking the p2m and m2p. */ void dump_bad_pseudophysmap_entry(struct xc_sr_context *ctx, xen_pfn_t mfn); /* * Convert a PV cr3 field to an mfn. * * Adjusts for Xen's extended-cr3 format to pack a 44bit physical address into * a 32bit architectural cr3. */ xen_pfn_t cr3_to_mfn(struct xc_sr_context *ctx, uint64_t cr3); /* * Convert an mfn to a PV cr3 field. * * Adjusts for Xen's extended-cr3 format to pack a 44bit physical address into * a 32bit architectural cr3. */ uint64_t mfn_to_cr3(struct xc_sr_context *ctx, xen_pfn_t mfn); /* Bits 12 through 51 of a PTE point at the frame */ #define PTE_FRAME_MASK 0x000ffffffffff000ULL /* * Extract an mfn from a Pagetable Entry. May return INVALID_MFN if the pte * would overflow a 32bit xen_pfn_t. */ static inline xen_pfn_t pte_to_frame(uint64_t pte) { uint64_t frame = (pte & PTE_FRAME_MASK) >> PAGE_SHIFT; #ifdef __i386__ if ( frame >= INVALID_MFN ) return INVALID_MFN; #endif return frame; } /* * Change the frame in a Pagetable Entry while leaving the flags alone. */ static inline uint64_t merge_pte(uint64_t pte, xen_pfn_t mfn) { return (pte & ~PTE_FRAME_MASK) | ((uint64_t)mfn << PAGE_SHIFT); } /* * Get current domain information. * * Fills ctx->x86_pv * - .width * - .levels * - .fpp * - .p2m_frames * * Used by the save side to create the X86_PV_INFO record, and by the restore * side to verify the incoming stream. * * Returns 0 on success and non-zero on error. */ int x86_pv_domain_info(struct xc_sr_context *ctx); /* * Maps the Xen M2P. * * Fills ctx->x86_pv. * - .max_mfn * - .m2p * * Returns 0 on success and non-zero on error. */ int x86_pv_map_m2p(struct xc_sr_context *ctx); #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_hvmloader.c0000664000175000017500000001553413256712137016404 0ustar smbsmb/* * Xen domain builder -- HVM specific bits. * * Parse and load ELF firmware images for HVM domains. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include "xg_private.h" #include "xc_dom.h" #include "xc_bitops.h" /* ------------------------------------------------------------------------ */ /* parse elf binary */ static elf_negerrnoval check_elf_kernel(struct xc_dom_image *dom, bool verbose) { if ( dom->kernel_blob == NULL ) { if ( verbose ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: no kernel image loaded", __func__); return -EINVAL; } if ( !elf_is_elfbinary(dom->kernel_blob, dom->kernel_size) ) { if ( verbose ) xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: kernel is not an ELF image", __func__); return -EINVAL; } return 0; } static elf_negerrnoval xc_dom_probe_hvm_kernel(struct xc_dom_image *dom) { struct elf_binary elf; int rc; /* This loader is designed for HVM guest firmware. */ if ( dom->container_type != XC_DOM_HVM_CONTAINER ) return -EINVAL; rc = check_elf_kernel(dom, 0); if ( rc != 0 ) return rc; rc = elf_init(&elf, dom->kernel_blob, dom->kernel_size); if ( rc != 0 ) return rc; /* * We need to check that there are no Xen ELFNOTES, or * else we might be trying to load a PV kernel. */ elf_parse_binary(&elf); rc = elf_xen_parse(&elf, &dom->parms); if ( rc == 0 ) return -EINVAL; return 0; } static elf_errorstatus xc_dom_parse_hvm_kernel(struct xc_dom_image *dom) /* * This function sometimes returns -1 for error and sometimes * an errno value. ?!?! */ { struct elf_binary *elf; elf_errorstatus rc; rc = check_elf_kernel(dom, 1); if ( rc != 0 ) return rc; elf = xc_dom_malloc(dom, sizeof(*elf)); if ( elf == NULL ) return -1; dom->private_loader = elf; rc = elf_init(elf, dom->kernel_blob, dom->kernel_size); xc_elf_set_logfile(dom->xch, elf, 1); if ( rc != 0 ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: corrupted ELF image", __func__); return rc; } if ( !elf_32bit(elf) ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: ELF image is not 32bit", __func__); return -EINVAL; } /* parse binary and get xen meta info */ elf_parse_binary(elf); /* find kernel segment */ dom->kernel_seg.vstart = elf->pstart; dom->kernel_seg.vend = elf->pend; dom->guest_type = "hvm-3.0-x86_32"; if ( elf_check_broken(elf) ) DOMPRINTF("%s: ELF broken: %s", __func__, elf_check_broken(elf)); return rc; } static int module_init_one(struct xc_dom_image *dom, struct xc_hvm_firmware_module *module, char *name) { struct xc_dom_seg seg; void *dest; if ( module->length ) { if ( xc_dom_alloc_segment(dom, &seg, name, 0, module->length) ) goto err; dest = xc_dom_seg_to_ptr(dom, &seg); if ( dest == NULL ) { DOMPRINTF("%s: xc_dom_seg_to_ptr(dom, &seg) => NULL", __FUNCTION__); goto err; } memcpy(dest, module->data, module->length); module->guest_addr_out = seg.vstart; assert(dom->mmio_start > 0 && dom->mmio_start < UINT32_MAX); if ( module->guest_addr_out > dom->mmio_start || module->guest_addr_out + module->length > dom->mmio_start ) { DOMPRINTF("%s: Module %s would be loaded abrove 4GB", __FUNCTION__, name); goto err; } } return 0; err: return -1; } static int modules_init(struct xc_dom_image *dom) { int rc; rc = module_init_one(dom, &dom->system_firmware_module, "System Firmware module"); if ( rc ) goto err; /* Only one module can be added */ rc = module_init_one(dom, &dom->acpi_modules[0], "ACPI module"); if ( rc ) goto err; rc = module_init_one(dom, &dom->smbios_module, "SMBIOS module"); if ( rc ) goto err; return 0; err: return -1; } static elf_errorstatus xc_dom_load_hvm_kernel(struct xc_dom_image *dom) { struct elf_binary *elf = dom->private_loader; privcmd_mmap_entry_t *entries = NULL; size_t pages = (elf->pend - elf->pstart + PAGE_SIZE - 1) >> PAGE_SHIFT; elf_errorstatus rc; int i; /* Map address space for initial elf image. */ entries = calloc(pages, sizeof(privcmd_mmap_entry_t)); if ( entries == NULL ) return -ENOMEM; for ( i = 0; i < pages; i++ ) entries[i].mfn = (elf->pstart >> PAGE_SHIFT) + i; elf->dest_base = xc_map_foreign_ranges( dom->xch, dom->guest_domid, pages << PAGE_SHIFT, PROT_READ | PROT_WRITE, 1 << PAGE_SHIFT, entries, pages); if ( elf->dest_base == NULL ) { DOMPRINTF("%s: unable to map guest memory space", __func__); rc = -EFAULT; goto error; } elf->dest_size = pages * XC_DOM_PAGE_SIZE(dom); rc = elf_load_binary(elf); if ( rc < 0 ) { DOMPRINTF("%s: failed to load elf binary", __func__); goto error; } munmap(elf->dest_base, elf->dest_size); rc = modules_init(dom); if ( rc != 0 ) { DOMPRINTF("%s: unable to load modules.", __func__); goto error; } dom->parms.phys_entry = elf_uval(elf, elf->ehdr, e_entry); free(entries); return 0; error: assert(rc != 0); free(entries); return rc; } /* ------------------------------------------------------------------------ */ struct xc_dom_loader hvm_loader = { .name = "HVM-generic", .probe = xc_dom_probe_hvm_kernel, .parser = xc_dom_parse_hvm_kernel, .loader = xc_dom_load_hvm_kernel, }; static void __init register_loader(void) { xc_dom_register_loader(&hvm_loader); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_arm.c0000664000175000017500000004141613256712137015200 0ustar smbsmb/* * Xen domain builder -- ARM * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (c) 2011, Citrix Systems */ #include #include #include #include #include #include "xg_private.h" #include "xc_dom.h" #define NR_MAGIC_PAGES 3 #define CONSOLE_PFN_OFFSET 0 #define XENSTORE_PFN_OFFSET 1 #define MEMACCESS_PFN_OFFSET 2 #define LPAE_SHIFT 9 #define PFN_4K_SHIFT (0) #define PFN_2M_SHIFT (PFN_4K_SHIFT+LPAE_SHIFT) #define PFN_1G_SHIFT (PFN_2M_SHIFT+LPAE_SHIFT) #define PFN_512G_SHIFT (PFN_1G_SHIFT+LPAE_SHIFT) /* get guest IO ABI protocol */ const char *xc_domain_get_native_protocol(xc_interface *xch, uint32_t domid) { return XEN_IO_PROTO_ABI_ARM; } /* ------------------------------------------------------------------------ */ /* * arm guests are hybrid and start off with paging disabled, therefore no * pagetables and nothing to do here. */ static int alloc_pgtables_arm(struct xc_dom_image *dom) { DOMPRINTF_CALLED(dom->xch); return 0; } static int setup_pgtables_arm(struct xc_dom_image *dom) { DOMPRINTF_CALLED(dom->xch); return 0; } /* ------------------------------------------------------------------------ */ static int alloc_magic_pages(struct xc_dom_image *dom) { int rc, i; const xen_pfn_t base = GUEST_MAGIC_BASE >> XC_PAGE_SHIFT; xen_pfn_t p2m[NR_MAGIC_PAGES]; BUILD_BUG_ON(NR_MAGIC_PAGES > GUEST_MAGIC_SIZE >> XC_PAGE_SHIFT); DOMPRINTF_CALLED(dom->xch); for (i = 0; i < NR_MAGIC_PAGES; i++) p2m[i] = base + i; rc = xc_domain_populate_physmap_exact( dom->xch, dom->guest_domid, NR_MAGIC_PAGES, 0, 0, p2m); if ( rc < 0 ) return rc; dom->console_pfn = base + CONSOLE_PFN_OFFSET; dom->xenstore_pfn = base + XENSTORE_PFN_OFFSET; xc_clear_domain_page(dom->xch, dom->guest_domid, dom->console_pfn); xc_clear_domain_page(dom->xch, dom->guest_domid, dom->xenstore_pfn); xc_clear_domain_page(dom->xch, dom->guest_domid, base + MEMACCESS_PFN_OFFSET); xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_CONSOLE_PFN, dom->console_pfn); xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_STORE_PFN, dom->xenstore_pfn); xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_MONITOR_RING_PFN, base + MEMACCESS_PFN_OFFSET); /* allocated by toolstack */ xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_CONSOLE_EVTCHN, dom->console_evtchn); xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_STORE_EVTCHN, dom->xenstore_evtchn); return 0; } /* ------------------------------------------------------------------------ */ static int start_info_arm(struct xc_dom_image *dom) { DOMPRINTF_CALLED(dom->xch); return 0; } static int shared_info_arm(struct xc_dom_image *dom, void *ptr) { DOMPRINTF_CALLED(dom->xch); return 0; } /* ------------------------------------------------------------------------ */ static int vcpu_arm32(struct xc_dom_image *dom) { vcpu_guest_context_any_t any_ctx; vcpu_guest_context_t *ctxt = &any_ctx.c; int rc; DOMPRINTF_CALLED(dom->xch); /* clear everything */ memset(ctxt, 0, sizeof(*ctxt)); ctxt->user_regs.pc32 = dom->parms.virt_entry; /* Linux boot protocol. See linux.Documentation/arm/Booting. */ ctxt->user_regs.r0_usr = 0; /* SBZ */ /* Machine ID: We use DTB therefore no machine id */ ctxt->user_regs.r1_usr = 0xffffffff; /* ATAGS/DTB: We currently require that the guest kernel to be * using CONFIG_ARM_APPENDED_DTB. Ensure that r2 does not look * like a valid pointer to a set of ATAGS or a DTB. */ ctxt->user_regs.r2_usr = dom->devicetree_blob ? dom->devicetree_seg.vstart : 0xffffffff; ctxt->sctlr = SCTLR_GUEST_INIT; ctxt->ttbr0 = 0; ctxt->ttbr1 = 0; ctxt->ttbcr = 0; /* Defined Reset Value */ ctxt->user_regs.cpsr = PSR_GUEST32_INIT; ctxt->flags = VGCF_online; DOMPRINTF("Initial state CPSR %#"PRIx32" PC %#"PRIx32, ctxt->user_regs.cpsr, ctxt->user_regs.pc32); rc = xc_vcpu_setcontext(dom->xch, dom->guest_domid, 0, &any_ctx); if ( rc != 0 ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: SETVCPUCONTEXT failed (rc=%d)", __func__, rc); return rc; } static int vcpu_arm64(struct xc_dom_image *dom) { vcpu_guest_context_any_t any_ctx; vcpu_guest_context_t *ctxt = &any_ctx.c; int rc; DOMPRINTF_CALLED(dom->xch); /* clear everything */ memset(ctxt, 0, sizeof(*ctxt)); ctxt->user_regs.pc64 = dom->parms.virt_entry; /* Linux boot protocol. See linux.Documentation/arm64/booting.txt. */ ctxt->user_regs.x0 = dom->devicetree_blob ? dom->devicetree_seg.vstart : 0xffffffff; ctxt->user_regs.x1 = 0; ctxt->user_regs.x2 = 0; ctxt->user_regs.x3 = 0; DOMPRINTF("DTB %"PRIx64, ctxt->user_regs.x0); ctxt->sctlr = SCTLR_GUEST_INIT; ctxt->ttbr0 = 0; ctxt->ttbr1 = 0; ctxt->ttbcr = 0; /* Defined Reset Value */ ctxt->user_regs.cpsr = PSR_GUEST64_INIT; ctxt->flags = VGCF_online; DOMPRINTF("Initial state CPSR %#"PRIx32" PC %#"PRIx64, ctxt->user_regs.cpsr, ctxt->user_regs.pc64); rc = xc_vcpu_setcontext(dom->xch, dom->guest_domid, 0, &any_ctx); if ( rc != 0 ) xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: SETVCPUCONTEXT failed (rc=%d)", __func__, rc); return rc; } /* ------------------------------------------------------------------------ */ static int set_mode(xc_interface *xch, domid_t domid, char *guest_type) { static const struct { char *guest; uint32_t size; } types[] = { { "xen-3.0-aarch64", 64 }, { "xen-3.0-armv7l", 32 }, }; DECLARE_DOMCTL; int i,rc; domctl.domain = domid; domctl.cmd = XEN_DOMCTL_set_address_size; domctl.u.address_size.size = 0; for ( i = 0; i < ARRAY_SIZE(types); i++ ) if ( !strcmp(types[i].guest, guest_type) ) domctl.u.address_size.size = types[i].size; if ( domctl.u.address_size.size == 0 ) { xc_dom_printf(xch, "%s: warning: unknown guest type %s", __FUNCTION__, guest_type); return -EINVAL; } xc_dom_printf(xch, "%s: guest %s, address size %" PRId32 "", __FUNCTION__, guest_type, domctl.u.address_size.size); rc = do_domctl(xch, &domctl); if ( rc != 0 ) xc_dom_printf(xch, "%s: warning: failed (rc=%d)", __FUNCTION__, rc); return rc; } /* >0: success, *nr_pfns set to number actually populated * 0: didn't try with this pfn shift (e.g. misaligned base etc) * <0: ERROR */ static int populate_one_size(struct xc_dom_image *dom, int pfn_shift, xen_pfn_t base_pfn, xen_pfn_t *nr_pfns, xen_pfn_t *extents) { /* The mask for this level */ const uint64_t mask = ((uint64_t)1<<(pfn_shift))-1; /* The shift, mask and next boundary for the level above this one */ const int next_shift = pfn_shift + LPAE_SHIFT; const uint64_t next_mask = ((uint64_t)1< next_boundary ) end_pfn = next_boundary; count = ( end_pfn - base_pfn ) >> pfn_shift; /* Nothing to allocate */ if ( !count ) return 0; for ( i = 0 ; i < count ; i ++ ) extents[i] = base_pfn + (i<xch, dom->guest_domid, count, pfn_shift, 0, extents); if ( nr <= 0 ) return nr; DOMPRINTF("%s: populated %#x/%#x entries with shift %d", __FUNCTION__, nr, count, pfn_shift); *nr_pfns = nr << pfn_shift; return 1; } static int populate_guest_memory(struct xc_dom_image *dom, xen_pfn_t base_pfn, xen_pfn_t nr_pfns) { int rc = 0; xen_pfn_t allocsz, pfn, *extents; extents = calloc(1024*1024,sizeof(xen_pfn_t)); if ( extents == NULL ) { DOMPRINTF("%s: Unable to allocate extent array", __FUNCTION__); return -1; } DOMPRINTF("%s: populating RAM @ %016"PRIx64"-%016"PRIx64" (%"PRId64"MB)", __FUNCTION__, (uint64_t)base_pfn << XC_PAGE_SHIFT, (uint64_t)(base_pfn + nr_pfns) << XC_PAGE_SHIFT, (uint64_t)nr_pfns >> (20-XC_PAGE_SHIFT)); for ( pfn = 0; pfn < nr_pfns; pfn += allocsz ) { allocsz = min_t(int, 1024*1024, nr_pfns - pfn); #if 0 /* Enable this to exercise/debug the code which tries to realign * to a superpage boundary, by misaligning at the start. */ if ( pfn == 0 ) { allocsz = 1; rc = populate_one_size(dom, PFN_4K_SHIFT, base_pfn + pfn, &allocsz, extents); if (rc < 0) break; if (rc > 0) continue; /* Failed to allocate a single page? */ break; } #endif rc = populate_one_size(dom, PFN_512G_SHIFT, base_pfn + pfn, &allocsz, extents); if ( rc < 0 ) break; if ( rc > 0 ) continue; rc = populate_one_size(dom, PFN_1G_SHIFT, base_pfn + pfn, &allocsz, extents); if ( rc < 0 ) break; if ( rc > 0 ) continue; rc = populate_one_size(dom, PFN_2M_SHIFT, base_pfn + pfn, &allocsz, extents); if ( rc < 0 ) break; if ( rc > 0 ) continue; rc = populate_one_size(dom, PFN_4K_SHIFT, base_pfn + pfn, &allocsz, extents); if ( rc < 0 ) break; if ( rc == 0 ) { DOMPRINTF("%s: Not enough RAM", __FUNCTION__); errno = ENOMEM; rc = -1; goto out; } } for ( pfn = 0; pfn < nr_pfns; pfn++ ) dom->p2m_host[pfn] = base_pfn + pfn; out: free(extents); return rc < 0 ? rc : 0; } static int meminit(struct xc_dom_image *dom) { int i, rc; xen_pfn_t pfn; uint64_t modbase; uint64_t ramsize = (uint64_t)dom->total_pages << XC_PAGE_SHIFT; const uint64_t bankbase[] = GUEST_RAM_BANK_BASES; const uint64_t bankmax[] = GUEST_RAM_BANK_SIZES; /* Convenient */ const uint64_t kernbase = dom->kernel_seg.vstart; const uint64_t kernend = ROUNDUP(dom->kernel_seg.vend, 21/*2MB*/); const uint64_t kernsize = kernend - kernbase; const uint64_t dtb_size = dom->devicetree_blob ? ROUNDUP(dom->devicetree_size, XC_PAGE_SHIFT) : 0; const uint64_t ramdisk_size = dom->ramdisk_blob ? ROUNDUP(dom->ramdisk_size, XC_PAGE_SHIFT) : 0; const uint64_t modsize = dtb_size + ramdisk_size; const uint64_t ram128mb = bankbase[0] + (128<<20); xen_pfn_t p2m_size; uint64_t bank0end; assert(dom->rambase_pfn << XC_PAGE_SHIFT == bankbase[0]); if ( modsize + kernsize > bankmax[0] ) { DOMPRINTF("%s: Not enough memory for the kernel+dtb+initrd", __FUNCTION__); return -1; } if ( ramsize == 0 ) { DOMPRINTF("%s: ram size is 0", __FUNCTION__); return -1; } if ( ramsize > GUEST_RAM_MAX ) { DOMPRINTF("%s: ram size is too large for guest address space: " "%"PRIx64" > %llx", __FUNCTION__, ramsize, GUEST_RAM_MAX); return -1; } rc = set_mode(dom->xch, dom->guest_domid, dom->guest_type); if ( rc ) return rc; for ( i = 0; ramsize && i < GUEST_RAM_BANKS; i++ ) { uint64_t banksize = ramsize > bankmax[i] ? bankmax[i] : ramsize; ramsize -= banksize; p2m_size = ( bankbase[i] + banksize - bankbase[0] ) >> XC_PAGE_SHIFT; dom->rambank_size[i] = banksize >> XC_PAGE_SHIFT; } assert(dom->rambank_size[0] != 0); assert(ramsize == 0); /* Too much RAM is rejected above */ dom->p2m_size = p2m_size; dom->p2m_host = xc_dom_malloc(dom, sizeof(xen_pfn_t) * p2m_size); if ( dom->p2m_host == NULL ) return -EINVAL; for ( pfn = 0; pfn < p2m_size; pfn++ ) dom->p2m_host[pfn] = INVALID_PFN; /* setup initial p2m and allocate guest memory */ for ( i = 0; i < GUEST_RAM_BANKS && dom->rambank_size[i]; i++ ) { if ((rc = populate_guest_memory(dom, bankbase[i] >> XC_PAGE_SHIFT, dom->rambank_size[i]))) return rc; } /* * We try to place dtb+initrd at 128MB or if we have less RAM * as high as possible. If there is no space then fallback to * just before the kernel. * * If changing this then consider * xen/arch/arm/kernel.c:place_modules as well. */ bank0end = bankbase[0] + ((uint64_t)dom->rambank_size[0] << XC_PAGE_SHIFT); if ( bank0end >= ram128mb + modsize && kernend < ram128mb ) modbase = ram128mb; else if ( bank0end - modsize > kernend ) modbase = bank0end - modsize; else if (kernbase - bankbase[0] > modsize ) modbase = kernbase - modsize; else return -1; DOMPRINTF("%s: placing boot modules at 0x%" PRIx64, __FUNCTION__, modbase); /* * Must map DTB *after* initrd, to satisfy order of calls to * xc_dom_alloc_segment in xc_dom_build_image, which must map * things at monotonolically increasing addresses. */ if ( ramdisk_size ) { dom->ramdisk_seg.vstart = modbase; dom->ramdisk_seg.vend = modbase + ramdisk_size; DOMPRINTF("%s: ramdisk: 0x%" PRIx64 " -> 0x%" PRIx64 "", __FUNCTION__, dom->ramdisk_seg.vstart, dom->ramdisk_seg.vend); modbase += ramdisk_size; } if ( dtb_size ) { dom->devicetree_seg.vstart = modbase; dom->devicetree_seg.vend = modbase + dtb_size; DOMPRINTF("%s: devicetree: 0x%" PRIx64 " -> 0x%" PRIx64 "", __FUNCTION__, dom->devicetree_seg.vstart, dom->devicetree_seg.vend); modbase += dtb_size; } return 0; } bool xc_dom_translated(const struct xc_dom_image *dom) { return true; } /* ------------------------------------------------------------------------ */ static int bootearly(struct xc_dom_image *dom) { DOMPRINTF("%s: doing nothing", __FUNCTION__); return 0; } static int bootlate(struct xc_dom_image *dom) { /* XXX * map shared info * map grant tables * setup shared info */ return 0; } /* ------------------------------------------------------------------------ */ static struct xc_dom_arch xc_dom_32 = { .guest_type = "xen-3.0-armv7l", .native_protocol = XEN_IO_PROTO_ABI_ARM, .page_shift = PAGE_SHIFT_ARM, .sizeof_pfn = 8, .alloc_magic_pages = alloc_magic_pages, .alloc_pgtables = alloc_pgtables_arm, .setup_pgtables = setup_pgtables_arm, .start_info = start_info_arm, .shared_info = shared_info_arm, .vcpu = vcpu_arm32, .meminit = meminit, .bootearly = bootearly, .bootlate = bootlate, }; static struct xc_dom_arch xc_dom_64 = { .guest_type = "xen-3.0-aarch64", .native_protocol = XEN_IO_PROTO_ABI_ARM, .page_shift = PAGE_SHIFT_ARM, .sizeof_pfn = 8, .alloc_magic_pages = alloc_magic_pages, .alloc_pgtables = alloc_pgtables_arm, .setup_pgtables = setup_pgtables_arm, .start_info = start_info_arm, .shared_info = shared_info_arm, .vcpu = vcpu_arm64, .meminit = meminit, .bootearly = bootearly, .bootlate = bootlate, }; static void __init register_arch_hooks(void) { xc_dom_register_arch_hooks(&xc_dom_32); xc_dom_register_arch_hooks(&xc_dom_64); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_core_arm.h0000664000175000017500000000356113256712137015355 0ustar smbsmb/* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (c) 2012 Citrix Systems * */ #ifndef XC_CORE_ARM_H #define XC_CORE_ARM_H #define ELF_ARCH_DATA ELFDATA2LSB #define ELF_ARCH_MACHINE EM_ARM struct xc_core_arch_context { /* nothing */ }; #define xc_core_arch_context_init(arch_ctxt) do {} while (0) #define xc_core_arch_context_free(arch_ctxt) do {} while (0) #define xc_core_arch_context_get(arch_ctxt, ctxt, xch, domid) \ (0) #define xc_core_arch_context_dump(xch, arch_ctxt, args, dump_rtn) (0) int xc_core_arch_gpfn_may_present(struct xc_core_arch_context *arch_ctxt, unsigned long pfn); static inline int xc_core_arch_context_get_shdr(xc_interface *xch, struct xc_core_arch_context *arch_ctxt, struct xc_core_section_headers *sheaders, struct xc_core_strtab *strtab, uint64_t *filesz, uint64_t offset) { *filesz = 0; return 0; } #endif /* XC_CORE_ARM_H */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_domain.c0000664000175000017500000020400313256712137015022 0ustar smbsmb/****************************************************************************** * xc_domain.c * * API for manipulating and obtaining information on domains. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (c) 2003, K A Fraser. */ #include "xc_private.h" #include "xc_core.h" #include "xg_private.h" #include "xg_save_restore.h" #include #include int xc_domain_create(xc_interface *xch, uint32_t ssidref, xen_domain_handle_t handle, uint32_t flags, uint32_t *pdomid, xc_domain_configuration_t *config) { xc_domain_configuration_t lconfig; int err; DECLARE_DOMCTL; if ( config == NULL ) { memset(&lconfig, 0, sizeof(lconfig)); #if defined (__i386) || defined(__x86_64__) if ( flags & XEN_DOMCTL_CDF_hvm_guest ) lconfig.emulation_flags = XEN_X86_EMU_ALL; #elif defined (__arm__) || defined(__aarch64__) lconfig.gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE; lconfig.nr_spis = 0; #else #error Architecture not supported #endif config = &lconfig; } domctl.cmd = XEN_DOMCTL_createdomain; domctl.domain = (domid_t)*pdomid; domctl.u.createdomain.ssidref = ssidref; domctl.u.createdomain.flags = flags; memcpy(domctl.u.createdomain.handle, handle, sizeof(xen_domain_handle_t)); /* xc_domain_configure_t is an alias of arch_domainconfig_t */ memcpy(&domctl.u.createdomain.config, config, sizeof(*config)); if ( (err = do_domctl(xch, &domctl)) != 0 ) return err; *pdomid = (uint16_t)domctl.domain; memcpy(config, &domctl.u.createdomain.config, sizeof(*config)); return 0; } int xc_domain_cacheflush(xc_interface *xch, uint32_t domid, xen_pfn_t start_pfn, xen_pfn_t nr_pfns) { #if defined (__i386__) || defined (__x86_64__) /* * The x86 architecture provides cache coherency guarantees which prevent * the need for this hypercall. Avoid the overhead of making a hypercall * just for Xen to return -ENOSYS. It is safe to ignore this call on x86 * so we just return 0. */ return 0; #else DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_cacheflush; domctl.domain = (domid_t)domid; domctl.u.cacheflush.start_pfn = start_pfn; domctl.u.cacheflush.nr_pfns = nr_pfns; return do_domctl(xch, &domctl); #endif } int xc_domain_pause(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_pausedomain; domctl.domain = (domid_t)domid; return do_domctl(xch, &domctl); } int xc_domain_unpause(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_unpausedomain; domctl.domain = (domid_t)domid; return do_domctl(xch, &domctl); } int xc_domain_destroy(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_destroydomain; domctl.domain = (domid_t)domid; return do_domctl(xch, &domctl); } int xc_domain_shutdown(xc_interface *xch, uint32_t domid, int reason) { int ret = -1; DECLARE_HYPERCALL_BUFFER(sched_remote_shutdown_t, arg); arg = xc_hypercall_buffer_alloc(xch, arg, sizeof(*arg)); if ( arg == NULL ) { PERROR("Could not allocate memory for xc_domain_shutdown hypercall"); goto out1; } arg->domain_id = domid; arg->reason = reason; ret = xencall2(xch->xcall, __HYPERVISOR_sched_op, SCHEDOP_remote_shutdown, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_buffer_free(xch, arg); out1: return ret; } int xc_domain_node_setaffinity(xc_interface *xch, uint32_t domid, xc_nodemap_t nodemap) { DECLARE_DOMCTL; DECLARE_HYPERCALL_BUFFER(uint8_t, local); int ret = -1; int nodesize; nodesize = xc_get_nodemap_size(xch); if (nodesize <= 0) { PERROR("Could not get number of nodes"); goto out; } local = xc_hypercall_buffer_alloc(xch, local, nodesize); if ( local == NULL ) { PERROR("Could not allocate memory for setnodeaffinity domctl hypercall"); goto out; } domctl.cmd = XEN_DOMCTL_setnodeaffinity; domctl.domain = (domid_t)domid; memcpy(local, nodemap, nodesize); set_xen_guest_handle(domctl.u.nodeaffinity.nodemap.bitmap, local); domctl.u.nodeaffinity.nodemap.nr_bits = nodesize * 8; ret = do_domctl(xch, &domctl); xc_hypercall_buffer_free(xch, local); out: return ret; } int xc_domain_node_getaffinity(xc_interface *xch, uint32_t domid, xc_nodemap_t nodemap) { DECLARE_DOMCTL; DECLARE_HYPERCALL_BUFFER(uint8_t, local); int ret = -1; int nodesize; nodesize = xc_get_nodemap_size(xch); if (nodesize <= 0) { PERROR("Could not get number of nodes"); goto out; } local = xc_hypercall_buffer_alloc(xch, local, nodesize); if ( local == NULL ) { PERROR("Could not allocate memory for getnodeaffinity domctl hypercall"); goto out; } domctl.cmd = XEN_DOMCTL_getnodeaffinity; domctl.domain = (domid_t)domid; set_xen_guest_handle(domctl.u.nodeaffinity.nodemap.bitmap, local); domctl.u.nodeaffinity.nodemap.nr_bits = nodesize * 8; ret = do_domctl(xch, &domctl); memcpy(nodemap, local, nodesize); xc_hypercall_buffer_free(xch, local); out: return ret; } int xc_vcpu_setaffinity(xc_interface *xch, uint32_t domid, int vcpu, xc_cpumap_t cpumap_hard_inout, xc_cpumap_t cpumap_soft_inout, uint32_t flags) { DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(cpumap_hard_inout, 0, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); DECLARE_HYPERCALL_BOUNCE(cpumap_soft_inout, 0, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); int ret = -1; int cpusize; cpusize = xc_get_cpumap_size(xch); if (cpusize <= 0) { PERROR("Could not get number of cpus"); return -1; } HYPERCALL_BOUNCE_SET_SIZE(cpumap_hard_inout, cpusize); HYPERCALL_BOUNCE_SET_SIZE(cpumap_soft_inout, cpusize); if ( xc_hypercall_bounce_pre(xch, cpumap_hard_inout) || xc_hypercall_bounce_pre(xch, cpumap_soft_inout) ) { PERROR("Could not allocate hcall buffers for DOMCTL_setvcpuaffinity"); goto out; } domctl.cmd = XEN_DOMCTL_setvcpuaffinity; domctl.domain = (domid_t)domid; domctl.u.vcpuaffinity.vcpu = vcpu; domctl.u.vcpuaffinity.flags = flags; set_xen_guest_handle(domctl.u.vcpuaffinity.cpumap_hard.bitmap, cpumap_hard_inout); domctl.u.vcpuaffinity.cpumap_hard.nr_bits = cpusize * 8; set_xen_guest_handle(domctl.u.vcpuaffinity.cpumap_soft.bitmap, cpumap_soft_inout); domctl.u.vcpuaffinity.cpumap_soft.nr_bits = cpusize * 8; ret = do_domctl(xch, &domctl); out: xc_hypercall_bounce_post(xch, cpumap_hard_inout); xc_hypercall_bounce_post(xch, cpumap_soft_inout); return ret; } int xc_vcpu_getaffinity(xc_interface *xch, uint32_t domid, int vcpu, xc_cpumap_t cpumap_hard, xc_cpumap_t cpumap_soft, uint32_t flags) { DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(cpumap_hard, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_HYPERCALL_BOUNCE(cpumap_soft, 0, XC_HYPERCALL_BUFFER_BOUNCE_OUT); int ret = -1; int cpusize; cpusize = xc_get_cpumap_size(xch); if (cpusize <= 0) { PERROR("Could not get number of cpus"); return -1; } HYPERCALL_BOUNCE_SET_SIZE(cpumap_hard, cpusize); HYPERCALL_BOUNCE_SET_SIZE(cpumap_soft, cpusize); if ( xc_hypercall_bounce_pre(xch, cpumap_hard) || xc_hypercall_bounce_pre(xch, cpumap_soft) ) { PERROR("Could not allocate hcall buffers for DOMCTL_getvcpuaffinity"); goto out; } domctl.cmd = XEN_DOMCTL_getvcpuaffinity; domctl.domain = (domid_t)domid; domctl.u.vcpuaffinity.vcpu = vcpu; domctl.u.vcpuaffinity.flags = flags; set_xen_guest_handle(domctl.u.vcpuaffinity.cpumap_hard.bitmap, cpumap_hard); domctl.u.vcpuaffinity.cpumap_hard.nr_bits = cpusize * 8; set_xen_guest_handle(domctl.u.vcpuaffinity.cpumap_soft.bitmap, cpumap_soft); domctl.u.vcpuaffinity.cpumap_soft.nr_bits = cpusize * 8; ret = do_domctl(xch, &domctl); out: xc_hypercall_bounce_post(xch, cpumap_hard); xc_hypercall_bounce_post(xch, cpumap_soft); return ret; } int xc_domain_get_guest_width(xc_interface *xch, uint32_t domid, unsigned int *guest_width) { DECLARE_DOMCTL; memset(&domctl, 0, sizeof(domctl)); domctl.domain = domid; domctl.cmd = XEN_DOMCTL_get_address_size; if ( do_domctl(xch, &domctl) != 0 ) return 1; /* We want the result in bytes */ *guest_width = domctl.u.address_size.size / 8; return 0; } int xc_domain_getinfo(xc_interface *xch, uint32_t first_domid, unsigned int max_doms, xc_dominfo_t *info) { unsigned int nr_doms; uint32_t next_domid = first_domid; DECLARE_DOMCTL; int rc = 0; memset(info, 0, max_doms*sizeof(xc_dominfo_t)); for ( nr_doms = 0; nr_doms < max_doms; nr_doms++ ) { domctl.cmd = XEN_DOMCTL_getdomaininfo; domctl.domain = (domid_t)next_domid; if ( (rc = do_domctl(xch, &domctl)) < 0 ) break; info->domid = (uint16_t)domctl.domain; info->dying = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_dying); info->shutdown = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_shutdown); info->paused = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_paused); info->blocked = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_blocked); info->running = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_running); info->hvm = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_hvm_guest); info->debugged = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_debugged); info->xenstore = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_xs_domain); info->hap = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_hap); info->shutdown_reason = (domctl.u.getdomaininfo.flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask; if ( info->shutdown && (info->shutdown_reason == SHUTDOWN_crash) ) { info->shutdown = 0; info->crashed = 1; } info->ssidref = domctl.u.getdomaininfo.ssidref; info->nr_pages = domctl.u.getdomaininfo.tot_pages; info->nr_outstanding_pages = domctl.u.getdomaininfo.outstanding_pages; info->nr_shared_pages = domctl.u.getdomaininfo.shr_pages; info->nr_paged_pages = domctl.u.getdomaininfo.paged_pages; info->max_memkb = domctl.u.getdomaininfo.max_pages << (PAGE_SHIFT-10); info->shared_info_frame = domctl.u.getdomaininfo.shared_info_frame; info->cpu_time = domctl.u.getdomaininfo.cpu_time; info->nr_online_vcpus = domctl.u.getdomaininfo.nr_online_vcpus; info->max_vcpu_id = domctl.u.getdomaininfo.max_vcpu_id; info->cpupool = domctl.u.getdomaininfo.cpupool; memcpy(info->handle, domctl.u.getdomaininfo.handle, sizeof(xen_domain_handle_t)); next_domid = (uint16_t)domctl.domain + 1; info++; } if ( nr_doms == 0 ) return rc; return nr_doms; } int xc_domain_getinfolist(xc_interface *xch, uint32_t first_domain, unsigned int max_domains, xc_domaininfo_t *info) { int ret = 0; DECLARE_SYSCTL; DECLARE_HYPERCALL_BOUNCE(info, max_domains*sizeof(*info), XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( xc_hypercall_bounce_pre(xch, info) ) return -1; sysctl.cmd = XEN_SYSCTL_getdomaininfolist; sysctl.u.getdomaininfolist.first_domain = first_domain; sysctl.u.getdomaininfolist.max_domains = max_domains; set_xen_guest_handle(sysctl.u.getdomaininfolist.buffer, info); if ( xc_sysctl(xch, &sysctl) < 0 ) ret = -1; else ret = sysctl.u.getdomaininfolist.num_domains; xc_hypercall_bounce_post(xch, info); return ret; } /* set broken page p2m */ int xc_set_broken_page_p2m(xc_interface *xch, uint32_t domid, unsigned long pfn) { int ret; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_set_broken_page_p2m; domctl.domain = (domid_t)domid; domctl.u.set_broken_page_p2m.pfn = pfn; ret = do_domctl(xch, &domctl); return ret ? -1 : 0; } /* get info from hvm guest for save */ int xc_domain_hvm_getcontext(xc_interface *xch, uint32_t domid, uint8_t *ctxt_buf, uint32_t size) { int ret; DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(ctxt_buf, size, XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( xc_hypercall_bounce_pre(xch, ctxt_buf) ) return -1; domctl.cmd = XEN_DOMCTL_gethvmcontext; domctl.domain = (domid_t)domid; domctl.u.hvmcontext.size = size; set_xen_guest_handle(domctl.u.hvmcontext.buffer, ctxt_buf); ret = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, ctxt_buf); return (ret < 0 ? -1 : domctl.u.hvmcontext.size); } /* Get just one element of the HVM guest context. * size must be >= HVM_SAVE_LENGTH(type) */ int xc_domain_hvm_getcontext_partial(xc_interface *xch, uint32_t domid, uint16_t typecode, uint16_t instance, void *ctxt_buf, uint32_t size) { int ret; DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(ctxt_buf, size, XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( !ctxt_buf || xc_hypercall_bounce_pre(xch, ctxt_buf) ) return -1; domctl.cmd = XEN_DOMCTL_gethvmcontext_partial; domctl.domain = (domid_t) domid; domctl.u.hvmcontext_partial.type = typecode; domctl.u.hvmcontext_partial.instance = instance; set_xen_guest_handle(domctl.u.hvmcontext_partial.buffer, ctxt_buf); ret = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, ctxt_buf); return ret ? -1 : 0; } /* set info to hvm guest for restore */ int xc_domain_hvm_setcontext(xc_interface *xch, uint32_t domid, uint8_t *ctxt_buf, uint32_t size) { int ret; DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(ctxt_buf, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, ctxt_buf) ) return -1; domctl.cmd = XEN_DOMCTL_sethvmcontext; domctl.domain = domid; domctl.u.hvmcontext.size = size; set_xen_guest_handle(domctl.u.hvmcontext.buffer, ctxt_buf); ret = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, ctxt_buf); return ret; } int xc_vcpu_getcontext(xc_interface *xch, uint32_t domid, uint32_t vcpu, vcpu_guest_context_any_t *ctxt) { int rc; DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(ctxt, sizeof(vcpu_guest_context_any_t), XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( xc_hypercall_bounce_pre(xch, ctxt) ) return -1; domctl.cmd = XEN_DOMCTL_getvcpucontext; domctl.domain = (domid_t)domid; domctl.u.vcpucontext.vcpu = (uint16_t)vcpu; set_xen_guest_handle(domctl.u.vcpucontext.ctxt, ctxt); rc = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, ctxt); return rc; } int xc_vcpu_get_extstate(xc_interface *xch, uint32_t domid, uint32_t vcpu, xc_vcpu_extstate_t *extstate) { int rc = -ENODEV; #if defined (__i386__) || defined(__x86_64__) DECLARE_DOMCTL; DECLARE_HYPERCALL_BUFFER(void, buffer); bool get_state; if ( !extstate ) return -EINVAL; domctl.cmd = XEN_DOMCTL_getvcpuextstate; domctl.domain = (domid_t)domid; domctl.u.vcpuextstate.vcpu = (uint16_t)vcpu; domctl.u.vcpuextstate.xfeature_mask = extstate->xfeature_mask; domctl.u.vcpuextstate.size = extstate->size; get_state = (extstate->size != 0); if ( get_state ) { buffer = xc_hypercall_buffer_alloc(xch, buffer, extstate->size); if ( !buffer ) { PERROR("Unable to allocate memory for vcpu%u's xsave context", vcpu); rc = -ENOMEM; goto out; } set_xen_guest_handle(domctl.u.vcpuextstate.buffer, buffer); } rc = do_domctl(xch, &domctl); if ( rc ) goto out; /* A query for the size of buffer to use. */ if ( !extstate->size && !extstate->xfeature_mask ) { extstate->xfeature_mask = domctl.u.vcpuextstate.xfeature_mask; extstate->size = domctl.u.vcpuextstate.size; goto out; } if ( get_state ) memcpy(extstate->buffer, buffer, extstate->size); out: if ( get_state ) xc_hypercall_buffer_free(xch, buffer); #endif return rc; } int xc_watchdog(xc_interface *xch, uint32_t id, uint32_t timeout) { int ret = -1; DECLARE_HYPERCALL_BUFFER(sched_watchdog_t, arg); arg = xc_hypercall_buffer_alloc(xch, arg, sizeof(*arg)); if ( arg == NULL ) { PERROR("Could not allocate memory for xc_watchdog hypercall"); goto out1; } arg->id = id; arg->timeout = timeout; ret = xencall2(xch->xcall, __HYPERVISOR_sched_op, SCHEDOP_watchdog, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_buffer_free(xch, arg); out1: return ret; } int xc_shadow_control(xc_interface *xch, uint32_t domid, unsigned int sop, xc_hypercall_buffer_t *dirty_bitmap, unsigned long pages, unsigned long *mb, uint32_t mode, xc_shadow_op_stats_t *stats) { int rc; DECLARE_DOMCTL; DECLARE_HYPERCALL_BUFFER_ARGUMENT(dirty_bitmap); memset(&domctl, 0, sizeof(domctl)); domctl.cmd = XEN_DOMCTL_shadow_op; domctl.domain = (domid_t)domid; domctl.u.shadow_op.op = sop; domctl.u.shadow_op.pages = pages; domctl.u.shadow_op.mb = mb ? *mb : 0; domctl.u.shadow_op.mode = mode; if (dirty_bitmap != NULL) set_xen_guest_handle(domctl.u.shadow_op.dirty_bitmap, dirty_bitmap); rc = do_domctl(xch, &domctl); if ( stats ) memcpy(stats, &domctl.u.shadow_op.stats, sizeof(xc_shadow_op_stats_t)); if ( mb ) *mb = domctl.u.shadow_op.mb; return (rc == 0) ? domctl.u.shadow_op.pages : rc; } int xc_domain_setmaxmem(xc_interface *xch, uint32_t domid, uint64_t max_memkb) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_max_mem; domctl.domain = (domid_t)domid; domctl.u.max_mem.max_memkb = max_memkb; return do_domctl(xch, &domctl); } int xc_domain_pin_memory_cacheattr(xc_interface *xch, uint32_t domid, uint64_t start, uint64_t end, uint32_t type) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_pin_mem_cacheattr; domctl.domain = (domid_t)domid; domctl.u.pin_mem_cacheattr.start = start; domctl.u.pin_mem_cacheattr.end = end; domctl.u.pin_mem_cacheattr.type = type; return do_domctl(xch, &domctl); } #if defined(__i386__) || defined(__x86_64__) int xc_domain_set_memory_map(xc_interface *xch, uint32_t domid, struct e820entry entries[], uint32_t nr_entries) { int rc; struct xen_foreign_memory_map fmap = { .domid = domid, .map = { .nr_entries = nr_entries } }; DECLARE_HYPERCALL_BOUNCE(entries, nr_entries * sizeof(struct e820entry), XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( !entries || xc_hypercall_bounce_pre(xch, entries) ) return -1; set_xen_guest_handle(fmap.map.buffer, entries); rc = do_memory_op(xch, XENMEM_set_memory_map, &fmap, sizeof(fmap)); xc_hypercall_bounce_post(xch, entries); return rc; } int xc_get_machine_memory_map(xc_interface *xch, struct e820entry entries[], uint32_t max_entries) { int rc; struct xen_memory_map memmap = { .nr_entries = max_entries }; DECLARE_HYPERCALL_BOUNCE(entries, sizeof(struct e820entry) * max_entries, XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( !entries || xc_hypercall_bounce_pre(xch, entries) || max_entries <= 1) return -1; set_xen_guest_handle(memmap.buffer, entries); rc = do_memory_op(xch, XENMEM_machine_memory_map, &memmap, sizeof(memmap)); xc_hypercall_bounce_post(xch, entries); return rc ? rc : memmap.nr_entries; } int xc_domain_set_memmap_limit(xc_interface *xch, uint32_t domid, unsigned long map_limitkb) { struct e820entry e820; e820.addr = 0; e820.size = (uint64_t)map_limitkb << 10; e820.type = E820_RAM; return xc_domain_set_memory_map(xch, domid, &e820, 1); } #else int xc_domain_set_memmap_limit(xc_interface *xch, uint32_t domid, unsigned long map_limitkb) { PERROR("Function not implemented"); errno = ENOSYS; return -1; } #endif int xc_reserved_device_memory_map(xc_interface *xch, uint32_t flags, uint16_t seg, uint8_t bus, uint8_t devfn, struct xen_reserved_device_memory entries[], uint32_t *max_entries) { int rc; struct xen_reserved_device_memory_map xrdmmap = { .flags = flags, .dev.pci.seg = seg, .dev.pci.bus = bus, .dev.pci.devfn = devfn, .nr_entries = *max_entries }; DECLARE_HYPERCALL_BOUNCE(entries, sizeof(struct xen_reserved_device_memory) * *max_entries, XC_HYPERCALL_BUFFER_BOUNCE_OUT); if ( xc_hypercall_bounce_pre(xch, entries) ) return -1; set_xen_guest_handle(xrdmmap.buffer, entries); rc = do_memory_op(xch, XENMEM_reserved_device_memory_map, &xrdmmap, sizeof(xrdmmap)); xc_hypercall_bounce_post(xch, entries); *max_entries = xrdmmap.nr_entries; return rc; } int xc_domain_set_time_offset(xc_interface *xch, uint32_t domid, int32_t time_offset_seconds) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_settimeoffset; domctl.domain = (domid_t)domid; domctl.u.settimeoffset.time_offset_seconds = time_offset_seconds; return do_domctl(xch, &domctl); } int xc_domain_disable_migrate(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_disable_migrate; domctl.domain = (domid_t)domid; domctl.u.disable_migrate.disable = 1; return do_domctl(xch, &domctl); } int xc_domain_set_tsc_info(xc_interface *xch, uint32_t domid, uint32_t tsc_mode, uint64_t elapsed_nsec, uint32_t gtsc_khz, uint32_t incarnation) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_settscinfo; domctl.domain = (domid_t)domid; domctl.u.tsc_info.tsc_mode = tsc_mode; domctl.u.tsc_info.elapsed_nsec = elapsed_nsec; domctl.u.tsc_info.gtsc_khz = gtsc_khz; domctl.u.tsc_info.incarnation = incarnation; return do_domctl(xch, &domctl); } int xc_domain_get_tsc_info(xc_interface *xch, uint32_t domid, uint32_t *tsc_mode, uint64_t *elapsed_nsec, uint32_t *gtsc_khz, uint32_t *incarnation) { int rc; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_gettscinfo; domctl.domain = (domid_t)domid; rc = do_domctl(xch, &domctl); if ( rc == 0 ) { *tsc_mode = domctl.u.tsc_info.tsc_mode; *elapsed_nsec = domctl.u.tsc_info.elapsed_nsec; *gtsc_khz = domctl.u.tsc_info.gtsc_khz; *incarnation = domctl.u.tsc_info.incarnation; } return rc; } int xc_domain_maximum_gpfn(xc_interface *xch, domid_t domid, xen_pfn_t *gpfns) { long rc = do_memory_op(xch, XENMEM_maximum_gpfn, &domid, sizeof(domid)); if ( rc >= 0 ) { *gpfns = rc; rc = 0; } return rc; } int xc_domain_nr_gpfns(xc_interface *xch, domid_t domid, xen_pfn_t *gpfns) { int rc = xc_domain_maximum_gpfn(xch, domid, gpfns); if ( rc >= 0 ) *gpfns += 1; return rc; } int xc_domain_increase_reservation(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start) { int err; DECLARE_HYPERCALL_BOUNCE(extent_start, nr_extents * sizeof(*extent_start), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); struct xen_memory_reservation reservation = { .nr_extents = nr_extents, .extent_order = extent_order, .mem_flags = mem_flags, .domid = domid }; /* may be NULL */ if ( xc_hypercall_bounce_pre(xch, extent_start) ) { PERROR("Could not bounce memory for XENMEM_increase_reservation hypercall"); return -1; } set_xen_guest_handle(reservation.extent_start, extent_start); err = do_memory_op(xch, XENMEM_increase_reservation, &reservation, sizeof(reservation)); xc_hypercall_bounce_post(xch, extent_start); return err; } int xc_domain_increase_reservation_exact(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start) { int err; err = xc_domain_increase_reservation(xch, domid, nr_extents, extent_order, mem_flags, extent_start); if ( err == nr_extents ) return 0; if ( err >= 0 ) { DPRINTF("Failed allocation for dom %d: " "%ld extents of order %d, mem_flags %x\n", domid, nr_extents, extent_order, mem_flags); errno = ENOMEM; err = -1; } return err; } int xc_domain_decrease_reservation(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, xen_pfn_t *extent_start) { int err; DECLARE_HYPERCALL_BOUNCE(extent_start, nr_extents * sizeof(*extent_start), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); struct xen_memory_reservation reservation = { .nr_extents = nr_extents, .extent_order = extent_order, .mem_flags = 0, .domid = domid }; if ( extent_start == NULL ) { DPRINTF("decrease_reservation extent_start is NULL!\n"); errno = EINVAL; return -1; } if ( xc_hypercall_bounce_pre(xch, extent_start) ) { PERROR("Could not bounce memory for XENMEM_decrease_reservation hypercall"); return -1; } set_xen_guest_handle(reservation.extent_start, extent_start); err = do_memory_op(xch, XENMEM_decrease_reservation, &reservation, sizeof(reservation)); xc_hypercall_bounce_post(xch, extent_start); return err; } int xc_domain_decrease_reservation_exact(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, xen_pfn_t *extent_start) { int err; err = xc_domain_decrease_reservation(xch, domid, nr_extents, extent_order, extent_start); if ( err == nr_extents ) return 0; if ( err >= 0 ) { DPRINTF("Failed deallocation for dom %d: %ld extents of order %d\n", domid, nr_extents, extent_order); errno = EINVAL; err = -1; } return err; } int xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid, unsigned int space, unsigned long idx, xen_pfn_t gpfn) { struct xen_add_to_physmap xatp = { .domid = domid, .space = space, .idx = idx, .gpfn = gpfn, }; return do_memory_op(xch, XENMEM_add_to_physmap, &xatp, sizeof(xatp)); } int xc_domain_claim_pages(xc_interface *xch, uint32_t domid, unsigned long nr_pages) { int err; struct xen_memory_reservation reservation = { .nr_extents = nr_pages, .extent_order = 0, .mem_flags = 0, /* no flags */ .domid = domid }; set_xen_guest_handle(reservation.extent_start, HYPERCALL_BUFFER_NULL); err = do_memory_op(xch, XENMEM_claim_pages, &reservation, sizeof(reservation)); /* Ignore it if the hypervisor does not support the call. */ if (err == -1 && errno == ENOSYS) err = errno = 0; return err; } int xc_domain_populate_physmap(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start) { int err; DECLARE_HYPERCALL_BOUNCE(extent_start, nr_extents * sizeof(*extent_start), XC_HYPERCALL_BUFFER_BOUNCE_BOTH); struct xen_memory_reservation reservation = { .nr_extents = nr_extents, .extent_order = extent_order, .mem_flags = mem_flags, .domid = domid }; if ( xc_hypercall_bounce_pre(xch, extent_start) ) { PERROR("Could not bounce memory for XENMEM_populate_physmap hypercall"); return -1; } set_xen_guest_handle(reservation.extent_start, extent_start); err = do_memory_op(xch, XENMEM_populate_physmap, &reservation, sizeof(reservation)); xc_hypercall_bounce_post(xch, extent_start); return err; } int xc_domain_populate_physmap_exact(xc_interface *xch, uint32_t domid, unsigned long nr_extents, unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start) { int err; err = xc_domain_populate_physmap(xch, domid, nr_extents, extent_order, mem_flags, extent_start); if ( err == nr_extents ) return 0; if ( err >= 0 ) { DPRINTF("Failed allocation for dom %d: %ld extents of order %d\n", domid, nr_extents, extent_order); errno = EBUSY; err = -1; } return err; } int xc_domain_memory_exchange_pages(xc_interface *xch, int domid, unsigned long nr_in_extents, unsigned int in_order, xen_pfn_t *in_extents, unsigned long nr_out_extents, unsigned int out_order, xen_pfn_t *out_extents) { int rc = -1; DECLARE_HYPERCALL_BOUNCE(in_extents, nr_in_extents*sizeof(*in_extents), XC_HYPERCALL_BUFFER_BOUNCE_IN); DECLARE_HYPERCALL_BOUNCE(out_extents, nr_out_extents*sizeof(*out_extents), XC_HYPERCALL_BUFFER_BOUNCE_OUT); struct xen_memory_exchange exchange = { .in = { .nr_extents = nr_in_extents, .extent_order = in_order, .domid = domid }, .out = { .nr_extents = nr_out_extents, .extent_order = out_order, .domid = domid } }; if ( xc_hypercall_bounce_pre(xch, in_extents) || xc_hypercall_bounce_pre(xch, out_extents)) goto out; set_xen_guest_handle(exchange.in.extent_start, in_extents); set_xen_guest_handle(exchange.out.extent_start, out_extents); rc = do_memory_op(xch, XENMEM_exchange, &exchange, sizeof(exchange)); out: xc_hypercall_bounce_post(xch, in_extents); xc_hypercall_bounce_post(xch, out_extents); return rc; } /* Currently only implemented on x86. This cannot be handled in the * caller, e.g. by looking for errno==ENOSYS because of the broken * error reporting style. Once this is fixed then this condition can * be removed. */ #if defined(__i386__)||defined(__x86_64__) static int xc_domain_pod_target(xc_interface *xch, int op, uint32_t domid, uint64_t target_pages, uint64_t *tot_pages, uint64_t *pod_cache_pages, uint64_t *pod_entries) { int err; struct xen_pod_target pod_target = { .domid = domid, .target_pages = target_pages }; err = do_memory_op(xch, op, &pod_target, sizeof(pod_target)); if ( err < 0 ) { DPRINTF("Failed %s_pod_target dom %d\n", (op==XENMEM_set_pod_target)?"set":"get", domid); errno = -err; err = -1; } else err = 0; if ( tot_pages ) *tot_pages = pod_target.tot_pages; if ( pod_cache_pages ) *pod_cache_pages = pod_target.pod_cache_pages; if ( pod_entries ) *pod_entries = pod_target.pod_entries; return err; } int xc_domain_set_pod_target(xc_interface *xch, uint32_t domid, uint64_t target_pages, uint64_t *tot_pages, uint64_t *pod_cache_pages, uint64_t *pod_entries) { return xc_domain_pod_target(xch, XENMEM_set_pod_target, domid, target_pages, tot_pages, pod_cache_pages, pod_entries); } int xc_domain_get_pod_target(xc_interface *xch, uint32_t domid, uint64_t *tot_pages, uint64_t *pod_cache_pages, uint64_t *pod_entries) { return xc_domain_pod_target(xch, XENMEM_get_pod_target, domid, -1, tot_pages, pod_cache_pages, pod_entries); } #else int xc_domain_set_pod_target(xc_interface *xch, uint32_t domid, uint64_t target_pages, uint64_t *tot_pages, uint64_t *pod_cache_pages, uint64_t *pod_entries) { return 0; } int xc_domain_get_pod_target(xc_interface *xch, uint32_t domid, uint64_t *tot_pages, uint64_t *pod_cache_pages, uint64_t *pod_entries) { /* On x86 (above) xc_domain_pod_target will incorrectly return -1 * with errno==-1 on error. Do the same for least surprise. */ errno = -1; return -1; } #endif int xc_domain_max_vcpus(xc_interface *xch, uint32_t domid, unsigned int max) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_max_vcpus; domctl.domain = (domid_t)domid; domctl.u.max_vcpus.max = max; return do_domctl(xch, &domctl); } int xc_domain_sethandle(xc_interface *xch, uint32_t domid, xen_domain_handle_t handle) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_setdomainhandle; domctl.domain = (domid_t)domid; memcpy(domctl.u.setdomainhandle.handle, handle, sizeof(xen_domain_handle_t)); return do_domctl(xch, &domctl); } int xc_vcpu_getinfo(xc_interface *xch, uint32_t domid, uint32_t vcpu, xc_vcpuinfo_t *info) { int rc; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_getvcpuinfo; domctl.domain = (domid_t)domid; domctl.u.getvcpuinfo.vcpu = (uint16_t)vcpu; rc = do_domctl(xch, &domctl); memcpy(info, &domctl.u.getvcpuinfo, sizeof(*info)); return rc; } int xc_domain_ioport_permission(xc_interface *xch, uint32_t domid, uint32_t first_port, uint32_t nr_ports, uint32_t allow_access) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_ioport_permission; domctl.domain = (domid_t)domid; domctl.u.ioport_permission.first_port = first_port; domctl.u.ioport_permission.nr_ports = nr_ports; domctl.u.ioport_permission.allow_access = allow_access; return do_domctl(xch, &domctl); } int xc_availheap(xc_interface *xch, int min_width, int max_width, int node, uint64_t *bytes) { DECLARE_SYSCTL; int rc; sysctl.cmd = XEN_SYSCTL_availheap; sysctl.u.availheap.min_bitwidth = min_width; sysctl.u.availheap.max_bitwidth = max_width; sysctl.u.availheap.node = node; rc = xc_sysctl(xch, &sysctl); *bytes = sysctl.u.availheap.avail_bytes; return rc; } int xc_vcpu_setcontext(xc_interface *xch, uint32_t domid, uint32_t vcpu, vcpu_guest_context_any_t *ctxt) { DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(ctxt, sizeof(vcpu_guest_context_any_t), XC_HYPERCALL_BUFFER_BOUNCE_IN); int rc; if ( xc_hypercall_bounce_pre(xch, ctxt) ) return -1; domctl.cmd = XEN_DOMCTL_setvcpucontext; domctl.domain = domid; domctl.u.vcpucontext.vcpu = vcpu; set_xen_guest_handle(domctl.u.vcpucontext.ctxt, ctxt); rc = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, ctxt); return rc; } int xc_domain_irq_permission(xc_interface *xch, uint32_t domid, uint8_t pirq, uint8_t allow_access) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_irq_permission; domctl.domain = domid; domctl.u.irq_permission.pirq = pirq; domctl.u.irq_permission.allow_access = allow_access; return do_domctl(xch, &domctl); } int xc_domain_iomem_permission(xc_interface *xch, uint32_t domid, unsigned long first_mfn, unsigned long nr_mfns, uint8_t allow_access) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_iomem_permission; domctl.domain = domid; domctl.u.iomem_permission.first_mfn = first_mfn; domctl.u.iomem_permission.nr_mfns = nr_mfns; domctl.u.iomem_permission.allow_access = allow_access; return do_domctl(xch, &domctl); } int xc_domain_send_trigger(xc_interface *xch, uint32_t domid, uint32_t trigger, uint32_t vcpu) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_sendtrigger; domctl.domain = domid; domctl.u.sendtrigger.trigger = trigger; domctl.u.sendtrigger.vcpu = vcpu; return do_domctl(xch, &domctl); } static inline int xc_hvm_param_deprecated_check(uint32_t param) { switch ( param ) { case HVM_PARAM_MEMORY_EVENT_CR0: case HVM_PARAM_MEMORY_EVENT_CR3: case HVM_PARAM_MEMORY_EVENT_CR4: case HVM_PARAM_MEMORY_EVENT_INT3: case HVM_PARAM_MEMORY_EVENT_SINGLE_STEP: case HVM_PARAM_MEMORY_EVENT_MSR: return -EOPNOTSUPP; default: break; }; return 0; } int xc_hvm_param_set(xc_interface *handle, domid_t dom, uint32_t param, uint64_t value) { DECLARE_HYPERCALL_BUFFER(xen_hvm_param_t, arg); int rc = xc_hvm_param_deprecated_check(param); if ( rc ) return rc; arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->domid = dom; arg->index = param; arg->value = value; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_set_param, HYPERCALL_BUFFER_AS_ARG(arg)); xc_hypercall_buffer_free(handle, arg); return rc; } int xc_hvm_param_get(xc_interface *handle, domid_t dom, uint32_t param, uint64_t *value) { DECLARE_HYPERCALL_BUFFER(xen_hvm_param_t, arg); int rc = xc_hvm_param_deprecated_check(param); if ( rc ) return rc; arg = xc_hypercall_buffer_alloc(handle, arg, sizeof(*arg)); if ( arg == NULL ) return -1; arg->domid = dom; arg->index = param; rc = xencall2(handle->xcall, __HYPERVISOR_hvm_op, HVMOP_get_param, HYPERCALL_BUFFER_AS_ARG(arg)); *value = arg->value; xc_hypercall_buffer_free(handle, arg); return rc; } int xc_set_hvm_param(xc_interface *handle, domid_t dom, int param, unsigned long value) { return xc_hvm_param_set(handle, dom, param, value); } int xc_get_hvm_param(xc_interface *handle, domid_t dom, int param, unsigned long *value) { uint64_t v; int ret; ret = xc_hvm_param_get(handle, dom, param, &v); if (ret < 0) return ret; *value = v; return 0; } int xc_domain_setdebugging(xc_interface *xch, uint32_t domid, unsigned int enable) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_setdebugging; domctl.domain = domid; domctl.u.setdebugging.enable = enable; return do_domctl(xch, &domctl); } int xc_assign_device( xc_interface *xch, uint32_t domid, uint32_t machine_sbdf, uint32_t flag) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_assign_device; domctl.domain = domid; domctl.u.assign_device.dev = XEN_DOMCTL_DEV_PCI; domctl.u.assign_device.u.pci.machine_sbdf = machine_sbdf; domctl.u.assign_device.flag = flag; return do_domctl(xch, &domctl); } int xc_get_device_group( xc_interface *xch, uint32_t domid, uint32_t machine_sbdf, uint32_t max_sdevs, uint32_t *num_sdevs, uint32_t *sdev_array) { int rc; DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(sdev_array, max_sdevs * sizeof(*sdev_array), XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, sdev_array) ) { PERROR("Could not bounce buffer for xc_get_device_group"); return -1; } domctl.cmd = XEN_DOMCTL_get_device_group; domctl.domain = (domid_t)domid; domctl.u.get_device_group.machine_sbdf = machine_sbdf; domctl.u.get_device_group.max_sdevs = max_sdevs; set_xen_guest_handle(domctl.u.get_device_group.sdev_array, sdev_array); rc = do_domctl(xch, &domctl); *num_sdevs = domctl.u.get_device_group.num_sdevs; xc_hypercall_bounce_post(xch, sdev_array); return rc; } int xc_test_assign_device( xc_interface *xch, uint32_t domid, uint32_t machine_sbdf) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_test_assign_device; domctl.domain = domid; domctl.u.assign_device.dev = XEN_DOMCTL_DEV_PCI; domctl.u.assign_device.u.pci.machine_sbdf = machine_sbdf; return do_domctl(xch, &domctl); } int xc_deassign_device( xc_interface *xch, uint32_t domid, uint32_t machine_sbdf) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_deassign_device; domctl.domain = domid; domctl.u.assign_device.dev = XEN_DOMCTL_DEV_PCI; domctl.u.assign_device.u.pci.machine_sbdf = machine_sbdf; return do_domctl(xch, &domctl); } int xc_assign_dt_device( xc_interface *xch, uint32_t domid, char *path) { int rc; size_t size = strlen(path); DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(path, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, path) ) return -1; domctl.cmd = XEN_DOMCTL_assign_device; domctl.domain = (domid_t)domid; domctl.u.assign_device.dev = XEN_DOMCTL_DEV_DT; domctl.u.assign_device.u.dt.size = size; /* * DT doesn't own any RDM so actually DT has nothing to do * for any flag and here just fix that as 0. */ domctl.u.assign_device.flag = 0; set_xen_guest_handle(domctl.u.assign_device.u.dt.path, path); rc = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, path); return rc; } int xc_test_assign_dt_device( xc_interface *xch, uint32_t domid, char *path) { int rc; size_t size = strlen(path); DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(path, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, path) ) return -1; domctl.cmd = XEN_DOMCTL_test_assign_device; domctl.domain = (domid_t)domid; domctl.u.assign_device.dev = XEN_DOMCTL_DEV_DT; domctl.u.assign_device.u.dt.size = size; set_xen_guest_handle(domctl.u.assign_device.u.dt.path, path); rc = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, path); return rc; } int xc_deassign_dt_device( xc_interface *xch, uint32_t domid, char *path) { int rc; size_t size = strlen(path); DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(path, size, XC_HYPERCALL_BUFFER_BOUNCE_IN); if ( xc_hypercall_bounce_pre(xch, path) ) return -1; domctl.cmd = XEN_DOMCTL_deassign_device; domctl.domain = (domid_t)domid; domctl.u.assign_device.dev = XEN_DOMCTL_DEV_DT; domctl.u.assign_device.u.dt.size = size; set_xen_guest_handle(domctl.u.assign_device.u.dt.path, path); rc = do_domctl(xch, &domctl); xc_hypercall_bounce_post(xch, path); return rc; } int xc_domain_update_msi_irq( xc_interface *xch, uint32_t domid, uint32_t gvec, uint32_t pirq, uint32_t gflags, uint64_t gtable) { int rc; xen_domctl_bind_pt_irq_t *bind; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_bind_pt_irq; domctl.domain = (domid_t)domid; bind = &(domctl.u.bind_pt_irq); bind->hvm_domid = domid; bind->irq_type = PT_IRQ_TYPE_MSI; bind->machine_irq = pirq; bind->u.msi.gvec = gvec; bind->u.msi.gflags = gflags; bind->u.msi.gtable = gtable; rc = do_domctl(xch, &domctl); return rc; } int xc_domain_unbind_msi_irq( xc_interface *xch, uint32_t domid, uint32_t gvec, uint32_t pirq, uint32_t gflags) { int rc; xen_domctl_bind_pt_irq_t *bind; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_unbind_pt_irq; domctl.domain = (domid_t)domid; bind = &(domctl.u.bind_pt_irq); bind->hvm_domid = domid; bind->irq_type = PT_IRQ_TYPE_MSI; bind->machine_irq = pirq; bind->u.msi.gvec = gvec; bind->u.msi.gflags = gflags; rc = do_domctl(xch, &domctl); return rc; } /* Pass-through: binds machine irq to guests irq */ static int xc_domain_bind_pt_irq_int( xc_interface *xch, uint32_t domid, uint32_t machine_irq, uint8_t irq_type, uint8_t bus, uint8_t device, uint8_t intx, uint8_t isa_irq, uint16_t spi) { int rc; xen_domctl_bind_pt_irq_t * bind; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_bind_pt_irq; domctl.domain = (domid_t)domid; bind = &(domctl.u.bind_pt_irq); bind->hvm_domid = domid; bind->irq_type = irq_type; bind->machine_irq = machine_irq; switch ( irq_type ) { case PT_IRQ_TYPE_PCI: case PT_IRQ_TYPE_MSI_TRANSLATE: bind->u.pci.bus = bus; bind->u.pci.device = device; bind->u.pci.intx = intx; break; case PT_IRQ_TYPE_ISA: bind->u.isa.isa_irq = isa_irq; break; case PT_IRQ_TYPE_SPI: bind->u.spi.spi = spi; break; default: errno = EINVAL; return -1; } rc = do_domctl(xch, &domctl); return rc; } int xc_domain_bind_pt_irq( xc_interface *xch, uint32_t domid, uint8_t machine_irq, uint8_t irq_type, uint8_t bus, uint8_t device, uint8_t intx, uint8_t isa_irq) { return xc_domain_bind_pt_irq_int(xch, domid, machine_irq, irq_type, bus, device, intx, isa_irq, 0); } static int xc_domain_unbind_pt_irq_int( xc_interface *xch, uint32_t domid, uint32_t machine_irq, uint8_t irq_type, uint8_t bus, uint8_t device, uint8_t intx, uint8_t isa_irq, uint8_t spi) { int rc; xen_domctl_bind_pt_irq_t * bind; DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_unbind_pt_irq; domctl.domain = (domid_t)domid; bind = &(domctl.u.bind_pt_irq); bind->hvm_domid = domid; bind->irq_type = irq_type; bind->machine_irq = machine_irq; switch ( irq_type ) { case PT_IRQ_TYPE_PCI: case PT_IRQ_TYPE_MSI_TRANSLATE: bind->u.pci.bus = bus; bind->u.pci.device = device; bind->u.pci.intx = intx; break; case PT_IRQ_TYPE_ISA: bind->u.isa.isa_irq = isa_irq; break; case PT_IRQ_TYPE_SPI: bind->u.spi.spi = spi; break; default: errno = EINVAL; return -1; } rc = do_domctl(xch, &domctl); return rc; } int xc_domain_unbind_pt_irq( xc_interface *xch, uint32_t domid, uint8_t machine_irq, uint8_t irq_type, uint8_t bus, uint8_t device, uint8_t intx, uint8_t isa_irq) { return xc_domain_unbind_pt_irq_int(xch, domid, machine_irq, irq_type, bus, device, intx, isa_irq, 0); } int xc_domain_bind_pt_pci_irq( xc_interface *xch, uint32_t domid, uint8_t machine_irq, uint8_t bus, uint8_t device, uint8_t intx) { return (xc_domain_bind_pt_irq(xch, domid, machine_irq, PT_IRQ_TYPE_PCI, bus, device, intx, 0)); } int xc_domain_bind_pt_isa_irq( xc_interface *xch, uint32_t domid, uint8_t machine_irq) { return (xc_domain_bind_pt_irq(xch, domid, machine_irq, PT_IRQ_TYPE_ISA, 0, 0, 0, machine_irq)); } int xc_domain_bind_pt_spi_irq( xc_interface *xch, uint32_t domid, uint16_t vspi, uint16_t spi) { return (xc_domain_bind_pt_irq_int(xch, domid, vspi, PT_IRQ_TYPE_SPI, 0, 0, 0, 0, spi)); } int xc_domain_unbind_pt_spi_irq(xc_interface *xch, uint32_t domid, uint16_t vspi, uint16_t spi) { return (xc_domain_unbind_pt_irq_int(xch, domid, vspi, PT_IRQ_TYPE_SPI, 0, 0, 0, 0, spi)); } int xc_unmap_domain_meminfo(xc_interface *xch, struct xc_domain_meminfo *minfo) { struct domain_info_context _di = { .guest_width = minfo->guest_width, .p2m_size = minfo->p2m_size}; struct domain_info_context *dinfo = &_di; free(minfo->pfn_type); if ( minfo->p2m_table ) munmap(minfo->p2m_table, P2M_FL_ENTRIES * PAGE_SIZE); minfo->p2m_table = NULL; return 0; } int xc_map_domain_meminfo(xc_interface *xch, int domid, struct xc_domain_meminfo *minfo) { struct domain_info_context _di; struct domain_info_context *dinfo = &_di; xc_dominfo_t info; shared_info_any_t *live_shinfo; xen_capabilities_info_t xen_caps = ""; int i; /* Only be initialized once */ if ( minfo->pfn_type || minfo->p2m_table ) { errno = EINVAL; return -1; } if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 ) { PERROR("Could not get domain info"); return -1; } if ( xc_domain_get_guest_width(xch, domid, &minfo->guest_width) ) { PERROR("Could not get domain address size"); return -1; } _di.guest_width = minfo->guest_width; /* Get page table levels (see get_platform_info() in xg_save_restore.h */ if ( xc_version(xch, XENVER_capabilities, &xen_caps) ) { PERROR("Could not get Xen capabilities (for page table levels)"); return -1; } if ( strstr(xen_caps, "xen-3.0-x86_64") ) /* Depends on whether it's a compat 32-on-64 guest */ minfo->pt_levels = ( (minfo->guest_width == 8) ? 4 : 3 ); else if ( strstr(xen_caps, "xen-3.0-x86_32p") ) minfo->pt_levels = 3; else if ( strstr(xen_caps, "xen-3.0-x86_32") ) minfo->pt_levels = 2; else { errno = EFAULT; return -1; } /* We need the shared info page for mapping the P2M */ live_shinfo = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ, info.shared_info_frame); if ( !live_shinfo ) { PERROR("Could not map the shared info frame (MFN 0x%lx)", info.shared_info_frame); return -1; } if ( xc_core_arch_map_p2m_writable(xch, minfo->guest_width, &info, live_shinfo, &minfo->p2m_table, &minfo->p2m_size) ) { PERROR("Could not map the P2M table"); munmap(live_shinfo, PAGE_SIZE); return -1; } munmap(live_shinfo, PAGE_SIZE); _di.p2m_size = minfo->p2m_size; /* Make space and prepare for getting the PFN types */ minfo->pfn_type = calloc(sizeof(*minfo->pfn_type), minfo->p2m_size); if ( !minfo->pfn_type ) { PERROR("Could not allocate memory for the PFN types"); goto failed; } for ( i = 0; i < minfo->p2m_size; i++ ) minfo->pfn_type[i] = xc_pfn_to_mfn(i, minfo->p2m_table, minfo->guest_width); /* Retrieve PFN types in batches */ for ( i = 0; i < minfo->p2m_size ; i+=1024 ) { int count = ((minfo->p2m_size - i ) > 1024 ) ? 1024: (minfo->p2m_size - i); if ( xc_get_pfn_type_batch(xch, domid, count, minfo->pfn_type + i) ) { PERROR("Could not get %d-eth batch of PFN types", (i+1)/1024); goto failed; } } return 0; failed: if ( minfo->pfn_type ) { free(minfo->pfn_type); minfo->pfn_type = NULL; } if ( minfo->p2m_table ) { munmap(minfo->p2m_table, P2M_FL_ENTRIES * PAGE_SIZE); minfo->p2m_table = NULL; } return -1; } int xc_domain_memory_mapping( xc_interface *xch, uint32_t domid, unsigned long first_gfn, unsigned long first_mfn, unsigned long nr_mfns, uint32_t add_mapping) { DECLARE_DOMCTL; xc_dominfo_t info; int ret = 0, rc; unsigned long done = 0, nr, max_batch_sz; if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 || info.domid != domid ) { PERROR("Could not get info for domain"); return -EINVAL; } if ( !xc_core_arch_auto_translated_physmap(&info) ) return 0; if ( !nr_mfns ) return 0; domctl.cmd = XEN_DOMCTL_memory_mapping; domctl.domain = domid; domctl.u.memory_mapping.add_mapping = add_mapping; max_batch_sz = nr_mfns; do { nr = min_t(unsigned long, nr_mfns - done, max_batch_sz); domctl.u.memory_mapping.nr_mfns = nr; domctl.u.memory_mapping.first_gfn = first_gfn + done; domctl.u.memory_mapping.first_mfn = first_mfn + done; rc = do_domctl(xch, &domctl); if ( rc < 0 && errno == E2BIG ) { if ( max_batch_sz <= 1 ) break; max_batch_sz >>= 1; continue; } if ( rc > 0 ) { done += rc; continue; } /* Save the first error... */ if ( !ret ) ret = rc; /* .. and ignore the rest of them when removing. */ if ( rc && add_mapping != DPCI_REMOVE_MAPPING ) break; done += nr; } while ( done < nr_mfns ); /* * Undo what we have done unless unmapping, by unmapping the entire region. * Errors here are ignored. */ if ( ret && add_mapping != DPCI_REMOVE_MAPPING ) xc_domain_memory_mapping(xch, domid, first_gfn, first_mfn, nr_mfns, DPCI_REMOVE_MAPPING); /* We might get E2BIG so many times that we never advance. */ if ( !done && !ret ) ret = -1; return ret; } int xc_domain_ioport_mapping( xc_interface *xch, uint32_t domid, uint32_t first_gport, uint32_t first_mport, uint32_t nr_ports, uint32_t add_mapping) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_ioport_mapping; domctl.domain = domid; domctl.u.ioport_mapping.first_gport = first_gport; domctl.u.ioport_mapping.first_mport = first_mport; domctl.u.ioport_mapping.nr_ports = nr_ports; domctl.u.ioport_mapping.add_mapping = add_mapping; return do_domctl(xch, &domctl); } int xc_domain_set_target( xc_interface *xch, uint32_t domid, uint32_t target) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_set_target; domctl.domain = domid; domctl.u.set_target.target = target; return do_domctl(xch, &domctl); } int xc_domain_subscribe_for_suspend( xc_interface *xch, domid_t dom, evtchn_port_t port) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_subscribe; domctl.domain = dom; domctl.u.subscribe.port = port; return do_domctl(xch, &domctl); } int xc_domain_set_machine_address_size(xc_interface *xch, uint32_t domid, unsigned int width) { DECLARE_DOMCTL; memset(&domctl, 0, sizeof(domctl)); domctl.domain = domid; domctl.cmd = XEN_DOMCTL_set_machine_address_size; domctl.u.address_size.size = width; return do_domctl(xch, &domctl); } int xc_domain_get_machine_address_size(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; int rc; memset(&domctl, 0, sizeof(domctl)); domctl.domain = domid; domctl.cmd = XEN_DOMCTL_get_machine_address_size; rc = do_domctl(xch, &domctl); return rc == 0 ? domctl.u.address_size.size : rc; } int xc_domain_suppress_spurious_page_faults(xc_interface *xc, uint32_t domid) { DECLARE_DOMCTL; memset(&domctl, 0, sizeof(domctl)); domctl.domain = domid; domctl.cmd = XEN_DOMCTL_suppress_spurious_page_faults; return do_domctl(xc, &domctl); } int xc_domain_debug_control(xc_interface *xc, uint32_t domid, uint32_t sop, uint32_t vcpu) { DECLARE_DOMCTL; memset(&domctl, 0, sizeof(domctl)); domctl.domain = (domid_t)domid; domctl.cmd = XEN_DOMCTL_debug_op; domctl.u.debug_op.op = sop; domctl.u.debug_op.vcpu = vcpu; return do_domctl(xc, &domctl); } int xc_domain_p2m_audit(xc_interface *xch, uint32_t domid, uint64_t *orphans, uint64_t *m2p_bad, uint64_t *p2m_bad) { DECLARE_DOMCTL; int rc; domctl.cmd = XEN_DOMCTL_audit_p2m; domctl.domain = domid; rc = do_domctl(xch, &domctl); *orphans = domctl.u.audit_p2m.orphans; *m2p_bad = domctl.u.audit_p2m.m2p_bad; *p2m_bad = domctl.u.audit_p2m.p2m_bad; return rc; } int xc_domain_set_access_required(xc_interface *xch, uint32_t domid, unsigned int required) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_set_access_required; domctl.domain = domid; domctl.u.access_required.access_required = required; return do_domctl(xch, &domctl); } int xc_domain_set_virq_handler(xc_interface *xch, uint32_t domid, int virq) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_set_virq_handler; domctl.domain = domid; domctl.u.set_virq_handler.virq = virq; return do_domctl(xch, &domctl); } int xc_domain_set_max_evtchn(xc_interface *xch, uint32_t domid, uint32_t max_port) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_set_max_evtchn; domctl.domain = domid; domctl.u.set_max_evtchn.max_port = max_port; return do_domctl(xch, &domctl); } /* Plumbing Xen with vNUMA topology */ int xc_domain_setvnuma(xc_interface *xch, uint32_t domid, uint32_t nr_vnodes, uint32_t nr_vmemranges, uint32_t nr_vcpus, xen_vmemrange_t *vmemrange, unsigned int *vdistance, unsigned int *vcpu_to_vnode, unsigned int *vnode_to_pnode) { int rc; DECLARE_DOMCTL; DECLARE_HYPERCALL_BOUNCE(vmemrange, sizeof(*vmemrange) * nr_vmemranges, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); DECLARE_HYPERCALL_BOUNCE(vdistance, sizeof(*vdistance) * nr_vnodes * nr_vnodes, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); DECLARE_HYPERCALL_BOUNCE(vcpu_to_vnode, sizeof(*vcpu_to_vnode) * nr_vcpus, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); DECLARE_HYPERCALL_BOUNCE(vnode_to_pnode, sizeof(*vnode_to_pnode) * nr_vnodes, XC_HYPERCALL_BUFFER_BOUNCE_BOTH); errno = EINVAL; if ( nr_vnodes == 0 || nr_vmemranges == 0 || nr_vcpus == 0 ) return -1; if ( !vdistance || !vcpu_to_vnode || !vmemrange || !vnode_to_pnode ) { PERROR("%s: Cant set vnuma without initializing topology", __func__); return -1; } if ( xc_hypercall_bounce_pre(xch, vmemrange) || xc_hypercall_bounce_pre(xch, vdistance) || xc_hypercall_bounce_pre(xch, vcpu_to_vnode) || xc_hypercall_bounce_pre(xch, vnode_to_pnode) ) { rc = -1; goto vnumaset_fail; } set_xen_guest_handle(domctl.u.vnuma.vmemrange, vmemrange); set_xen_guest_handle(domctl.u.vnuma.vdistance, vdistance); set_xen_guest_handle(domctl.u.vnuma.vcpu_to_vnode, vcpu_to_vnode); set_xen_guest_handle(domctl.u.vnuma.vnode_to_pnode, vnode_to_pnode); domctl.cmd = XEN_DOMCTL_setvnumainfo; domctl.domain = (domid_t)domid; domctl.u.vnuma.nr_vnodes = nr_vnodes; domctl.u.vnuma.nr_vmemranges = nr_vmemranges; domctl.u.vnuma.nr_vcpus = nr_vcpus; domctl.u.vnuma.pad = 0; rc = do_domctl(xch, &domctl); vnumaset_fail: xc_hypercall_bounce_post(xch, vmemrange); xc_hypercall_bounce_post(xch, vdistance); xc_hypercall_bounce_post(xch, vcpu_to_vnode); xc_hypercall_bounce_post(xch, vnode_to_pnode); return rc; } int xc_domain_getvnuma(xc_interface *xch, uint32_t domid, uint32_t *nr_vnodes, uint32_t *nr_vmemranges, uint32_t *nr_vcpus, xen_vmemrange_t *vmemrange, unsigned int *vdistance, unsigned int *vcpu_to_vnode) { int rc; DECLARE_HYPERCALL_BOUNCE(vmemrange, sizeof(*vmemrange) * *nr_vmemranges, XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_HYPERCALL_BOUNCE(vdistance, sizeof(*vdistance) * *nr_vnodes * *nr_vnodes, XC_HYPERCALL_BUFFER_BOUNCE_OUT); DECLARE_HYPERCALL_BOUNCE(vcpu_to_vnode, sizeof(*vcpu_to_vnode) * *nr_vcpus, XC_HYPERCALL_BUFFER_BOUNCE_OUT); struct xen_vnuma_topology_info vnuma_topo; if ( xc_hypercall_bounce_pre(xch, vmemrange) || xc_hypercall_bounce_pre(xch, vdistance) || xc_hypercall_bounce_pre(xch, vcpu_to_vnode) ) { rc = -1; errno = ENOMEM; goto vnumaget_fail; } set_xen_guest_handle(vnuma_topo.vmemrange.h, vmemrange); set_xen_guest_handle(vnuma_topo.vdistance.h, vdistance); set_xen_guest_handle(vnuma_topo.vcpu_to_vnode.h, vcpu_to_vnode); vnuma_topo.nr_vnodes = *nr_vnodes; vnuma_topo.nr_vcpus = *nr_vcpus; vnuma_topo.nr_vmemranges = *nr_vmemranges; vnuma_topo.domid = domid; vnuma_topo.pad = 0; rc = do_memory_op(xch, XENMEM_get_vnumainfo, &vnuma_topo, sizeof(vnuma_topo)); *nr_vnodes = vnuma_topo.nr_vnodes; *nr_vcpus = vnuma_topo.nr_vcpus; *nr_vmemranges = vnuma_topo.nr_vmemranges; vnumaget_fail: xc_hypercall_bounce_post(xch, vmemrange); xc_hypercall_bounce_post(xch, vdistance); xc_hypercall_bounce_post(xch, vcpu_to_vnode); return rc; } int xc_domain_soft_reset(xc_interface *xch, uint32_t domid) { DECLARE_DOMCTL; domctl.cmd = XEN_DOMCTL_soft_reset; domctl.domain = (domid_t)domid; return do_domctl(xch, &domctl); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxc/xc_dom_bzimageloader.c0000664000175000017500000005202113256712137017220 0ustar smbsmb/* * Xen domain builder -- bzImage bits * * Parse and load bzImage kernel images. * * This relies on version 2.08 of the boot protocol, which contains an * ELF file embedded in the bzImage. The loader extracts this ELF * image and passes it off to the standard ELF loader. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * written 2006 by Gerd Hoffmann . * written 2007 by Jeremy Fitzhardinge * written 2008 by Ian Campbell * written 2009 by Chris Lalancette * */ #include #include #include #include "xg_private.h" #include "xc_dom_decompress.h" #include #ifndef __MINIOS__ #if defined(HAVE_BZLIB) #include static int xc_try_bzip2_decode( struct xc_dom_image *dom, void **blob, size_t *size) { bz_stream stream; int ret; char *out_buf; char *tmp_buf; int retval = -1; unsigned int outsize; uint64_t total; stream.bzalloc = NULL; stream.bzfree = NULL; stream.opaque = NULL; if ( dom->kernel_size == 0) { DOMPRINTF("BZIP2: Input is 0 size"); return -1; } ret = BZ2_bzDecompressInit(&stream, 0, 0); if ( ret != BZ_OK ) { DOMPRINTF("BZIP2: Error initting stream"); return -1; } /* sigh. We don't know up-front how much memory we are going to need * for the output buffer. Allocate the output buffer to be equal * the input buffer to start, and we'll realloc as needed. */ outsize = dom->kernel_size; /* * stream.avail_in and outsize are unsigned int, while kernel_size * is a size_t. Check we aren't overflowing. */ if ( outsize != dom->kernel_size ) { DOMPRINTF("BZIP2: Input too large"); goto bzip2_cleanup; } out_buf = malloc(outsize); if ( out_buf == NULL ) { DOMPRINTF("BZIP2: Failed to alloc memory"); goto bzip2_cleanup; } stream.next_in = dom->kernel_blob; stream.avail_in = dom->kernel_size; stream.next_out = out_buf; stream.avail_out = dom->kernel_size; for ( ; ; ) { ret = BZ2_bzDecompress(&stream); if ( ret == BZ_STREAM_END ) { DOMPRINTF("BZIP2: Saw data stream end"); retval = 0; break; } if ( ret != BZ_OK ) { DOMPRINTF("BZIP2: error %d", ret); free(out_buf); goto bzip2_cleanup; } if ( stream.avail_out == 0 ) { /* Protect against output buffer overflow */ if ( outsize > UINT_MAX / 2 ) { DOMPRINTF("BZIP2: output buffer overflow"); free(out_buf); goto bzip2_cleanup; } if ( xc_dom_kernel_check_size(dom, outsize * 2) ) { DOMPRINTF("BZIP2: output too large"); free(out_buf); goto bzip2_cleanup; } tmp_buf = realloc(out_buf, outsize * 2); if ( tmp_buf == NULL ) { DOMPRINTF("BZIP2: Failed to realloc memory"); free(out_buf); goto bzip2_cleanup; } out_buf = tmp_buf; stream.next_out = out_buf + outsize; stream.avail_out = (outsize * 2) - outsize; outsize *= 2; } else if ( stream.avail_in == 0 ) { /* * If there is output buffer available then this indicates * that BZ2_bzDecompress would like more input data to be * provided. However our complete input buffer is in * memory and provided upfront so if avail_in is zero this * actually indicates a truncated input. */ DOMPRINTF("BZIP2: not enough input"); free(out_buf); goto bzip2_cleanup; } } total = (((uint64_t)stream.total_out_hi32) << 32) | stream.total_out_lo32; if ( xc_dom_register_external(dom, out_buf, total) ) { DOMPRINTF("BZIP2: Error registering stream output"); free(out_buf); goto bzip2_cleanup; } DOMPRINTF("%s: BZIP2 decompress OK, 0x%zx -> 0x%lx", __FUNCTION__, *size, (long unsigned int) total); *blob = out_buf; *size = total; bzip2_cleanup: BZ2_bzDecompressEnd(&stream); return retval; } #else /* !defined(HAVE_BZLIB) */ static int xc_try_bzip2_decode( struct xc_dom_image *dom, void **blob, size_t *size) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: BZIP2 decompress support unavailable", __FUNCTION__); return -1; } #endif #if defined(HAVE_LZMA) #include static int _xc_try_lzma_decode( struct xc_dom_image *dom, void **blob, size_t *size, lzma_stream *stream, const char *what) { lzma_ret ret; lzma_action action = LZMA_RUN; unsigned char *out_buf; unsigned char *tmp_buf; int retval = -1; size_t outsize; const char *msg; if ( dom->kernel_size == 0) { DOMPRINTF("%s: Input is 0 size", what); return -1; } /* sigh. We don't know up-front how much memory we are going to need * for the output buffer. Allocate the output buffer to be equal * the input buffer to start, and we'll realloc as needed. */ outsize = dom->kernel_size; out_buf = malloc(outsize); if ( out_buf == NULL ) { DOMPRINTF("%s: Failed to alloc memory", what); goto lzma_cleanup; } stream->next_in = dom->kernel_blob; stream->avail_in = dom->kernel_size; stream->next_out = out_buf; stream->avail_out = dom->kernel_size; for ( ; ; ) { ret = lzma_code(stream, action); if ( ret == LZMA_STREAM_END ) { DOMPRINTF("%s: Saw data stream end", what); retval = 0; break; } if ( ret != LZMA_OK ) { switch ( ret ) { case LZMA_MEM_ERROR: msg = strerror(ENOMEM); break; case LZMA_MEMLIMIT_ERROR: msg = "Memory usage limit reached"; break; case LZMA_FORMAT_ERROR: msg = "File format not recognized"; break; case LZMA_OPTIONS_ERROR: // FIXME: Better message? msg = "Unsupported compression options"; break; case LZMA_DATA_ERROR: msg = "File is corrupt"; break; case LZMA_BUF_ERROR: msg = "Unexpected end of input"; break; default: msg = "Internal program error (bug)"; break; } DOMPRINTF("%s: %s decompression error: %s", __FUNCTION__, what, msg); free(out_buf); goto lzma_cleanup; } if ( stream->avail_out == 0 ) { /* Protect against output buffer overflow */ if ( outsize > SIZE_MAX / 2 ) { DOMPRINTF("%s: output buffer overflow", what); free(out_buf); goto lzma_cleanup; } if ( xc_dom_kernel_check_size(dom, outsize * 2) ) { DOMPRINTF("%s: output too large", what); free(out_buf); goto lzma_cleanup; } tmp_buf = realloc(out_buf, outsize * 2); if ( tmp_buf == NULL ) { DOMPRINTF("%s: Failed to realloc memory", what); free(out_buf); goto lzma_cleanup; } out_buf = tmp_buf; stream->next_out = out_buf + outsize; stream->avail_out = (outsize * 2) - outsize; outsize *= 2; } } if ( xc_dom_register_external(dom, out_buf, stream->total_out) ) { DOMPRINTF("%s: Error registering stream output", what); free(out_buf); goto lzma_cleanup; } DOMPRINTF("%s: %s decompress OK, 0x%zx -> 0x%zx", __FUNCTION__, what, *size, (size_t)stream->total_out); *blob = out_buf; *size = stream->total_out; lzma_cleanup: lzma_end(stream); return retval; } /* 128 Mb is the minimum size (half-way) documented to work for all inputs. */ #define LZMA_BLOCK_SIZE (128*1024*1024) static int xc_try_xz_decode( struct xc_dom_image *dom, void **blob, size_t *size) { lzma_stream stream = LZMA_STREAM_INIT; if ( lzma_stream_decoder(&stream, LZMA_BLOCK_SIZE, 0) != LZMA_OK ) { DOMPRINTF("XZ: Failed to init decoder"); return -1; } return _xc_try_lzma_decode(dom, blob, size, &stream, "XZ"); } static int xc_try_lzma_decode( struct xc_dom_image *dom, void **blob, size_t *size) { lzma_stream stream = LZMA_STREAM_INIT; if ( lzma_alone_decoder(&stream, LZMA_BLOCK_SIZE) != LZMA_OK ) { DOMPRINTF("LZMA: Failed to init decoder"); return -1; } return _xc_try_lzma_decode(dom, blob, size, &stream, "LZMA"); } #else /* !defined(HAVE_LZMA) */ static int xc_try_xz_decode( struct xc_dom_image *dom, void **blob, size_t *size) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: XZ decompress support unavailable", __FUNCTION__); return -1; } static int xc_try_lzma_decode( struct xc_dom_image *dom, void **blob, size_t *size) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: LZMA decompress support unavailable", __FUNCTION__); return -1; } #endif #if defined(HAVE_LZO1X) #include #define LZOP_HEADER_HAS_FILTER 0x00000800 #define LZOP_MAX_BLOCK_SIZE (64*1024*1024) static inline uint_fast16_t lzo_read_16(const unsigned char *buf) { return buf[1] | (buf[0] << 8); } static inline uint_fast32_t lzo_read_32(const unsigned char *buf) { return lzo_read_16(buf + 2) | ((uint32_t)lzo_read_16(buf) << 16); } static int xc_try_lzo1x_decode( struct xc_dom_image *dom, void **blob, size_t *size) { int ret; const unsigned char *cur = dom->kernel_blob; unsigned char *out_buf = NULL; size_t left = dom->kernel_size; const char *msg; unsigned version; static const unsigned char magic[] = { 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a }; /* * lzo_uint should match size_t. Check that this is the case to be * sure we won't overflow various lzo_uint fields. */ BUILD_BUG_ON(sizeof(lzo_uint) != sizeof(size_t)); ret = lzo_init(); if ( ret != LZO_E_OK ) { DOMPRINTF("LZO1x: Failed to init library (%d)\n", ret); return -1; } if ( left < 16 || memcmp(cur, magic, 9) ) { DOMPRINTF("LZO1x: Unrecognized magic\n"); return -1; } /* get version (2bytes), skip library version (2), * 'need to be extracted' version (2) and method (1) */ version = lzo_read_16(cur + 9); cur += 16; left -= 16; if ( version >= 0x0940 ) { /* skip level */ ++cur; if ( left ) --left; } if ( left >= 4 && (lzo_read_32(cur) & LZOP_HEADER_HAS_FILTER) ) ret = 8; /* flags + filter info */ else ret = 4; /* flags */ /* skip mode and mtime_low */ ret += 8; if ( version >= 0x0940 ) ret += 4; /* skip mtime_high */ /* don't care about the file name, and skip checksum */ if ( left > ret ) ret += 1 + cur[ret] + 4; if ( left < ret ) { DOMPRINTF("LZO1x: Incomplete header\n"); return -1; } cur += ret; left -= ret; for ( *size = 0; ; ) { lzo_uint src_len, dst_len, out_len; unsigned char *tmp_buf; msg = "Short input"; if ( left < 4 ) break; dst_len = lzo_read_32(cur); if ( !dst_len ) { msg = "Error registering stream output"; if ( xc_dom_register_external(dom, out_buf, *size) ) break; return 0; } if ( dst_len > LZOP_MAX_BLOCK_SIZE ) { msg = "Block size too large"; break; } if ( left < 12 ) break; src_len = lzo_read_32(cur + 4); cur += 12; /* also skip block checksum info */ left -= 12; msg = "Bad source length"; if ( src_len <= 0 || src_len > dst_len || src_len > left ) break; msg = "Output buffer overflow"; if ( *size > SIZE_MAX - dst_len ) break; msg = "Decompressed image too large"; if ( xc_dom_kernel_check_size(dom, *size + dst_len) ) break; msg = "Failed to (re)alloc memory"; tmp_buf = realloc(out_buf, *size + dst_len); if ( tmp_buf == NULL ) break; out_buf = tmp_buf; out_len = dst_len; ret = lzo1x_decompress_safe(cur, src_len, out_buf + *size, &out_len, NULL); switch ( ret ) { case LZO_E_OK: msg = "Input underrun"; if ( out_len != dst_len ) break; *blob = out_buf; *size += out_len; cur += src_len; left -= src_len; continue; case LZO_E_INPUT_NOT_CONSUMED: msg = "Unconsumed input"; break; case LZO_E_OUTPUT_OVERRUN: msg = "Output overrun"; break; case LZO_E_INPUT_OVERRUN: msg = "Input overrun"; break; case LZO_E_LOOKBEHIND_OVERRUN: msg = "Look-behind overrun"; break; case LZO_E_EOF_NOT_FOUND: msg = "No EOF marker"; break; case LZO_E_ERROR: msg = "General error"; break; default: msg = "Internal program error (bug)"; break; } break; } free(out_buf); DOMPRINTF("LZO1x decompression error: %s\n", msg); return -1; } #else /* !defined(HAVE_LZO1X) */ static int xc_try_lzo1x_decode( struct xc_dom_image *dom, void **blob, size_t *size) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: LZO1x decompress support unavailable\n", __FUNCTION__); return -1; } #endif #else /* __MINIOS__ */ int xc_try_bzip2_decode(struct xc_dom_image *dom, void **blob, size_t *size); int xc_try_lzma_decode(struct xc_dom_image *dom, void **blob, size_t *size); int xc_try_lzo1x_decode(struct xc_dom_image *dom, void **blob, size_t *size); int xc_try_xz_decode(struct xc_dom_image *dom, void **blob, size_t *size); #endif /* !__MINIOS__ */ struct setup_header { uint8_t _pad0[0x1f1]; /* skip uninteresting stuff */ uint8_t setup_sects; uint16_t root_flags; uint32_t syssize; uint16_t ram_size; uint16_t vid_mode; uint16_t root_dev; uint16_t boot_flag; uint16_t jump; uint32_t header; #define HDR_MAGIC "HdrS" #define HDR_MAGIC_SZ 4 uint16_t version; #define VERSION(h,l) (((h)<<8) | (l)) uint32_t realmode_swtch; uint16_t start_sys; uint16_t kernel_version; uint8_t type_of_loader; uint8_t loadflags; uint16_t setup_move_size; uint32_t code32_start; uint32_t ramdisk_image; uint32_t ramdisk_size; uint32_t bootsect_kludge; uint16_t heap_end_ptr; uint16_t _pad1; uint32_t cmd_line_ptr; uint32_t initrd_addr_max; uint32_t kernel_alignment; uint8_t relocatable_kernel; uint8_t _pad2[3]; uint32_t cmdline_size; uint32_t hardware_subarch; uint64_t hardware_subarch_data; uint32_t payload_offset; uint32_t payload_length; } __attribute__((packed)); extern struct xc_dom_loader elf_loader; static int check_magic(struct xc_dom_image *dom, const void *magic, size_t len) { if (len > dom->kernel_size) return 0; return (memcmp(dom->kernel_blob, magic, len) == 0); } static int xc_dom_probe_bzimage_kernel(struct xc_dom_image *dom) { struct setup_header *hdr; uint64_t payload_offset, payload_length; int ret; if ( dom->kernel_blob == NULL ) { xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, "%s: no kernel image loaded", __FUNCTION__); return -EINVAL; } if ( dom->kernel_size < sizeof(struct setup_header) ) { xc_dom_printf(dom->xch, "%s: kernel image too small", __FUNCTION__); return -EINVAL; } hdr = dom->kernel_blob; if ( memcmp(&hdr->header, HDR_MAGIC, HDR_MAGIC_SZ) != 0 ) { xc_dom_printf(dom->xch, "%s: kernel is not a bzImage", __FUNCTION__); return -EINVAL; } if ( hdr->version < VERSION(2,8) ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: boot protocol" " too old (%04x)", __FUNCTION__, hdr->version); return -EINVAL; } /* upcast to 64 bits to avoid overflow */ /* setup_sects is u8 and so cannot overflow */ payload_offset = (hdr->setup_sects + 1) * 512; payload_offset += hdr->payload_offset; payload_length = hdr->payload_length; if ( payload_offset >= dom->kernel_size ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: payload offset overflow", __FUNCTION__); return -EINVAL; } if ( (payload_offset + payload_length) > dom->kernel_size ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: payload length overflow", __FUNCTION__); return -EINVAL; } dom->kernel_blob = dom->kernel_blob + payload_offset; dom->kernel_size = payload_length; if ( check_magic(dom, "\037\213", 2) ) { ret = xc_dom_try_gunzip(dom, &dom->kernel_blob, &dom->kernel_size); if ( ret == -1 ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: unable to" " gzip decompress kernel", __FUNCTION__); return -EINVAL; } } else if ( check_magic(dom, "\102\132\150", 3) ) { ret = xc_try_bzip2_decode(dom, &dom->kernel_blob, &dom->kernel_size); if ( ret < 0 ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s unable to BZIP2 decompress kernel", __FUNCTION__); return -EINVAL; } } else if ( check_magic(dom, "\3757zXZ", 6) ) { ret = xc_try_xz_decode(dom, &dom->kernel_blob, &dom->kernel_size); if ( ret < 0 ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s unable to XZ decompress kernel", __FUNCTION__); return -EINVAL; } } else if ( check_magic(dom, "\135\000", 2) ) { ret = xc_try_lzma_decode(dom, &dom->kernel_blob, &dom->kernel_size); if ( ret < 0 ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s unable to LZMA decompress kernel", __FUNCTION__); return -EINVAL; } } else if ( check_magic(dom, "\x89LZO", 5) ) { ret = xc_try_lzo1x_decode(dom, &dom->kernel_blob, &dom->kernel_size); if ( ret < 0 ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s unable to LZO decompress kernel\n", __FUNCTION__); return -EINVAL; } } else if ( check_magic(dom, "\x02\x21", 2) ) { ret = xc_try_lz4_decode(dom, &dom->kernel_blob, &dom->kernel_size); if ( ret < 0 ) { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s unable to LZ4 decompress kernel\n", __FUNCTION__); return -EINVAL; } } else { xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: unknown compression format", __FUNCTION__); return -EINVAL; } return elf_loader.probe(dom); } static int xc_dom_parse_bzimage_kernel(struct xc_dom_image *dom) { return elf_loader.parser(dom); } static int xc_dom_load_bzimage_kernel(struct xc_dom_image *dom) { return elf_loader.loader(dom); } static struct xc_dom_loader bzimage_loader = { .name = "Linux bzImage", .probe = xc_dom_probe_bzimage_kernel, .parser = xc_dom_parse_bzimage_kernel, .loader = xc_dom_load_bzimage_kernel, }; static void __init register_loader(void) { xc_dom_register_loader(&bzimage_loader); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/misc/0000775000175000017500000000000013256712137012550 5ustar smbsmbxen-4.9.2/tools/misc/mkrpm0000664000175000017500000000311113256712137013615 0ustar smbsmb#!/bin/bash # # mkrpm: package the dist/install output of a Xen build in an .rpm # # Takes 2 arguments, the path to the dist directory and the version set -e if [[ -z "$1" || -z "$2" ]] ; then echo "usage: $0 path-to-XEN_ROOT xen-version" exit 1 fi xenroot="$1" # rpmbuild doesn't like dashes in the version; break it down into # version and release. Default to "0" if there isn't a release. v=(${2/-/ }) version=${v[0]} release="${v[1]:-0}${PKG_RELEASE:+.$PKG_RELEASE}" cd $xenroot # Prepare the directory to package cd dist rm -rf rpm # Fill in the rpm boilerplate mkdir -p rpm/{BUILD,RPMS,SOURCES,SPECS,SRPMS} cat >rpm/SPECS/xen.spec < #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, char **argv) { int domid; xc_interface *xch; xc_dominfo_t dominfo; int ret; uint32_t len; uint8_t *buf; uint32_t off; struct hvm_save_descriptor *descriptor; if (argc != 2 || !argv[1] || (domid = atoi(argv[1])) < 0) { fprintf(stderr, "usage: %s \n", argv[0]); exit(1); } xch = xc_interface_open(0, 0, 0); if (!xch) { fprintf(stderr, "error: can't open libxc handle\n"); exit(1); } ret = xc_domain_getinfo(xch, domid, 1, &dominfo); if (ret < 0) { perror("xc_domain_getinfo"); exit(1); } if (!dominfo.hvm) { fprintf(stderr, "domain %d is not HVM\n", domid); exit(1); } ret = xc_domain_pause(xch, domid); if (ret < 0) { perror("xc_domain_pause"); exit(-1); } /* * Calling with zero buffer length should return the buffer length * required. */ ret = xc_domain_hvm_getcontext(xch, domid, 0, 0); if (ret < 0) { perror("xc_domain_hvm_getcontext"); exit(1); } len = ret; buf = malloc(len); if (buf == NULL) { perror("malloc"); exit(1); } ret = xc_domain_hvm_getcontext(xch, domid, buf, len); if (ret < 0) { perror("xc_domain_hvm_getcontext"); exit(1); } off = 0; while (off < len) { descriptor = (struct hvm_save_descriptor *)(buf + off); off += sizeof (struct hvm_save_descriptor); if (descriptor->typecode == HVM_SAVE_CODE(CPU)) { HVM_SAVE_TYPE(CPU) *cpu; /* Overwrite EIP/RIP with some recognisable but bogus value */ cpu = (HVM_SAVE_TYPE(CPU) *)(buf + off); printf("CPU[%d]: RIP = %" PRIx64 "\n", descriptor->instance, cpu->rip); cpu->rip = 0xf001; } else if (descriptor->typecode == HVM_SAVE_CODE(END)) { break; } off += descriptor->length; } ret = xc_domain_hvm_setcontext(xch, domid, buf, len); if (ret < 0) { perror("xc_domain_hvm_setcontext"); exit(1); } ret = xc_domain_unpause(xch, domid); if (ret < 0) { perror("xc_domain_unpause"); exit(1); } return 0; } xen-4.9.2/tools/misc/xen-livepatch.c0000664000175000017500000003201013256712137015457 0ustar smbsmb/* * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include static xc_interface *xch; void show_help(void) { fprintf(stderr, "xen-livepatch: live patching tool\n" "Usage: xen-livepatch [args]\n" " An unique name of payload. Up to %d characters.\n" "Commands:\n" " help display this help\n" " upload upload file with name\n" " list list payloads uploaded.\n" " apply apply patch.\n" " revert revert name patch.\n" " replace apply patch and revert all others.\n" " unload unload name patch.\n" " load upload and apply .\n" " name is the name\n", XEN_LIVEPATCH_NAME_SIZE); } /* wrapper function */ static int help_func(int argc, char *argv[]) { show_help(); return 0; } #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) static const char *state2str(unsigned int state) { #define STATE(x) [LIVEPATCH_STATE_##x] = #x static const char *const names[] = { STATE(CHECKED), STATE(APPLIED), }; #undef STATE if (state >= ARRAY_SIZE(names) || !names[state]) return "unknown"; return names[state]; } /* This value was choosen adhoc. It could be 42 too. */ #define MAX_LEN 11 static int list_func(int argc, char *argv[]) { unsigned int idx, done, left, i; xen_livepatch_status_t *info = NULL; char *name = NULL; uint32_t *len = NULL; int rc = ENOMEM; if ( argc ) { show_help(); return -1; } idx = left = 0; info = malloc(sizeof(*info) * MAX_LEN); if ( !info ) return rc; name = malloc(sizeof(*name) * XEN_LIVEPATCH_NAME_SIZE * MAX_LEN); if ( !name ) { free(info); return rc; } len = malloc(sizeof(*len) * MAX_LEN); if ( !len ) { free(name); free(info); return rc; } do { done = 0; /* The memset is done to catch errors. */ memset(info, 'A', sizeof(*info) * MAX_LEN); memset(name, 'B', sizeof(*name) * MAX_LEN * XEN_LIVEPATCH_NAME_SIZE); memset(len, 'C', sizeof(*len) * MAX_LEN); rc = xc_livepatch_list(xch, MAX_LEN, idx, info, name, len, &done, &left); if ( rc ) { rc = errno; fprintf(stderr, "Failed to list %d/%d.\n" "Error %d: %s\n", idx, left, rc, strerror(rc)); break; } if ( !idx ) fprintf(stdout," ID | status\n" "----------------------------------------+------------\n"); for ( i = 0; i < done; i++ ) { unsigned int j; uint32_t sz; char *str; sz = len[i]; str = name + (i * XEN_LIVEPATCH_NAME_SIZE); for ( j = sz; j < XEN_LIVEPATCH_NAME_SIZE; j++ ) str[j] = '\0'; printf("%-40s| %s", str, state2str(info[i].state)); if ( info[i].rc ) printf(" (%d, %s)\n", -info[i].rc, strerror(-info[i].rc)); else puts(""); } idx += done; } while ( left ); free(name); free(info); free(len); return rc; } #undef MAX_LEN static int get_name(int argc, char *argv[], char *name) { ssize_t len = strlen(argv[0]); if ( len > XEN_LIVEPATCH_NAME_SIZE ) { fprintf(stderr, "ID must be no more than %d characters.\n", XEN_LIVEPATCH_NAME_SIZE); errno = EINVAL; return errno; } /* Don't want any funny strings from the stack. */ memset(name, 0, XEN_LIVEPATCH_NAME_SIZE); strncpy(name, argv[0], len); return 0; } static int upload_func(int argc, char *argv[]) { char *filename; char name[XEN_LIVEPATCH_NAME_SIZE]; int fd = 0, rc; struct stat buf; unsigned char *fbuf; ssize_t len; if ( argc != 2 ) { show_help(); return -1; } if ( get_name(argc, argv, name) ) return EINVAL; filename = argv[1]; fd = open(filename, O_RDONLY); if ( fd < 0 ) { int saved_errno = errno; fprintf(stderr, "Could not open %s.\n" "Error %d: %s\n", filename, saved_errno, strerror(saved_errno)); return saved_errno; } if ( stat(filename, &buf) != 0 ) { int saved_errno = errno; fprintf(stderr, "Could not get size of %s.\n" "Error %d: %s\n", filename, saved_errno, strerror(saved_errno)); close(fd); return saved_errno; } len = buf.st_size; fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0); if ( fbuf == MAP_FAILED ) { int saved_errno = errno; fprintf(stderr, "Could not map %s.\n" "Error %d: %s\n", filename, saved_errno, strerror(saved_errno)); close (fd); return saved_errno; } printf("Uploading %s... ", filename); rc = xc_livepatch_upload(xch, name, fbuf, len); if ( rc ) { rc = errno; printf("failed\n"); fprintf(stderr, "Error %d: %s\n", rc, strerror(rc)); } else printf("completed\n"); if ( munmap( fbuf, len) ) { fprintf(stderr, "Could not unmap %s.\n" "Error %d: %s\n", filename, errno, strerror(errno)); } close(fd); return rc; } /* These MUST match to the 'action_options[]' array slots. */ enum { ACTION_APPLY = 0, ACTION_REVERT = 1, ACTION_UNLOAD = 2, ACTION_REPLACE = 3, }; struct { int allow; /* State it must be in to call function. */ int expected; /* The state to be in after the function. */ const char *name; const char *verb; int (*function)(xc_interface *xch, char *name, uint32_t timeout); } action_options[] = { { .allow = LIVEPATCH_STATE_CHECKED, .expected = LIVEPATCH_STATE_APPLIED, .name = "apply", .verb = "Applying", .function = xc_livepatch_apply, }, { .allow = LIVEPATCH_STATE_APPLIED, .expected = LIVEPATCH_STATE_CHECKED, .name = "revert", .verb = "Reverting", .function = xc_livepatch_revert, }, { .allow = LIVEPATCH_STATE_CHECKED, .expected = -XEN_ENOENT, .name = "unload", .verb = "Unloading", .function = xc_livepatch_unload, }, { .allow = LIVEPATCH_STATE_CHECKED, .expected = LIVEPATCH_STATE_APPLIED, .name = "replace", .verb = "Replacing all live patches with", .function = xc_livepatch_replace, }, }; /* The hypervisor timeout for the live patching operation is 30 msec, * but it could take some time for the operation to start, so wait twice * that period. */ #define HYPERVISOR_TIMEOUT_NS 30000000 #define DELAY (2 * HYPERVISOR_TIMEOUT_NS) static void nanosleep_retry(long ns) { struct timespec req, rem; int rc; rem.tv_sec = 0; rem.tv_nsec = ns; do { req = rem; rc = nanosleep(&req, &rem); } while ( rc == -1 && errno == EINTR ); } int action_func(int argc, char *argv[], unsigned int idx) { char name[XEN_LIVEPATCH_NAME_SIZE]; int rc; xen_livepatch_status_t status; if ( argc != 1 ) { show_help(); return -1; } if ( idx >= ARRAY_SIZE(action_options) ) return -1; if ( get_name(argc, argv, name) ) return EINVAL; /* Check initial status. */ rc = xc_livepatch_get(xch, name, &status); if ( rc ) { int saved_errno = errno; fprintf(stderr, "Failed to get status of %s.\n" "Error %d: %s\n", name, saved_errno, strerror(saved_errno)); return saved_errno; } if ( status.rc == -XEN_EAGAIN ) { fprintf(stderr, "Cannot execute %s.\n" "Operation already in progress.\n", action_options[idx].name); return EAGAIN; } if ( status.state == action_options[idx].expected ) { printf("No action needed.\n"); return 0; } /* Perform action. */ if ( action_options[idx].allow & status.state ) { printf("%s %s... ", action_options[idx].verb, name); rc = action_options[idx].function(xch, name, HYPERVISOR_TIMEOUT_NS); if ( rc ) { int saved_errno = errno; printf("failed\n"); fprintf(stderr, "Error %d: %s\n", saved_errno, strerror(saved_errno)); return saved_errno; } } else { fprintf(stderr, "%s is in the wrong state.\n" "Current state: %s\n" "Expected state: %s\n", name, state2str(status.state), state2str(action_options[idx].allow)); return -1; } nanosleep_retry(DELAY); rc = xc_livepatch_get(xch, name, &status); if ( rc ) rc = -errno; else if ( status.rc ) rc = status.rc; if ( rc == -XEN_EAGAIN ) { printf("failed\n"); fprintf(stderr, "Operation didn't complete.\n"); return EAGAIN; } if ( rc == 0 ) rc = status.state; if ( action_options[idx].expected == rc ) printf("completed\n"); else if ( rc < 0 ) { printf("failed\n"); fprintf(stderr, "Error %d: %s\n", -rc, strerror(-rc)); return -rc; } else { printf("failed\n"); fprintf(stderr, "%s is in the wrong state.\n" "Current state: %s\n" "Expected state: %s\n", name, state2str(rc), state2str(action_options[idx].expected)); return -1; } return 0; } static int load_func(int argc, char *argv[]) { int rc; char *new_argv[2]; char *path, *name, *lastdot; if ( argc != 1 ) { show_help(); return -1; } /* */ new_argv[1] = argv[0]; /* Synthesize the */ path = strdup(argv[0]); name = basename(path); lastdot = strrchr(name, '.'); if ( lastdot != NULL ) *lastdot = '\0'; new_argv[0] = name; rc = upload_func(2 /* */, new_argv); if ( rc ) return rc; rc = action_func(1 /* only */, new_argv, ACTION_APPLY); if ( rc ) action_func(1, new_argv, ACTION_UNLOAD); free(path); return rc; } /* * These are also functions in action_options that are called in case * none of the ones in main_options match. */ struct { const char *name; int (*function)(int argc, char *argv[]); } main_options[] = { { "help", help_func }, { "list", list_func }, { "upload", upload_func }, { "load", load_func }, }; int main(int argc, char *argv[]) { int i, j = 0, ret; /* * Set stdout to be unbuffered to avoid having to fflush when * printing without a newline. */ setvbuf(stdout, NULL, _IONBF, 0); if ( argc <= 1 ) { show_help(); return 0; } for ( i = 0; i < ARRAY_SIZE(main_options); i++ ) if (!strncmp(main_options[i].name, argv[1], strlen(argv[1]))) break; if ( i == ARRAY_SIZE(main_options) ) { for ( j = 0; j < ARRAY_SIZE(action_options); j++ ) if (!strncmp(action_options[j].name, argv[1], strlen(argv[1]))) break; if ( j == ARRAY_SIZE(action_options) ) { fprintf(stderr, "Unrecognised command '%s' -- try " "'xen-livepatch help'\n", argv[1]); return 1; } } xch = xc_interface_open(0,0,0); if ( !xch ) { fprintf(stderr, "failed to get the handler\n"); return 0; } if ( i == ARRAY_SIZE(main_options) ) ret = action_func(argc -2, argv + 2, j); else ret = main_options[i].function(argc -2, argv + 2); xc_interface_close(xch); /* * Exitcode 0 for success. * Exitcode 1 for an error. * Exitcode 2 if the operation should be retried for any reason (e.g. a * timeout or because another operation was in progress). */ #define EXIT_TIMEOUT (EXIT_FAILURE + 1) BUILD_BUG_ON(EXIT_SUCCESS != 0); BUILD_BUG_ON(EXIT_FAILURE != 1); BUILD_BUG_ON(EXIT_TIMEOUT != 2); switch ( ret ) { case 0: return EXIT_SUCCESS; case EAGAIN: case EBUSY: return EXIT_TIMEOUT; default: return EXIT_FAILURE; } } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/misc/xencov_split0000775000175000017500000000501713256712137015216 0ustar smbsmb#!/usr/bin/python import sys, os, os.path as path, struct, errno from optparse import OptionParser def xencov_split(opts): """Split input into multiple gcda files""" # Check native byte order and explicitly specify it. The "native" # byte order in struct module takes into account padding while the # data is always packed. if sys.byteorder == 'little': bo_prefix = '<' else: bo_prefix = '>' input_file = opts.args[0] f = open(input_file) # Magic number s = f.read(4) magic, = struct.unpack(bo_prefix + "I", s) # See public/sysctl.h for magic number -- "XCOV" if magic != 0x58434f56: raise Exception("Invalid magic number") # The rest is zero or more records content = f.read() f.close() while content: off = content.find('\x00') fmt = bo_prefix + str(off) + 's' fn, = struct.unpack_from(fmt, content) content = content[off+1:] fmt = bo_prefix + 'I' sz, = struct.unpack_from(fmt, content) content = content[struct.calcsize(fmt):] fmt = bo_prefix + str(sz) + 's' payload, = struct.unpack_from(fmt, content) content = content[sz:] # Create and store files if opts.output_dir == '.': opts.output_dir = os.getcwd() dir = opts.output_dir + path.dirname(fn) try: os.makedirs(dir) except OSError, e: if e.errno == errno.EEXIST and os.path.isdir(dir): pass else: raise full_path = dir + '/' + path.basename(fn) f = open(full_path, "w") f.write(payload) f.close() def main(): """ Main entrypoint """ # Change stdout to be line-buffered. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) parser = OptionParser( usage = "%prog [OPTIONS] ", description = "Utility to split xencov data file", ) parser.add_option("--output-dir", action = "store", dest = "output_dir", default = ".", type = "string", help = ('Specify the directory to place output files, ' 'defaults to current directory'), ) opts, args = parser.parse_args() opts.args = args xencov_split(opts) if __name__ == "__main__": try: sys.exit(main()) except Exception, e: print >>sys.stderr, "Error:", e sys.exit(1) except KeyboardInterrupt: sys.exit(1) xen-4.9.2/tools/misc/xenpvnetboot0000775000175000017500000002307313256712137015236 0ustar smbsmb#!/usr/bin/env python # # Copyright (C) 2010 Oracle. All rights reserved. # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation, version 2. This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. You should have received a copy of the GNU # General Public License along with this program; If not, see . import sys import os import stat import time import string import random import tempfile import commands import subprocess import urlgrabber from optparse import OptionParser XEN_PATHS = [ ('images/xen/vmlinuz', 'images/xen/initrd.img'), # Fedora <= 10 and OL = 5 ('boot/i386/vmlinuz-xen', 'boot/i386/initrd-xen'), # openSUSE >= 10.2 and SLES >= 10 ('boot/x86_64/vmlinuz-xen', 'boot/x86_64/initrd-xen'), # openSUSE >= 10.2 and SLES >= 10 ('current/images/netboot/xen/vmlinuz', 'current/images/netboot/xen/initrd.gz'), # Debian ('images/pxeboot/vmlinuz', 'images/pxeboot/initrd.img'), # Fedora >=10 and OL >= 6 ('isolinux/vmlinuz', 'isolinux/initrd.img'), # Fedora >= 10 and OL >= 6 ] def format_sxp(kernel, ramdisk, args): s = 'linux (kernel %s)' % kernel if ramdisk: s += '(ramdisk %s)' % ramdisk if args: s += '(args "%s")' % args return s def format_simple(kernel, ramdisk, args, sep): s = ('kernel %s' % kernel) + sep if ramdisk: s += ('ramdisk %s' % ramdisk) + sep if args: s += ('args %s' % args) + sep s += sep return s def mount(dev, path, option=''): if os.uname()[0] == 'SunOS': mountcmd = '/usr/sbin/mount' else: mountcmd = '/bin/mount' cmd = ' '.join([mountcmd, option, dev, path]) (status, output) = commands.getstatusoutput(cmd) if status != 0: raise RuntimeError('Command: (%s) failed: (%s) %s' % (cmd, status, output)) def umount(path): if os.uname()[0] == 'SunOS': cmd = ['/usr/sbin/umount', path] else: cmd = ['/bin/umount', path] subprocess.call(cmd) class Fetcher: def __init__(self, location, tmpdir): self.location = location self.tmpdir = tmpdir self.srcdir = location def prepare(self): if not os.path.exists(self.tmpdir): os.makedirs(self.tmpdir, 0750) def cleanup(self): pass def get_file(self, filename): url = os.path.join(self.srcdir, filename) suffix = ''.join(random.sample(string.ascii_letters, 6)) local_name = os.path.join(self.tmpdir, 'xenpvboot.%s.%s' % (os.path.basename(filename), suffix)) try: return urlgrabber.urlgrab(url, local_name, copy_local=1) except Exception, err: raise RuntimeError('Cannot get file %s: %s' % (url, err)) class MountedFetcher(Fetcher): def prepare(self): Fetcher.prepare(self) self.srcdir = tempfile.mkdtemp(prefix='xenpvboot.', dir=self.tmpdir) if self.location.startswith('nfs:'): mount(self.location[4:], self.srcdir, '-o ro') else: if stat.S_ISBLK(os.stat(self.location)[stat.ST_MODE]): option = '-o ro' else: option = '-o ro,loop' if os.uname()[0] == 'SunOS': option += ' -F hsfs' mount(self.location, self.srcdir, option) def cleanup(self): umount(self.srcdir) try: os.rmdir(self.srcdir) except: pass class NFSISOFetcher(MountedFetcher): def __init__(self, location, tmpdir): self.nfsdir = None MountedFetcher.__init__(self, location, tmpdir) def prepare(self): Fetcher.prepare(self) self.nfsdir = tempfile.mkdtemp(prefix='xenpvboot.', dir=self.tmpdir) self.srcdir = tempfile.mkdtemp(prefix='xenpvboot.', dir=self.tmpdir) nfs = os.path.dirname(self.location[8:]) iso = os.path.basename(self.location[8:]) mount(nfs, self.nfsdir, '-o ro') option = '-o ro,loop' if os.uname()[0] == 'SunOS': option += ' -F hsfs' mount(os.path.join(self.nfsdir, iso), self.srcdir, option) def cleanup(self): MountedFetcher.cleanup(self) time.sleep(1) umount(self.nfsdir) try: os.rmdir(self.nfsdir) except: pass class TFTPFetcher(Fetcher): def get_file(self, filename): if '/' in self.location[7:]: host = self.location[7:].split('/', 1)[0].replace(':', ' ') basedir = self.location[7:].split('/', 1)[1] else: host = self.location[7:].replace(':', ' ') basedir = '' suffix = ''.join(random.sample(string.ascii_letters, 6)) local_name = os.path.join(self.tmpdir, 'xenpvboot.%s.%s' % (os.path.basename(filename), suffix)) cmd = '/usr/bin/tftp %s -c get %s %s' % (host, os.path.join(basedir, filename), local_name) (status, output) = commands.getstatusoutput(cmd) if status != 0: raise RuntimeError('Command: (%s) failed: (%s) %s' % (cmd, status, output)) return local_name def main(): usage = '''%prog [option] Get boot images from the given location and prepare for Xen to use. Supported locations: - http://host/path - https://host/path - ftp://host/path - file:///path - tftp://host/path - nfs:host:/path - /path - /path/file.iso - /path/filesystem.img - /dev/sda1 - nfs+iso:host:/path/file.iso - nfs+iso:host:/path/filesystem.img''' version = '%prog version 0.1' parser = OptionParser(usage=usage, version=version) parser.add_option('', '--location', help='The base url for kernel and ramdisk files.') parser.add_option('', '--kernel', help='The kernel image file.') parser.add_option('', '--ramdisk', help='The initial ramdisk file.') parser.add_option('', '--args', help='Arguments pass to the kernel.') parser.add_option('', '--output', help='Redirect output to this file instead of stdout.') parser.add_option('', '--output-directory', default='/var/run/libxl', help='Output directory.') parser.add_option('', '--output-format', default='sxp', help='Output format: sxp, simple or simple0.') parser.add_option('-q', '--quiet', action='store_true', help='Be quiet.') (opts, args) = parser.parse_args() if not opts.location and not opts.kernel and not opts.ramdisk: if not opts.quiet: print >> sys.stderr, 'You should at least specify a location or kernel/ramdisk.' parser.print_help(sys.stderr) sys.exit(1) if not opts.output or opts.output == '-': fd = sys.stdout.fileno() else: fd = os.open(opts.output, os.O_WRONLY) if opts.location: location = opts.location else: location = '' if (location == '' or location.startswith('http://') or location.startswith('https://') or location.startswith('ftp://') or location.startswith('file://') or (os.path.exists(location) and os.path.isdir(location))): fetcher = Fetcher(location, opts.output_directory) elif location.startswith('nfs:') or (os.path.exists(location) and not os.path.isdir(location)): fetcher = MountedFetcher(location, opts.output_directory) elif location.startswith('nfs+iso:'): fetcher = NFSISOFetcher(location, opts.output_directory) elif location.startswith('tftp://'): fetcher = TFTPFetcher(location, opts.output_directory) else: if not opts.quiet: print >> sys.stderr, 'Unsupported location: %s' % location sys.exit(1) try: fetcher.prepare() except Exception, err: if not opts.quiet: print >> sys.stderr, str(err) fetcher.cleanup() sys.exit(1) try: kernel = None if opts.kernel: kernel = fetcher.get_file(opts.kernel) else: for (kernel_path, _) in XEN_PATHS: try: kernel = fetcher.get_file(kernel_path) except Exception, err: if not opts.quiet: print >> sys.stderr, str(err) continue break if not kernel: if not opts.quiet: print >> sys.stderr, 'Cannot get kernel from loacation: %s' % location sys.exit(1) ramdisk = None if opts.ramdisk: ramdisk = fetcher.get_file(opts.ramdisk) else: for (_, ramdisk_path) in XEN_PATHS: try: ramdisk = fetcher.get_file(ramdisk_path) except Exception, err: if not opts.quiet: print >> sys.stderr, str(err) continue break finally: fetcher.cleanup() if opts.output_format == 'sxp': output = format_sxp(kernel, ramdisk, opts.args) elif opts.output_format == 'simple': output = format_simple(kernel, ramdisk, opts.args, '\n') elif opts.output_format == 'simple0': output = format_simple(kernel, ramdisk, opts.args, '\0') else: print >> sys.stderr, 'Unknown output format: %s' % opts.output_format sys.exit(1) sys.stdout.flush() os.write(fd, output) if __name__ == '__main__': main() xen-4.9.2/tools/misc/xenwatchdogd.c0000664000175000017500000000353213256712137015376 0ustar smbsmb #include #include #include "xenctrl.h" #include #include #include #include #include #include #include xc_interface *h; int id = 0; void daemonize(void) { switch (fork()) { case -1: err(1, "fork"); case 0: break; default: exit(0); } umask(0); if (setsid() < 0) err(1, "setsid"); if (chdir("/") < 0) err(1, "chdir /"); if (freopen("/dev/null", "r", stdin) == NULL) err(1, "reopen stdin"); if(freopen("/dev/null", "w", stdout) == NULL) err(1, "reopen stdout"); if(freopen("/dev/null", "w", stderr) == NULL) err(1, "reopen stderr"); } void catch_exit(int sig) { if (id) xc_watchdog(h, id, 300); exit(0); } void catch_usr1(int sig) { if (id) xc_watchdog(h, id, 0); exit(0); } int main(int argc, char **argv) { int t, s; int ret; if (argc < 2) errx(1, "usage: %s ", argv[0]); daemonize(); h = xc_interface_open(NULL, NULL, 0); if (h == NULL) err(1, "xc_interface_open"); t = strtoul(argv[1], NULL, 0); if (t == ULONG_MAX) err(1, "strtoul"); s = t / 2; if (argc == 3) { s = strtoul(argv[2], NULL, 0); if (s == ULONG_MAX) err(1, "strtoul"); } if (signal(SIGHUP, &catch_exit) == SIG_ERR) err(1, "signal"); if (signal(SIGINT, &catch_exit) == SIG_ERR) err(1, "signal"); if (signal(SIGQUIT, &catch_exit) == SIG_ERR) err(1, "signal"); if (signal(SIGTERM, &catch_exit) == SIG_ERR) err(1, "signal"); if (signal(SIGUSR1, &catch_usr1) == SIG_ERR) err(1, "signal"); id = xc_watchdog(h, 0, t); if (id <= 0) err(1, "xc_watchdog setup"); for (;;) { sleep(s); ret = xc_watchdog(h, id, t); if (ret != 0) err(1, "xc_watchdog"); } } xen-4.9.2/tools/misc/xencons0000775000175000017500000000472513256712137014163 0ustar smbsmb#!/usr/bin/env python ############################################## # Console client for Xen guest OSes # Copyright (c) 2004, K A Fraser ############################################## import errno, os, signal, socket, struct, sys from termios import * # Indexes into termios.tcgetattr() list. IFLAG = 0 OFLAG = 1 CFLAG = 2 LFLAG = 3 ISPEED = 4 OSPEED = 5 CC = 6 def __child_death(signum, frame): global stop stop = True def __recv_from_sock(sock): global stop stop = False while not stop: try: data = sock.recv(1024) except socket.error, error: if error[0] != errno.EINTR: raise else: try: os.write(1, data) except os.error, error: if error[0] != errno.EINTR: raise os.wait() def __send_to_sock(sock): while 1: try: data = os.read(0,1024) except os.error, error: if error[0] != errno.EINTR: raise else: if ord(data[0]) == ord(']')-64: break try: sock.send(data) except socket.error, error: if error[0] == errno.EPIPE: sys.exit(0) if error[0] != errno.EINTR: raise sys.exit(0) def connect(host,port): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) sock.connect((host,port)) oattrs = tcgetattr(0) nattrs = tcgetattr(0) nattrs[IFLAG] = nattrs[IFLAG] & ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON) nattrs[OFLAG] = nattrs[OFLAG] & ~(OPOST) nattrs[CFLAG] = nattrs[CFLAG] & ~(CSIZE | PARENB) nattrs[CFLAG] = nattrs[CFLAG] | CS8 nattrs[LFLAG] = nattrs[LFLAG] & ~(ECHO | ICANON | IEXTEN | ISIG) nattrs[CC][VMIN] = 1 nattrs[CC][VTIME] = 0 if os.fork(): signal.signal(signal.SIGCHLD, __child_death) print "************ REMOTE CONSOLE: CTRL-] TO QUIT ********" tcsetattr(0, TCSAFLUSH, nattrs) try: __recv_from_sock(sock) finally: tcsetattr(0, TCSAFLUSH, oattrs) print print "************ REMOTE CONSOLE EXITED *****************" else: signal.signal(signal.SIGPIPE, signal.SIG_IGN) __send_to_sock(sock) if __name__ == '__main__': if len(sys.argv) != 3: print sys.argv[0] + " " sys.exit(1) connect(str(sys.argv[1]),int(sys.argv[2])) xen-4.9.2/tools/misc/mktarball0000775000175000017500000000165113256712137014452 0ustar smbsmb#!/bin/bash # # mktarball: Make a release tarball (including xen, qemu, and qemu-traditional) # # Takes 2 arguments, the path to the dist directory and the version set -ex function git_archive_into { mkdir -p "$2" git --git-dir="$1"/.git \ archive --format=tar HEAD | \ tar Cxf "$2" - } if [[ -z "$1" || -z "$2" ]] ; then echo "usage: $0 path-to-XEN_ROOT xen-version" exit 1 fi xen_root="$1" desc="$2" tdir="$xen_root/dist/tmp.src-tarball" rm -rf $tdir mkdir -p $tdir git_archive_into $xen_root $tdir/xen-$desc git_archive_into $xen_root/tools/qemu-xen-dir-remote $tdir/xen-$desc/tools/qemu-xen git_archive_into $xen_root/tools/qemu-xen-traditional-dir-remote $tdir/xen-$desc/tools/qemu-xen-traditional git_archive_into $xen_root/extras/mini-os-remote $tdir/xen-$desc/extras/mini-os GZIP=-9v tar cz -f $xen_root/dist/xen-$desc.tar.gz -C $tdir xen-$desc echo "Source tarball in $xen_root/dist/xen-$desc.tar.gz" xen-4.9.2/tools/misc/xen-hptool.c0000664000175000017500000002365313256712137015022 0ustar smbsmb#include #include #include #include #include #include #undef ARRAY_SIZE /* We shouldn't be including xc_private.h */ #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) static xc_interface *xch; void show_help(void) { fprintf(stderr, "xen-hptool: Xen CPU/memory hotplug tool\n" "Usage: xen-hptool [args]\n" "Commands:\n" " help display this help\n" " cpu-online online CPU \n" " cpu-offline offline CPU \n" " mem-online online MEMORY \n" " mem-offline offline MEMORY \n" " mem-status query Memory status\n" ); } /* wrapper function */ static int help_func(int argc, char *argv[]) { show_help(); return 0; } static int hp_mem_online_func(int argc, char *argv[]) { uint32_t status; int ret; unsigned long mfn; if (argc != 1) { show_help(); return -1; } sscanf(argv[0], "%lx", &mfn); printf("Prepare to online MEMORY mfn %lx\n", mfn); ret = xc_mark_page_online(xch, mfn, mfn, &status); if (ret < 0) fprintf(stderr, "Onlining page mfn %lx failed, error %x", mfn, errno); else if (status & (PG_ONLINE_FAILED |PG_ONLINE_BROKEN)) { fprintf(stderr, "Onlining page mfn %lx is broken, " "Memory online failed\n", mfn); ret = -1; } else if (status & PG_ONLINE_ONLINED) printf("Memory mfn %lx onlined successfully\n", mfn); else printf("Memory is already onlined!\n"); return ret; } static int hp_mem_query_func(int argc, char *argv[]) { uint32_t status; int ret; unsigned long mfn; if (argc != 1) { show_help(); return -1; } sscanf(argv[0], "%lx", &mfn); printf("Querying MEMORY mfn %lx status\n", mfn); ret = xc_query_page_offline_status(xch, mfn, mfn, &status); if (ret < 0) fprintf(stderr, "Querying page mfn %lx failed, error %x", mfn, errno); else { printf("Memory Status %x: [", status); if ( status & PG_OFFLINE_STATUS_OFFLINE_PENDING) printf(" PAGE_OFFLINE_PENDING "); if ( status & PG_OFFLINE_STATUS_BROKEN ) printf(" PAGE_BROKEND "); if ( status & PG_OFFLINE_STATUS_OFFLINED ) printf(" PAGE_OFFLINED "); else printf(" PAGE_ONLINED "); printf("]\n"); } return ret; } static int suspend_guest(xc_interface *xch, xenevtchn_handle *xce, int domid, int *evtchn, int *lockfd) { int port, rc, suspend_evtchn = -1; *lockfd = -1; if (!evtchn) return -1; port = xs_suspend_evtchn_port(domid); if (port < 0) { fprintf(stderr, "DOM%d: No suspend port, try live migration\n", domid); goto failed; } suspend_evtchn = xc_suspend_evtchn_init_exclusive(xch, xce, domid, port, lockfd); if (suspend_evtchn < 0) { fprintf(stderr, "Suspend evtchn initialization failed\n"); goto failed; } *evtchn = suspend_evtchn; rc = xenevtchn_notify(xce, suspend_evtchn); if (rc < 0) { fprintf(stderr, "Failed to notify suspend channel: errno %d\n", rc); goto failed; } if (xc_await_suspend(xch, xce, suspend_evtchn) < 0) { fprintf(stderr, "Suspend Failed\n"); goto failed; } return 0; failed: if (suspend_evtchn != -1) xc_suspend_evtchn_release(xch, xce, domid, suspend_evtchn, lockfd); return -1; } static int hp_mem_offline_func(int argc, char *argv[]) { uint32_t status, domid; int ret; unsigned long mfn; if (argc != 1) { show_help(); return -1; } sscanf(argv[0], "%lx", &mfn); printf("Prepare to offline MEMORY mfn %lx\n", mfn); ret = xc_mark_page_offline(xch, mfn, mfn, &status); if (ret < 0) { fprintf(stderr, "Offlining page mfn %lx failed, error %x\n", mfn, errno); if (status & (PG_OFFLINE_XENPAGE | PG_OFFLINE_FAILED)) fprintf(stderr, "XEN_PAGE is not permitted be offlined\n"); else if (status & (PG_OFFLINE_FAILED | PG_OFFLINE_NOT_CONV_RAM)) fprintf(stderr, "RESERVED RAM is not permitted to be offlined\n"); } else { switch(status & PG_OFFLINE_STATUS_MASK) { case PG_OFFLINE_OFFLINED: { printf("Memory mfn %lx offlined successfully, current state is" " [PG_OFFLINE_OFFLINED]\n", mfn); if (status & PG_OFFLINE_BROKEN) printf("And this offlined PAGE is already marked broken" " before!\n"); break; } case PG_OFFLINE_FAILED: { fprintf(stderr, "Memory mfn %lx offline failed\n", mfn); if ( status & PG_OFFLINE_ANONYMOUS) fprintf(stderr, "the memory is an anonymous page!\n"); ret = -1; break; } case PG_OFFLINE_PENDING: { if (status & PG_OFFLINE_XENPAGE) { ret = -1; fprintf(stderr, "Memory mfn %lx offlined succssefully," "this page is xen page, current state is" " [PG_OFFLINE_PENDING, PG_OFFLINE_XENPAGE]\n", mfn); } else if (status & PG_OFFLINE_OWNED) { int result, suspend_evtchn = -1, suspend_lockfd = -1; xenevtchn_handle *xce; xce = xenevtchn_open(NULL, 0); if (xce == NULL) { fprintf(stderr, "When exchange page, fail" " to open evtchn\n"); return -1; } domid = status >> PG_OFFLINE_OWNER_SHIFT; if (suspend_guest(xch, xce, domid, &suspend_evtchn, &suspend_lockfd)) { fprintf(stderr, "Failed to suspend guest %d for" " mfn %lx\n", domid, mfn); xenevtchn_close(xce); return -1; } result = xc_exchange_page(xch, domid, mfn); /* Exchange page successfully */ if (result == 0) printf("Memory mfn %lx offlined successfully, this " "page is DOM%d page and being swapped " "successfully, current state is " "[PG_OFFLINE_OFFLINED, PG_OFFLINE_OWNED]\n", mfn, domid); else { ret = -1; fprintf(stderr, "Memory mfn %lx offlined successfully" " , this page is DOM%d page yet failed to be " "exchanged. current state is " "[PG_OFFLINE_PENDING, PG_OFFLINE_OWNED]\n", mfn, domid); } xc_domain_resume(xch, domid, 1); xc_suspend_evtchn_release(xch, xce, domid, suspend_evtchn, &suspend_lockfd); xenevtchn_close(xce); } break; } }//end of switch }//end of if return ret; } static int exec_cpu_hp_fn(int (*hp_fn)(xc_interface *, int), int cpu) { int ret; for ( ; ; ) { ret = (*hp_fn)(xch, cpu); if ( (ret >= 0) || (errno != EBUSY) ) break; usleep(100000); /* 100ms */ } return ret; } static int hp_cpu_online_func(int argc, char *argv[]) { int cpu, ret; if ( argc != 1 ) { show_help(); return -1; } cpu = atoi(argv[0]); printf("Prepare to online CPU %d\n", cpu); ret = exec_cpu_hp_fn(xc_cpu_online, cpu); if (ret < 0) fprintf(stderr, "CPU %d online failed (error %d: %s)\n", cpu, errno, strerror(errno)); else printf("CPU %d onlined successfully\n", cpu); return ret; } static int hp_cpu_offline_func(int argc, char *argv[]) { int cpu, ret; if (argc != 1 ) { show_help(); return -1; } cpu = atoi(argv[0]); printf("Prepare to offline CPU %d\n", cpu); ret = exec_cpu_hp_fn(xc_cpu_offline, cpu); if (ret < 0) fprintf(stderr, "CPU %d offline failed (error %d: %s)\n", cpu, errno, strerror(errno)); else printf("CPU %d offlined successfully\n", cpu); return ret; } struct { const char *name; int (*function)(int argc, char *argv[]); } main_options[] = { { "help", help_func }, { "cpu-online", hp_cpu_online_func }, { "cpu-offline", hp_cpu_offline_func }, { "mem-status", hp_mem_query_func}, { "mem-online", hp_mem_online_func}, { "mem-offline", hp_mem_offline_func}, }; int main(int argc, char *argv[]) { int i, ret; if (argc < 2) { show_help(); return 0; } xch = xc_interface_open(0,0,0); if ( !xch ) { fprintf(stderr, "failed to get the handler\n"); return 0; } for ( i = 0; i < ARRAY_SIZE(main_options); i++ ) if (!strncmp(main_options[i].name, argv[1], strlen(argv[1]))) break; if ( i == ARRAY_SIZE(main_options) ) { fprintf(stderr, "Unrecognised command '%s' -- try " "'xen-hptool help'\n", argv[1]); return 1; } ret = main_options[i].function(argc -2, argv + 2); xc_interface_close(xch); return !!ret; } xen-4.9.2/tools/misc/xen-mfndump.c0000664000175000017500000002650713256712137015164 0ustar smbsmb#define XC_WANT_COMPAT_MAP_FOREIGN_API #include #include #include #include #include #include "xg_save_restore.h" #undef ARRAY_SIZE /* We shouldn't be including xc_private.h */ #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) static xc_interface *xch; int help_func(int argc, char *argv[]) { fprintf(stderr, "Usage: xen-mfndump [args]\n" "Commands:\n" " help show this help\n" " dump-m2p show M2P\n" " dump-p2m show P2M of \n" " dump-ptes show the PTEs in \n" " lookup-pte find the PTE mapping \n" " memcmp-mfns \n" " compare content of & \n" ); return 0; } int dump_m2p_func(int argc, char *argv[]) { unsigned long i; unsigned long max_mfn; xen_pfn_t *m2p_table; if ( argc > 0 ) { help_func(0, NULL); return 1; } /* Map M2P and obtain gpfn */ if ( xc_maximum_ram_page(xch, &max_mfn) < 0 ) { ERROR("Failed to get the maximum mfn"); return -1; } if ( !(m2p_table = xc_map_m2p(xch, max_mfn, PROT_READ, NULL)) ) { ERROR("Failed to map live M2P table"); return -1; } printf(" --- Dumping M2P ---\n"); printf(" Max MFN: %lu\n", max_mfn); for ( i = 0; i < max_mfn; i++ ) { printf(" mfn=0x%lx ==> pfn=0x%lx\n", i, m2p_table[i]); } printf(" --- End of M2P ---\n"); munmap(m2p_table, M2P_SIZE(max_mfn)); return 0; } int dump_p2m_func(int argc, char *argv[]) { struct xc_domain_meminfo minfo; xc_dominfo_t info; unsigned long i; int domid; if ( argc < 1 ) { help_func(0, NULL); return 1; } domid = atoi(argv[0]); if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 || info.domid != domid ) { ERROR("Failed to obtain info for domain %d\n", domid); return -1; } /* Retrieve all the info about the domain's memory */ memset(&minfo, 0, sizeof(minfo)); if ( xc_map_domain_meminfo(xch, domid, &minfo) ) { ERROR("Could not map domain %d memory information\n", domid); return -1; } printf(" --- Dumping P2M for domain %d ---\n", domid); printf(" Guest Width: %u, PT Levels: %u P2M size: = %lu\n", minfo.guest_width, minfo.pt_levels, minfo.p2m_size); for ( i = 0; i < minfo.p2m_size; i++ ) { unsigned long pagetype = minfo.pfn_type[i] & XEN_DOMCTL_PFINFO_LTAB_MASK; xen_pfn_t mfn; if ( minfo.guest_width == sizeof(uint64_t) ) mfn = ((uint64_t*)minfo.p2m_table)[i]; else { mfn = ((uint32_t*)minfo.p2m_table)[i]; #ifdef __x86_64__ if ( mfn == ~0U ) /* Expand a 32bit guest's idea of INVALID_MFN */ mfn = ~0UL; #endif } printf(" pfn=0x%lx ==> mfn=0x%lx (type 0x%lx)", i, mfn, pagetype >> XEN_DOMCTL_PFINFO_LTAB_SHIFT); switch ( pagetype >> XEN_DOMCTL_PFINFO_LTAB_SHIFT ) { case 0x0: /* NOTAB */ printf("\n"); break; case 0x1 ... 0x4: /* L1 -> L4 */ printf(" L%lu\n", pagetype >> XEN_DOMCTL_PFINFO_LTAB_SHIFT); break; case 0x9 ... 0xc: /* Pinned L1 -> L4 */ printf(" pinned L%lu\n", (pagetype >> XEN_DOMCTL_PFINFO_LTAB_SHIFT) & 7); break; case 0xd: /* BROKEN */ printf(" broken\n"); break; case 0xe: /* XALLOC */ printf(" xalloc\n"); break; case 0xf: /* XTAB */ printf(" invalid\n"); break; default: printf(" \n"); break; } } printf(" --- End of P2M for domain %d ---\n", domid); xc_unmap_domain_meminfo(xch, &minfo); return 0; } int dump_ptes_func(int argc, char *argv[]) { struct xc_domain_meminfo minfo; xc_dominfo_t info; void *page = NULL; unsigned long i, max_mfn; int domid, pte_num, rc = 0; xen_pfn_t pfn, mfn, *m2p_table; if ( argc < 2 ) { help_func(0, NULL); return 1; } domid = atoi(argv[0]); mfn = strtoul(argv[1], NULL, 16); if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 || info.domid != domid ) { ERROR("Failed to obtain info for domain %d\n", domid); return -1; } /* Retrieve all the info about the domain's memory */ memset(&minfo, 0, sizeof(minfo)); if ( xc_map_domain_meminfo(xch, domid, &minfo) ) { ERROR("Could not map domain %d memory information\n", domid); return -1; } /* Map M2P and obtain gpfn */ rc = xc_maximum_ram_page(xch, &max_mfn); if ( rc || (mfn > max_mfn) || !(m2p_table = xc_map_m2p(xch, max_mfn, PROT_READ, NULL)) ) { xc_unmap_domain_meminfo(xch, &minfo); ERROR("Failed to map live M2P table"); return -1; } pfn = m2p_table[mfn]; if ( pfn >= minfo.p2m_size ) { ERROR("pfn 0x%lx out of range for domain %d\n", pfn, domid); rc = -1; goto out; } if ( !(minfo.pfn_type[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) ) { ERROR("pfn 0x%lx for domain %d is not a PT\n", pfn, domid); rc = -1; goto out; } page = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ, minfo.p2m_table[pfn]); if ( !page ) { ERROR("Failed to map 0x%lx\n", minfo.p2m_table[pfn]); rc = -1; goto out; } pte_num = PAGE_SIZE / 8; printf(" --- Dumping %d PTEs for domain %d ---\n", pte_num, domid); printf(" Guest Width: %u, PT Levels: %u P2M size: = %lu\n", minfo.guest_width, minfo.pt_levels, minfo.p2m_size); printf(" pfn: 0x%lx, mfn: 0x%lx", pfn, minfo.p2m_table[pfn]); switch ( minfo.pfn_type[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK ) { case XEN_DOMCTL_PFINFO_L1TAB: printf(", L1 table"); break; case XEN_DOMCTL_PFINFO_L2TAB: printf(", L2 table"); break; case XEN_DOMCTL_PFINFO_L3TAB: printf(", L3 table"); break; case XEN_DOMCTL_PFINFO_L4TAB: printf(", L4 table"); break; } if ( minfo.pfn_type[pfn] & XEN_DOMCTL_PFINFO_LPINTAB ) printf (" [pinned]"); if ( is_mapped(minfo.p2m_table[pfn]) ) printf(" [mapped]"); printf("\n"); for ( i = 0; i < pte_num; i++ ) printf(" pte[%lu]: 0x%"PRIx64"\n", i, ((const uint64_t*)page)[i]); printf(" --- End of PTEs for domain %d, pfn=0x%lx (mfn=0x%lx) ---\n", domid, pfn, minfo.p2m_table[pfn]); out: if ( page ) munmap(page, PAGE_SIZE); xc_unmap_domain_meminfo(xch, &minfo); munmap(m2p_table, M2P_SIZE(max_mfn)); return rc; } int lookup_pte_func(int argc, char *argv[]) { struct xc_domain_meminfo minfo; xc_dominfo_t info; void *page = NULL; unsigned long i, j; int domid, pte_num; xen_pfn_t mfn; if ( argc < 2 ) { help_func(0, NULL); return 1; } domid = atoi(argv[0]); mfn = strtoul(argv[1], NULL, 16); if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 || info.domid != domid ) { ERROR("Failed to obtain info for domain %d\n", domid); return -1; } /* Retrieve all the info about the domain's memory */ memset(&minfo, 0, sizeof(minfo)); if ( xc_map_domain_meminfo(xch, domid, &minfo) ) { ERROR("Could not map domain %d memory information\n", domid); return -1; } pte_num = PAGE_SIZE / 8; printf(" --- Lookig for PTEs mapping mfn 0x%lx for domain %d ---\n", mfn, domid); printf(" Guest Width: %u, PT Levels: %u P2M size: = %lu\n", minfo.guest_width, minfo.pt_levels, minfo.p2m_size); for ( i = 0; i < minfo.p2m_size; i++ ) { if ( !(minfo.pfn_type[i] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) ) continue; page = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ, minfo.p2m_table[i]); if ( !page ) continue; for ( j = 0; j < pte_num; j++ ) { uint64_t pte = ((const uint64_t*)page)[j]; #define __MADDR_BITS_X86 ((minfo.guest_width == 8) ? 52 : 44) #define __MFN_MASK_X86 ((1ULL << (__MADDR_BITS_X86 - PAGE_SHIFT_X86)) - 1) if ( ((pte >> PAGE_SHIFT_X86) & __MFN_MASK_X86) == mfn) printf(" 0x%lx <-- [0x%lx][%lu]: 0x%"PRIx64"\n", mfn, minfo.p2m_table[i], j, pte); #undef __MADDR_BITS_X86 #undef __MFN_MASK_X8 } munmap(page, PAGE_SIZE); page = NULL; } xc_unmap_domain_meminfo(xch, &minfo); return 1; } int memcmp_mfns_func(int argc, char *argv[]) { xc_dominfo_t info1, info2; void *page1 = NULL, *page2 = NULL; int domid1, domid2; xen_pfn_t mfn1, mfn2; int rc = 0; if ( argc < 4 ) { help_func(0, NULL); return 1; } domid1 = atoi(argv[0]); domid2 = atoi(argv[2]); mfn1 = strtoul(argv[1], NULL, 16); mfn2 = strtoul(argv[3], NULL, 16); if ( xc_domain_getinfo(xch, domid1, 1, &info1) != 1 || xc_domain_getinfo(xch, domid2, 1, &info2) != 1 || info1.domid != domid1 || info2.domid != domid2) { ERROR("Failed to obtain info for domains\n"); return -1; } page1 = xc_map_foreign_range(xch, domid1, PAGE_SIZE, PROT_READ, mfn1); page2 = xc_map_foreign_range(xch, domid2, PAGE_SIZE, PROT_READ, mfn2); if ( !page1 || !page2 ) { ERROR("Failed to map either 0x%lx[dom %d] or 0x%lx[dom %d]\n", mfn1, domid1, mfn2, domid2); rc = -1; goto out; } printf(" --- Comparing the content of 2 MFNs ---\n"); printf(" 1: 0x%lx[dom %d], 2: 0x%lx[dom %d]\n", mfn1, domid1, mfn2, domid2); printf(" memcpy(1, 2) = %d\n", memcmp(page1, page2, PAGE_SIZE)); out: if ( page1 ) munmap(page1, PAGE_SIZE); if ( page2 ) munmap(page2, PAGE_SIZE); return rc; } struct { const char *name; int (*func)(int argc, char *argv[]); } opts[] = { { "help", help_func }, { "dump-m2p", dump_m2p_func }, { "dump-p2m", dump_p2m_func }, { "dump-ptes", dump_ptes_func }, { "lookup-pte", lookup_pte_func }, { "memcmp-mfns", memcmp_mfns_func}, }; int main(int argc, char *argv[]) { int i, ret; if (argc < 2) { help_func(0, NULL); return 1; } xch = xc_interface_open(0, 0, 0); if ( !xch ) { fprintf(stderr, "Failed to open an xc handler"); return 1; } for ( i = 0; i < ARRAY_SIZE(opts); i++ ) { if ( !strncmp(opts[i].name, argv[1], strlen(argv[1])) ) break; } if ( i == ARRAY_SIZE(opts) ) { fprintf(stderr, "Unknown option '%s'", argv[1]); help_func(0, NULL); return 1; } ret = opts[i].func(argc - 2, argv + 2); xc_interface_close(xch); return !!ret; } /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/misc/mkdeb0000664000175000017500000000301013256712137013547 0ustar smbsmb#!/bin/sh # # mkdeb: package the dist/install output of a Xen build in a .deb # # Takes 2 arguments, the path to the dist directory and the version set -e if test -z "$1" -o -z "$2" ; then echo "usage: $0 path-to-XEN_ROOT xen-version" exit 1 fi cd $1 version=$2 # map the architecture, if necessary case "$XEN_TARGET_ARCH" in x86_32|x86_32p) arch=i386 ;; x86_64) arch=amd64 ;; arm32) arch=armhf ;; arm64) arch=$XEN_TARGET_ARCH;; *) echo "Unknown XEN_TARGET_ARCH $XEN_TARGET_ARCH" >&2 exit 1 ;; esac # Prepare the directory to package cd dist rm -rf deb cp -a install deb # Debian doesn't use /usr/lib64 for 64-bit libraries if test -d deb/usr/lib64 ; then cp -a deb/usr/lib64/* deb/usr/lib/ rm -rf deb/usr/lib64 fi # Fill in the debian boilerplate mkdir -p deb/DEBIAN cat >deb/DEBIAN/control <deb/DEBIAN/conffiles # Package it up chown -R root:root deb dpkg --build deb xen-upstream-$version.deb # Tidy up after ourselves rm -rf deb xen-4.9.2/tools/misc/xencov.c0000664000175000017500000000703013256712137014216 0ustar smbsmb/* * xencov: extract test coverage information from Xen. * * Copyright (c) 2013, 2016, Citrix Systems R&D Ltd. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include #include #include #include #include #include #include static xc_interface *xch = NULL; int gcov_sysctl(int op, struct xen_sysctl *sysctl, struct xc_hypercall_buffer *buf, uint32_t buf_size) { DECLARE_HYPERCALL_BUFFER_ARGUMENT(buf); memset(sysctl, 0, sizeof(*sysctl)); sysctl->cmd = XEN_SYSCTL_gcov_op; sysctl->u.gcov_op.cmd = op; sysctl->u.gcov_op.size = buf_size; set_xen_guest_handle(sysctl->u.gcov_op.buffer, buf); return xc_sysctl(xch, sysctl); } static void gcov_read(const char *fn) { struct xen_sysctl sys; uint32_t total_len; DECLARE_HYPERCALL_BUFFER(uint8_t, p); FILE *f; if (gcov_sysctl(XEN_SYSCTL_GCOV_get_size, &sys, NULL, 0) < 0) err(1, "getting total length"); total_len = sys.u.gcov_op.size; /* Shouldn't exceed a few hundred kilobytes */ if (total_len > 8u * 1024u * 1024u) errx(1, "gcov data too big %u bytes\n", total_len); p = xc_hypercall_buffer_alloc(xch, p, total_len); if (!p) err(1, "allocating buffer"); memset(p, 0, total_len); if (gcov_sysctl(XEN_SYSCTL_GCOV_read, &sys, HYPERCALL_BUFFER(p), total_len) < 0) err(1, "getting gcov data"); if (!strcmp(fn, "-")) f = stdout; else f = fopen(fn, "w"); if (!f) err(1, "opening output file"); if (fwrite(p, 1, total_len, f) != total_len) err(1, "writing gcov data to file"); if (f != stdout) fclose(f); xc_hypercall_buffer_free(xch, p); } static void gcov_reset(void) { struct xen_sysctl sys; if (gcov_sysctl(XEN_SYSCTL_GCOV_reset, &sys, NULL, 0) < 0) err(1, "resetting gcov information"); } static void usage(int exit_code) { FILE *out = exit_code ? stderr : stdout; fprintf(out, "xencov {reset|read} []\n" "\treset reset information\n" "\tread read information from xen to filename\n" "\tfilename optional filename (default output)\n" ); exit(exit_code); } int main(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': usage(0); break; default: usage(1); } } argv += optind; argc -= optind; if (argc <= 0) usage(1); xch = xc_interface_open(NULL, NULL, 0); if (!xch) err(1, "opening xc interface"); if (strcmp(argv[0], "reset") == 0) gcov_reset(); else if (strcmp(argv[0], "read") == 0) gcov_read(argc > 1 ? argv[1] : "-"); else usage(1); xc_interface_close(xch); return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/misc/Makefile0000664000175000017500000000725513256712137014221 0ustar smbsmbXEN_ROOT=$(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk CFLAGS += -Werror # Include configure output (config.h) CFLAGS += -include $(XEN_ROOT)/tools/config.h CFLAGS += $(CFLAGS_libxenevtchn) CFLAGS += $(CFLAGS_libxenctrl) CFLAGS += $(CFLAGS_xeninclude) CFLAGS += $(CFLAGS_libxenstore) # Everything to be installed in regular bin/ INSTALL_BIN-$(CONFIG_X86) += xen-cpuid INSTALL_BIN-$(CONFIG_X86) += xen-detect INSTALL_BIN += xencons INSTALL_BIN += xencov_split INSTALL_BIN += $(INSTALL_BIN-y) # Everything to be installed in regular sbin/ INSTALL_SBIN += xen-bugtool INSTALL_SBIN-$(CONFIG_MIGRATE) += xen-hptool INSTALL_SBIN-$(CONFIG_X86) += xen-hvmcrash INSTALL_SBIN-$(CONFIG_X86) += xen-hvmctx INSTALL_SBIN-$(CONFIG_X86) += xen-lowmemd INSTALL_SBIN-$(CONFIG_X86) += xen-mfndump INSTALL_SBIN += xen-ringwatch INSTALL_SBIN += xen-tmem-list-parse INSTALL_SBIN += xencov INSTALL_SBIN += xenlockprof INSTALL_SBIN += xenperf INSTALL_SBIN += xenpm INSTALL_SBIN += xenwatchdogd INSTALL_SBIN += xen-livepatch INSTALL_SBIN += $(INSTALL_SBIN-y) # Everything to be installed in a private bin/ INSTALL_PRIVBIN += xenpvnetboot # Everything to be installed TARGETS_ALL := $(INSTALL_BIN) $(INSTALL_SBIN) $(INSTALL_PRIVBIN) # Everything which only needs copying to install TARGETS_COPY += xen-bugtool TARGETS_COPY += xen-ringwatch TARGETS_COPY += xencons TARGETS_COPY += xencov_split TARGETS_COPY += xenpvnetboot # Everything which needs to be built TARGETS_BUILD := $(filter-out $(TARGETS_COPY),$(TARGETS_ALL)) .PHONY: all build all build: $(TARGETS_BUILD) .PHONY: install install: build $(INSTALL_DIR) $(DESTDIR)$(bindir) $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN) $(INSTALL_PYTHON_PROG) $(INSTALL_BIN) $(DESTDIR)$(bindir) $(INSTALL_PYTHON_PROG) $(INSTALL_SBIN) $(DESTDIR)$(sbindir) $(INSTALL_PYTHON_PROG) $(INSTALL_PRIVBIN) $(DESTDIR)$(LIBEXEC_BIN) .PHONY: clean clean: $(RM) *.o $(TARGETS_BUILD) *~ $(DEPS) .PHONY: distclean distclean: clean xen-cpuid: xen-cpuid.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(APPEND_LDFLAGS) xen-hvmctx: xen-hvmctx.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS) xen-hvmcrash: xen-hvmcrash.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS) xenperf: xenperf.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS) xenpm: xenpm.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS) xenlockprof: xenlockprof.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS) # xen-hptool incorrectly uses libxc internals xen-hptool.o: CFLAGS += -I$(XEN_ROOT)/tools/libxc $(CFLAGS_libxencall) xen-hptool: xen-hptool.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenevtchn) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenstore) $(APPEND_LDFLAGS) # xen-mfndump incorrectly uses libxc internals xen-mfndump.o: CFLAGS += -I$(XEN_ROOT)/tools/libxc $(CFLAGS_libxencall) xen-mfndump: xen-mfndump.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenevtchn) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(APPEND_LDFLAGS) xenwatchdogd: xenwatchdogd.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS) xen-livepatch: xen-livepatch.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS) xen-lowmemd: xen-lowmemd.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenevtchn) $(LDLIBS_libxenctrl) $(LDLIBS_libxenstore) $(APPEND_LDFLAGS) xencov: xencov.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS) -include $(DEPS) xen-4.9.2/tools/misc/xen-lowmemd.c0000664000175000017500000000674113256712137015160 0ustar smbsmb/* * xen-lowmemd: demo VIRQ_ENOMEM * Andres Lagar-Cavilla (GridCentric Inc.) */ #include #include #include #include #include #include static evtchn_port_t virq_port = ~0; static xenevtchn_handle *xce_handle = NULL; static xc_interface *xch = NULL; static struct xs_handle *xs_handle = NULL; void cleanup(void) { if (virq_port != ~0) xenevtchn_unbind(xce_handle, virq_port); if (xce_handle) xenevtchn_close(xce_handle); if (xch) xc_interface_close(xch); if (xs_handle) xs_daemon_close(xs_handle); } /* Never shrink dom0 below 1 GiB */ #define DOM0_FLOOR (1 << 30) #define DOM0_FLOOR_PG ((DOM0_FLOOR) >> 12) /* Act if free memory is less than 92 MiB */ #define THRESHOLD (92 << 20) #define THRESHOLD_PG ((THRESHOLD) >> 12) #define BUFSZ 512 void handle_low_mem(void) { xc_dominfo_t dom0_info; xc_physinfo_t info; unsigned long long free_pages, dom0_pages, diff, dom0_target; char data[BUFSZ], error[BUFSZ]; if (xc_physinfo(xch, &info) < 0) { perror("Getting physinfo failed"); return; } free_pages = (unsigned long long) info.free_pages; printf("Available free pages: 0x%llx:%llux\n", free_pages, free_pages); /* Don't do anything if we have more than the threshold free */ if ( free_pages >= THRESHOLD_PG ) return; diff = THRESHOLD_PG - free_pages; if (xc_domain_getinfo(xch, 0, 1, &dom0_info) < 1) { perror("Failed to get dom0 info"); return; } dom0_pages = (unsigned long long) dom0_info.nr_pages; printf("Dom0 pages: 0x%llx:%llu\n", dom0_pages, dom0_pages); dom0_target = dom0_pages - diff; if (dom0_target <= DOM0_FLOOR_PG) return; printf("Shooting for dom0 target 0x%llx:%llu\n", dom0_target, dom0_target); snprintf(data, BUFSZ, "%llu", dom0_target); if (!xs_write(xs_handle, XBT_NULL, "/local/domain/0/memory/target", data, strlen(data))) { snprintf(error, BUFSZ,"Failed to write target %s to xenstore", data); perror(error); } } int main(int argc, char *argv[]) { int rc; atexit(cleanup); xch = xc_interface_open(NULL, NULL, 0); if (xch == NULL) { perror("Failed to open xc interface"); return 1; } xce_handle = xenevtchn_open(NULL, 0); if (xce_handle == NULL) { perror("Failed to open evtchn device"); return 2; } xs_handle = xs_daemon_open(); if (xs_handle == NULL) { perror("Failed to open xenstore connection"); return 3; } if ((rc = xenevtchn_bind_virq(xce_handle, VIRQ_ENOMEM)) == -1) { perror("Failed to bind to domain exception virq port"); return 4; } virq_port = rc; while(1) { evtchn_port_t port; if ((port = xenevtchn_pending(xce_handle)) == -1) { perror("Failed to listen for pending event channel"); return 5; } if (port != virq_port) { char data[BUFSZ]; snprintf(data, BUFSZ, "Wrong port, got %d expected %d", port, virq_port); perror(data); return 6; } if (xenevtchn_unmask(xce_handle, port) == -1) { perror("Failed to unmask port"); return 7; } printf("Got a virq kick, time to get work\n"); handle_low_mem(); } return 0; } xen-4.9.2/tools/misc/xen-hvmctx.c0000664000175000017500000004276013256712137015026 0ustar smbsmb/* * xen-hvmctx.c * * Print out the contents of a HVM save record in a human-readable way. * * Tim Deegan * Copyright (c) 2008 Citrix Systems, Inc. * * 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 #include #include #include #include #include #include #include #include #include #include #include #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 #include #include #include 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); printf(" gtsc_khz %lu\n", (unsigned long) h.gtsc_khz); } 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[16]; 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" " idtr (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_domain(void) { HVM_SAVE_TYPE(VIRIDIAN_DOMAIN) p; READ(p); printf(" VIRIDIAN_DOMAIN: hypercall gpa 0x%llx, guest_os_id 0x%llx\n", (unsigned long long) p.hypercall_gpa, (unsigned long long) p.guest_os_id); } static void dump_viridian_vcpu(void) { HVM_SAVE_TYPE(VIRIDIAN_VCPU) p; READ(p); printf(" VIRIDIAN_VCPU: vp_assist_msr 0x%llx, vp_assist_vector 0x%x\n", (unsigned long long) p.vp_assist_msr, p.vp_assist_vector); } static void dump_vmce_vcpu(void) { HVM_SAVE_TYPE(VMCE_VCPU) p; READ(p); printf(" VMCE_VCPU: caps %" PRIx64 "\n", p.caps); printf(" VMCE_VCPU: bank0 mci_ctl2 %" PRIx64 "\n", p.mci_ctl2_bank0); printf(" VMCE_VCPU: bank1 mci_ctl2 %" PRIx64 "\n", p.mci_ctl2_bank1); } static void dump_tsc_adjust(void) { HVM_SAVE_TYPE(TSC_ADJUST) p; READ(p); printf(" TSC_ADJUST: tsc_adjust %" PRIx64 "\n", p.tsc_adjust); } int main(int argc, char **argv) { int entry, domid; xc_interface *xch; struct hvm_save_descriptor desc; if ( argc != 2 || !argv[1] || (domid = atoi(argv[1])) < 0 ) { fprintf(stderr, "usage: %s \n", argv[0]); exit(1); } xch = xc_interface_open(0,0,0); if ( !xch ) { 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_DOMAIN): dump_viridian_domain(); break; case HVM_SAVE_CODE(VIRIDIAN_VCPU): dump_viridian_vcpu(); break; case HVM_SAVE_CODE(VMCE_VCPU): dump_vmce_vcpu(); break; case HVM_SAVE_CODE(TSC_ADJUST): dump_tsc_adjust(); 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; } xen-4.9.2/tools/misc/xen-tmem-list-parse.c0000664000175000017500000002750513256712137016540 0ustar smbsmb/* * Parse output from tmem-list and reformat to human-readable * * NOTE: NEVER delete a parse call as this file documents backwards * compatibility for older versions of tmem-list and we don't want to * accidentally reuse an old tag * * Copyright (c) 2009, Dan Magenheimer, Oracle Corp. */ #include #include #include #define BUFSIZE 4096 #define PAGE_SIZE 4096 unsigned long long parse(char *s,char *match) { char *s1 = strstr(s,match); unsigned long long ret; if ( s1 == NULL ) return 0LL; s1 += 2; if ( *s1++ != ':' ) return 0LL; sscanf(s1,"%llu",&ret); return ret; } unsigned long long parse_hex(char *s,char *match) { char *s1 = strstr(s,match); unsigned long long ret; if ( s1 == NULL ) return 0LL; s1 += 2; if ( *s1++ != ':' ) return 0LL; sscanf(s1,"%llx",&ret); return ret; } unsigned long long parse2(char *s,char *match1, char *match2) { char match[3]; match[0] = *match1; match[1] = *match2; match[2] = '\0'; return parse(s,match); } void parse_string(char *s,char *match, char *buf, int len) { char *s1 = strstr(s,match); int i; if ( s1 == NULL ) return; s1 += 2; if ( *s1++ != ':' ) return; for ( i = 0; i < len; i++ ) *buf++ = *s1++; } void parse_sharers(char *s, char *match, char *buf, int len) { char *s1 = strstr(s,match); char *b = buf; if ( s1 == NULL ) return; while ( s1 ) { s1 += 2; if (*s1++ != ':') return; while (*s1 >= '0' && *s1 <= '9') *b++ = *s1++; *b++ = ','; s1 = strstr(s1,match); } if ( b != buf ) *--b = '\0'; } void parse_global(char *s) { unsigned long long total_ops = parse(s,"Tt"); unsigned long long errored_ops = parse(s,"Te"); unsigned long long failed_copies = parse(s,"Cf"); unsigned long long alloc_failed = parse(s,"Af"); unsigned long long alloc_page_failed = parse(s,"Pf"); unsigned long long avail_pages = parse(s,"Ta"); unsigned long long low_on_memory = parse(s,"Lm"); unsigned long long evicted_pgs = parse(s,"Et"); unsigned long long evict_attempts = parse(s,"Ea"); unsigned long long relinq_pgs = parse(s,"Rt"); unsigned long long relinq_attempts = parse(s,"Ra"); unsigned long long max_evicts_per_relinq = parse(s,"Rx"); unsigned long long total_flush_pool = parse(s,"Fp"); unsigned long long global_eph_count = parse(s,"Ec"); unsigned long long global_eph_max = parse(s,"Em"); unsigned long long obj_count = parse(s,"Oc"); unsigned long long obj_max = parse(s,"Om"); unsigned long long rtree_node_count = parse(s,"Nc"); unsigned long long rtree_node_max = parse(s,"Nm"); unsigned long long pgp_count = parse(s,"Pc"); unsigned long long pgp_max = parse(s,"Pm"); unsigned long long page_count = parse(s,"Fc"); unsigned long long max_page_count = parse(s,"Fm"); unsigned long long pcd_count = parse(s,"Sc"); unsigned long long max_pcd_count = parse(s,"Sm"); unsigned long long pcd_tot_tze_size = parse(s,"Zt"); unsigned long long pcd_tot_csize = parse(s,"Gz"); unsigned long long deduped_puts = parse(s,"Gd"); unsigned long long tot_good_eph_puts = parse(s,"Ep"); printf("total tmem ops=%llu (errors=%llu) -- tmem pages avail=%llu\n", total_ops, errored_ops, avail_pages); printf("datastructs: objs=%llu (max=%llu) pgps=%llu (max=%llu) " "nodes=%llu (max=%llu) pages=%llu (max=%llu) ", obj_count, obj_max, pgp_count, pgp_max, rtree_node_count, rtree_node_max, page_count,max_page_count); if (max_pcd_count != 0 && global_eph_count != 0 && tot_good_eph_puts != 0) { printf("pcds=%llu (max=%llu) ", pcd_count,max_pcd_count); printf("deduped: avg=%4.2f%% (curr=%4.2f%%) ", ((deduped_puts*1.0)/tot_good_eph_puts)*100, (1.0-(pcd_count*1.0)/global_eph_count)*100); } if (pcd_count != 0) { if (pcd_tot_tze_size && (pcd_tot_tze_size < pcd_count*PAGE_SIZE)) printf("tze savings=%4.2f%% ", (1.0-(pcd_tot_tze_size*1.0)/(pcd_count*PAGE_SIZE))*100); if (pcd_tot_csize && (pcd_tot_csize < pcd_count*PAGE_SIZE)) printf("compression savings=%4.2f%% ", (1.0-(pcd_tot_csize*1.0)/(pcd_count*PAGE_SIZE))*100); } printf("\n"); printf("misc: failed_copies=%llu alloc_failed=%llu alloc_page_failed=%llu " "low_mem=%llu evicted=%llu/%llu relinq=%llu/%llu, " "max_evicts_per_relinq=%llu, flush_pools=%llu, " "eph_count=%llu, eph_max=%llu\n", failed_copies, alloc_failed, alloc_page_failed, low_on_memory, evicted_pgs, evict_attempts, relinq_pgs, relinq_attempts, max_evicts_per_relinq, total_flush_pool, global_eph_count, global_eph_max); } #define PARSE_CYC_COUNTER(s,x,prefix) unsigned long long \ x##_count = parse2(s,prefix,"n"), \ x##_sum_cycles = parse2(s,prefix,"t"), \ x##_max_cycles = parse2(s,prefix,"x"), \ x##_min_cycles = parse2(s,prefix,"m") #define PRINTF_CYC_COUNTER(x,text) \ if (x##_count) printf(text" avg=%llu, max=%llu, " \ "min=%llu, samples=%llu\n", \ x##_sum_cycles ? (x##_sum_cycles/x##_count) : 0, \ x##_max_cycles, x##_min_cycles, x##_count) void parse_time_stats(char *s) { PARSE_CYC_COUNTER(s,succ_get,"G"); PARSE_CYC_COUNTER(s,succ_put,"P"); PARSE_CYC_COUNTER(s,non_succ_get,"g"); PARSE_CYC_COUNTER(s,non_succ_put,"p"); PARSE_CYC_COUNTER(s,flush,"F"); PARSE_CYC_COUNTER(s,flush_obj,"O"); PARSE_CYC_COUNTER(s,pg_copy,"C"); PARSE_CYC_COUNTER(s,compress,"c"); PARSE_CYC_COUNTER(s,decompress,"d"); PRINTF_CYC_COUNTER(succ_get,"succ get cycles:"); PRINTF_CYC_COUNTER(succ_put,"succ put cycles:"); PRINTF_CYC_COUNTER(non_succ_get,"failed get cycles:"); PRINTF_CYC_COUNTER(non_succ_put,"failed put cycles:"); PRINTF_CYC_COUNTER(flush,"flush cycles:"); PRINTF_CYC_COUNTER(flush_obj,"flush_obj cycles:"); PRINTF_CYC_COUNTER(pg_copy,"page copy cycles:"); PRINTF_CYC_COUNTER(compress,"compression cycles:"); PRINTF_CYC_COUNTER(decompress,"decompression cycles:"); } void parse_client(char *s) { unsigned long cli_id = parse(s,"CI"); unsigned long weight = parse(s,"ww"); unsigned long cap = parse(s,"ca"); unsigned long compress = parse(s,"co"); unsigned long frozen = parse(s,"fr"); unsigned long long eph_count = parse(s,"Ec"); unsigned long long max_eph_count = parse(s,"Em"); unsigned long long compressed_pages = parse(s,"cp"); unsigned long long compressed_sum_size = parse(s,"cb"); unsigned long long compress_poor = parse(s,"cn"); unsigned long long compress_nomem = parse(s,"cm"); unsigned long long total_cycles = parse(s,"Tc"); unsigned long long succ_eph_gets = parse(s,"Ge"); unsigned long long succ_pers_puts = parse(s,"Pp"); unsigned long long succ_pers_gets = parse(s,"Gp"); printf("domid%lu: weight=%lu,cap=%lu,compress=%d,frozen=%d," "total_cycles=%llu,succ_eph_gets=%llu," "succ_pers_puts=%llu,succ_pers_gets=%llu," "eph_count=%llu,max_eph=%llu," "compression ratio=%lu%% (samples=%llu,poor=%llu,nomem=%llu)\n", cli_id, weight, cap, compress?1:0, frozen?1:0, total_cycles, succ_eph_gets, succ_pers_puts, succ_pers_gets, eph_count, max_eph_count, compressed_pages ? (long)((compressed_sum_size*100LL) / (compressed_pages*PAGE_SIZE)) : 0, compressed_pages, compress_poor, compress_nomem); } void parse_pool(char *s) { char pool_type[3]; unsigned long cli_id = parse(s,"CI"); unsigned long pool_id = parse(s,"PI"); unsigned long long pgp_count = parse(s,"Pc"); unsigned long long max_pgp_count = parse(s,"Pm"); unsigned long long obj_count = parse(s,"Oc"); unsigned long long max_obj_count = parse(s,"Om"); unsigned long long objnode_count = parse(s,"Nc"); unsigned long long max_objnode_count = parse(s,"Nm"); unsigned long long good_puts = parse(s,"ps"); unsigned long long puts = parse(s,"pt"); unsigned long long no_mem_puts = parse(s,"px"); unsigned long long dup_puts_flushed = parse(s,"pd"); unsigned long long dup_puts_replaced = parse(s,"pr"); unsigned long long found_gets = parse(s,"gs"); unsigned long long gets = parse(s,"gt"); unsigned long long flushs_found = parse(s,"fs"); unsigned long long flushs = parse(s,"ft"); unsigned long long flush_objs_found = parse(s,"os"); unsigned long long flush_objs = parse(s,"ot"); parse_string(s,"PT",pool_type,2); pool_type[2] = '\0'; if (pool_type[1] == 'S') return; /* no need to repeat print data for shared pools */ printf("domid%lu,id%lu[%s]:pgp=%llu(max=%llu) obj=%llu(%llu) " "objnode=%llu(%llu) puts=%llu/%llu/%llu(dup=%llu/%llu) " "gets=%llu/%llu(%llu%%) " "flush=%llu/%llu flobj=%llu/%llu\n", cli_id, pool_id, pool_type, pgp_count, max_pgp_count, obj_count, max_obj_count, objnode_count, max_objnode_count, good_puts, puts, no_mem_puts, dup_puts_flushed, dup_puts_replaced, found_gets, gets, gets ? (found_gets*100LL)/gets : 0, flushs_found, flushs, flush_objs_found, flush_objs); } void parse_shared_pool(char *s) { char pool_type[3]; char buf[BUFSIZE]; unsigned long pool_id = parse(s,"PI"); unsigned long long uid0 = parse_hex(s,"U0"); unsigned long long uid1 = parse_hex(s,"U1"); unsigned long long pgp_count = parse(s,"Pc"); unsigned long long max_pgp_count = parse(s,"Pm"); unsigned long long obj_count = parse(s,"Oc"); unsigned long long max_obj_count = parse(s,"Om"); unsigned long long objnode_count = parse(s,"Nc"); unsigned long long max_objnode_count = parse(s,"Nm"); unsigned long long good_puts = parse(s,"ps"); unsigned long long puts = parse(s,"pt"); unsigned long long no_mem_puts = parse(s,"px"); unsigned long long dup_puts_flushed = parse(s,"pd"); unsigned long long dup_puts_replaced = parse(s,"pr"); unsigned long long found_gets = parse(s,"gs"); unsigned long long gets = parse(s,"gt"); unsigned long long flushs_found = parse(s,"fs"); unsigned long long flushs = parse(s,"ft"); unsigned long long flush_objs_found = parse(s,"os"); unsigned long long flush_objs = parse(s,"ot"); parse_string(s,"PT",pool_type,2); pool_type[2] = '\0'; parse_sharers(s,"SC",buf,BUFSIZE); printf("poolid=%lu[%s] uuid=%llx.%llx, shared-by:%s: " "pgp=%llu(max=%llu) obj=%llu(%llu) " "objnode=%llu(%llu) puts=%llu/%llu/%llu(dup=%llu/%llu) " "gets=%llu/%llu(%llu%%) " "flush=%llu/%llu flobj=%llu/%llu\n", pool_id, pool_type, uid0, uid1, buf, pgp_count, max_pgp_count, obj_count, max_obj_count, objnode_count, max_objnode_count, good_puts, puts, no_mem_puts, dup_puts_flushed, dup_puts_replaced, found_gets, gets, gets ? (found_gets*100LL)/gets : 0, flushs_found, flushs, flush_objs_found, flush_objs); } int main(int ac, char **av) { char *p, c; char buf[BUFSIZE]; while ( (p = fgets(buf,BUFSIZE,stdin)) != NULL ) { c = *p++; if ( *p++ != '=' ) continue; switch ( c ) { case 'G': parse_global(p); break; case 'T': parse_time_stats(p); break; case 'C': parse_client(p); break; case 'P': parse_pool(p); break; case 'S': parse_shared_pool(p); break; default: continue; } } return 0; } xen-4.9.2/tools/misc/xenpm.c0000664000175000017500000011143213256712137014045 0ustar smbsmb/* * xenpm.c: list the power information of the available processors * Copyright (c) 2008, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #define MAX_NR_CPU 512 #include #include #include #include #include #include #include #include #include #include #define MAX_PKG_RESIDENCIES 12 #define MAX_CORE_RESIDENCIES 8 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) static xc_interface *xc_handle; static unsigned int max_cpu_nr; /* help message */ void show_help(void) { fprintf(stderr, "xen power management control tool\n\n" "usage: xenpm [args]\n\n" "xenpm command list:\n\n" " get-cpuidle-states [cpuid] list cpu idle info of CPU or all\n" " get-cpufreq-states [cpuid] list cpu freq info of CPU or all\n" " get-cpufreq-average [cpuid] average cpu frequency since last invocation\n" " for CPU or all\n" " get-cpufreq-para [cpuid] list cpu freq parameter of CPU or all\n" " set-scaling-maxfreq [cpuid] set max cpu frequency on CPU \n" " or all CPUs\n" " set-scaling-minfreq [cpuid] set min cpu frequency on CPU \n" " or all CPUs\n" " set-scaling-speed [cpuid] set scaling speed on CPU or all\n" " it is used in userspace governor.\n" " set-scaling-governor [cpuid] set scaling governor on CPU or all\n" " as userspace/performance/powersave/ondemand\n" " set-sampling-rate [cpuid] set sampling rate on CPU or all\n" " it is used in ondemand governor.\n" " set-up-threshold [cpuid] set up threshold on CPU or all\n" " it is used in ondemand governor.\n" " get-cpu-topology get thread/core/socket topology info\n" " set-sched-smt enable|disable enable/disable scheduler smt power saving\n" " set-vcpu-migration-delay set scheduler vcpu migration delay in us\n" " get-vcpu-migration-delay get scheduler vcpu migration delay\n" " set-max-cstate set the C-State limitation ( >= 0)\n" " start [seconds] start collect Cx/Px statistics,\n" " output after CTRL-C or SIGINT or several seconds.\n" " enable-turbo-mode [cpuid] enable Turbo Mode for processors that support it.\n" " disable-turbo-mode [cpuid] disable Turbo Mode for processors that support it.\n" ); } /* wrapper function */ void help_func(int argc, char *argv[]) { show_help(); } static void parse_cpuid(const char *arg, int *cpuid) { if ( sscanf(arg, "%d", cpuid) != 1 || *cpuid < 0 ) { if ( strcasecmp(arg, "all") ) { fprintf(stderr, "Invalid CPU identifier: '%s'\n", arg); exit(EINVAL); } *cpuid = -1; } } static void parse_cpuid_and_int(int argc, char *argv[], int *cpuid, int *val, const char *what) { if ( argc == 0 ) { fprintf(stderr, "Missing %s\n", what); exit(EINVAL); } if ( argc > 1 ) parse_cpuid(argv[0], cpuid); if ( sscanf(argv[argc > 1], "%d", val) != 1 ) { fprintf(stderr, "Invalid %s '%s'\n", what, argv[argc > 1]); exit(EINVAL); } } static void print_cxstat(int cpuid, struct xc_cx_stat *cxstat) { unsigned int i; printf("cpu id : %d\n", cpuid); printf("total C-states : %d\n", cxstat->nr); printf("idle time(ms) : %"PRIu64"\n", cxstat->idle_time/1000000UL); for ( i = 0; i < cxstat->nr; i++ ) { printf("C%-20d: transition [%20"PRIu64"]\n", i, cxstat->triggers[i]); printf(" residency [%20"PRIu64" ms]\n", cxstat->residencies[i]/1000000UL); } for ( i = 0; i < MAX_PKG_RESIDENCIES && i < cxstat->nr_pc; ++i ) if ( cxstat->pc[i] ) printf("pc%d : [%20"PRIu64" ms]\n", i + 1, cxstat->pc[i] / 1000000UL); for ( i = 0; i < MAX_CORE_RESIDENCIES && i < cxstat->nr_cc; ++i ) if ( cxstat->cc[i] ) printf("cc%d : [%20"PRIu64" ms]\n", i + 1, cxstat->cc[i] / 1000000UL); printf("\n"); } /* show cpu idle information on CPU cpuid */ static int get_cxstat_by_cpuid(xc_interface *xc_handle, int cpuid, struct xc_cx_stat *cxstat) { int ret = 0; int max_cx_num = 0; ret = xc_pm_get_max_cx(xc_handle, cpuid, &max_cx_num); if ( ret ) return -errno; if ( !cxstat ) return -EINVAL; if ( !max_cx_num ) return -ENODEV; cxstat->triggers = calloc(max_cx_num, sizeof(*cxstat->triggers)); cxstat->residencies = calloc(max_cx_num, sizeof(*cxstat->residencies)); cxstat->pc = calloc(MAX_PKG_RESIDENCIES, sizeof(*cxstat->pc)); cxstat->cc = calloc(MAX_CORE_RESIDENCIES, sizeof(*cxstat->cc)); if ( !cxstat->triggers || !cxstat->residencies || !cxstat->pc || !cxstat->cc ) { free(cxstat->cc); free(cxstat->pc); free(cxstat->residencies); free(cxstat->triggers); return -ENOMEM; } cxstat->nr = max_cx_num; cxstat->nr_pc = MAX_PKG_RESIDENCIES; cxstat->nr_cc = MAX_CORE_RESIDENCIES; ret = xc_pm_get_cxstat(xc_handle, cpuid, cxstat); if( ret ) { ret = -errno; free(cxstat->triggers); free(cxstat->residencies); free(cxstat->pc); free(cxstat->cc); cxstat->triggers = NULL; cxstat->residencies = NULL; cxstat->pc = NULL; cxstat->cc = NULL; } return ret; } static int show_max_cstate(xc_interface *xc_handle) { int ret = 0; uint32_t value; if ( (ret = xc_get_cpuidle_max_cstate(xc_handle, &value)) ) return ret; printf("Max possible C-state: C%d\n\n", value); return 0; } static int show_cxstat_by_cpuid(xc_interface *xc_handle, int cpuid) { int ret = 0; struct xc_cx_stat cxstatinfo; ret = get_cxstat_by_cpuid(xc_handle, cpuid, &cxstatinfo); if ( ret ) { if ( ret == -ENODEV ) fprintf(stderr, "Either Xen cpuidle is disabled or no valid information is registered!\n"); return ret; } print_cxstat(cpuid, &cxstatinfo); free(cxstatinfo.triggers); free(cxstatinfo.residencies); free(cxstatinfo.pc); free(cxstatinfo.cc); return 0; } void cxstat_func(int argc, char *argv[]) { int cpuid = -1; if ( argc > 0 ) parse_cpuid(argv[0], &cpuid); show_max_cstate(xc_handle); if ( cpuid < 0 ) { /* show cxstates on all cpus */ int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( show_cxstat_by_cpuid(xc_handle, i) == -ENODEV ) break; } else show_cxstat_by_cpuid(xc_handle, cpuid); } static void print_pxstat(int cpuid, struct xc_px_stat *pxstat) { int i; printf("cpu id : %d\n", cpuid); printf("total P-states : %d\n", pxstat->total); printf("usable P-states : %d\n", pxstat->usable); printf("current frequency : %"PRIu64" MHz\n", pxstat->pt[pxstat->cur].freq); for ( i = 0; i < pxstat->total; i++ ) { if ( pxstat->cur == i ) printf("*P%-9d", i); else printf("P%-10d", i); printf("[%4"PRIu64" MHz]", pxstat->pt[i].freq); printf(": transition [%20"PRIu64"]\n", pxstat->pt[i].count); printf(" residency [%20"PRIu64" ms]\n", pxstat->pt[i].residency/1000000UL); } printf("\n"); } /* show cpu frequency information on CPU cpuid */ static int get_pxstat_by_cpuid(xc_interface *xc_handle, int cpuid, struct xc_px_stat *pxstat) { int ret = 0; int max_px_num = 0; ret = xc_pm_get_max_px(xc_handle, cpuid, &max_px_num); if ( ret ) return -errno; if ( !pxstat) return -EINVAL; pxstat->trans_pt = malloc(max_px_num * max_px_num * sizeof(uint64_t)); if ( !pxstat->trans_pt ) return -ENOMEM; pxstat->pt = malloc(max_px_num * sizeof(struct xc_px_val)); if ( !pxstat->pt ) { free(pxstat->trans_pt); return -ENOMEM; } ret = xc_pm_get_pxstat(xc_handle, cpuid, pxstat); if( ret ) { ret = -errno; free(pxstat->trans_pt); free(pxstat->pt); pxstat->trans_pt = NULL; pxstat->pt = NULL; } return ret; } /* show cpu actual average freq information on CPU cpuid */ static int get_avgfreq_by_cpuid(xc_interface *xc_handle, int cpuid, int *avgfreq) { int ret = 0; ret = xc_get_cpufreq_avgfreq(xc_handle, cpuid, avgfreq); if ( ret ) ret = -errno; return ret; } static int show_pxstat_by_cpuid(xc_interface *xc_handle, int cpuid) { int ret = 0; struct xc_px_stat pxstatinfo; ret = get_pxstat_by_cpuid(xc_handle, cpuid, &pxstatinfo); if ( ret ) return ret; print_pxstat(cpuid, &pxstatinfo); free(pxstatinfo.trans_pt); free(pxstatinfo.pt); return 0; } void pxstat_func(int argc, char *argv[]) { int cpuid = -1; if ( argc > 0 ) parse_cpuid(argv[0], &cpuid); if ( cpuid < 0 ) { /* show pxstates on all cpus */ int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( show_pxstat_by_cpuid(xc_handle, i) == -ENODEV ) break; } else show_pxstat_by_cpuid(xc_handle, cpuid); } static int show_cpufreq_by_cpuid(xc_interface *xc_handle, int cpuid) { int ret = 0; int average_cpufreq; ret = get_avgfreq_by_cpuid(xc_handle, cpuid, &average_cpufreq); if ( ret ) return ret; printf("cpu id : %d\n", cpuid); printf("average cpu frequency: %d\n", average_cpufreq); printf("\n"); return 0; } void cpufreq_func(int argc, char *argv[]) { int cpuid = -1; if ( argc > 0 ) parse_cpuid(argv[0], &cpuid); if ( cpuid < 0 ) { /* show average frequency on all cpus */ int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( show_cpufreq_by_cpuid(xc_handle, i) == -ENODEV ) break; } else show_cpufreq_by_cpuid(xc_handle, cpuid); } static uint64_t usec_start, usec_end; static struct xc_cx_stat *cxstat, *cxstat_start, *cxstat_end; static struct xc_px_stat *pxstat, *pxstat_start, *pxstat_end; static int *avgfreq; static uint64_t *sum, *sum_cx, *sum_px; static void signal_int_handler(int signo) { int i, j, k; struct timeval tv; int cx_cap = 0, px_cap = 0; xc_cputopo_t *cputopo = NULL; unsigned max_cpus = 0; if ( xc_cputopoinfo(xc_handle, &max_cpus, NULL) != 0 ) { fprintf(stderr, "failed to discover number of CPUs: %s\n", strerror(errno)); goto out; } cputopo = calloc(max_cpus, sizeof(*cputopo)); if ( cputopo == NULL ) { fprintf(stderr, "failed to allocate hypercall buffers\n"); goto out; } if ( gettimeofday(&tv, NULL) ) { fprintf(stderr, "failed to get timeofday\n"); goto out; } usec_end = tv.tv_sec * 1000000UL + tv.tv_usec; if ( get_cxstat_by_cpuid(xc_handle, 0, NULL) != -ENODEV ) { cx_cap = 1; for ( i = 0; i < max_cpu_nr; i++ ) if ( !get_cxstat_by_cpuid(xc_handle, i, &cxstat_end[i]) ) for ( j = 0; j < cxstat_end[i].nr; j++ ) { int64_t diff = (int64_t)cxstat_end[i].residencies[j] - (int64_t)cxstat_start[i].residencies[j]; if ( diff >=0 ) sum_cx[i] += diff; } } if ( get_pxstat_by_cpuid(xc_handle, 0, NULL) != -ENODEV ) { px_cap = 1; for ( i = 0; i < max_cpu_nr; i++ ) if ( !get_pxstat_by_cpuid(xc_handle, i , &pxstat_end[i]) ) for ( j = 0; j < pxstat_end[i].total; j++ ) sum_px[i] += pxstat_end[i].pt[j].residency - pxstat_start[i].pt[j].residency; } for ( i = 0; i < max_cpu_nr; i++ ) get_avgfreq_by_cpuid(xc_handle, i, &avgfreq[i]); printf("Elapsed time (ms): %"PRIu64"\n", (usec_end - usec_start) / 1000UL); for ( i = 0; i < max_cpu_nr; i++ ) { uint64_t res, triggers; double avg_res; printf("\nCPU%d:\tResidency(ms)\t\tAvg Res(ms)\n",i); if ( cx_cap && sum_cx[i] > 0 ) { for ( j = 0; j < cxstat_end[i].nr; j++ ) { int64_t diff = (int64_t)cxstat_end[i].residencies[j] - (int64_t)cxstat_start[i].residencies[j]; res = ( diff >= 0 ) ? diff : 0; triggers = cxstat_end[i].triggers[j] - cxstat_start[i].triggers[j]; /* * triggers may be zero if the CPU has been in this state for * the whole sample or if it never entered the state */ if ( triggers == 0 && cxstat_end[i].last == j ) avg_res = (double)sum_cx[i]/1000000.0; else avg_res = (triggers==0) ? 0: (double)res/triggers/1000000.0; printf(" C%d\t%"PRIu64"\t(%5.2f%%)\t%.2f\n", j, res/1000000UL, 100 * res / (double)sum_cx[i], avg_res ); } printf("\n"); } if ( px_cap && sum_px[i]>0 ) { for ( j = 0; j < pxstat_end[i].total; j++ ) { res = pxstat_end[i].pt[j].residency - pxstat_start[i].pt[j].residency; printf(" P%d\t%"PRIu64"\t(%5.2f%%)\n", j, res / 1000000UL, 100UL * res / (double)sum_px[i]); } } if ( px_cap && avgfreq[i] ) printf(" Avg freq\t%d\tKHz\n", avgfreq[i]); } if ( cx_cap && !xc_cputopoinfo(xc_handle, &max_cpus, cputopo) ) { uint32_t socket_ids[MAX_NR_CPU]; uint32_t core_ids[MAX_NR_CPU]; uint32_t socket_nr = 0; uint32_t core_nr = 0; if ( max_cpus > MAX_NR_CPU ) max_cpus = MAX_NR_CPU; /* check validity */ for ( i = 0; i < max_cpus; i++ ) { if ( cputopo[i].core == XEN_INVALID_CORE_ID || cputopo[i].socket == XEN_INVALID_SOCKET_ID ) break; } if ( i >= max_cpus ) { /* find socket nr & core nr per socket */ for ( i = 0; i < max_cpus; i++ ) { for ( j = 0; j < socket_nr; j++ ) if ( cputopo[i].socket == socket_ids[j] ) break; if ( j == socket_nr ) { socket_ids[j] = cputopo[i].socket; socket_nr++; } for ( j = 0; j < core_nr; j++ ) if ( cputopo[i].core == core_ids[j] ) break; if ( j == core_nr ) { core_ids[j] = cputopo[i].core; core_nr++; } } /* print out CC? and PC? */ for ( i = 0; i < socket_nr; i++ ) { unsigned int n; uint64_t res; for ( j = 0; j < max_cpus; j++ ) { if ( cputopo[j].socket == socket_ids[i] ) break; } printf("\nSocket %d\n", socket_ids[i]); for ( n = 0; n < MAX_PKG_RESIDENCIES; ++n ) { if ( n >= cxstat_end[j].nr_pc ) continue; res = cxstat_end[j].pc[n]; if ( n < cxstat_start[j].nr_pc ) res -= cxstat_start[j].pc[n]; printf("\tPC%u\t%"PRIu64" ms\t%.2f%%\n", n + 1, res / 1000000UL, 100UL * res / (double)sum_cx[j]); } for ( k = 0; k < core_nr; k++ ) { for ( j = 0; j < max_cpus; j++ ) { if ( cputopo[j].socket == socket_ids[i] && cputopo[j].core == core_ids[k] ) break; } printf("\t Core %d CPU %d\n", core_ids[k], j); for ( n = 0; n < MAX_CORE_RESIDENCIES; ++n ) { if ( n >= cxstat_end[j].nr_cc ) continue; res = cxstat_end[j].cc[n]; if ( n < cxstat_start[j].nr_cc ) res -= cxstat_start[j].cc[n]; printf("\t\tCC%u\t%"PRIu64" ms\t%.2f%%\n", n + 1, res / 1000000UL, 100UL * res / (double)sum_cx[j]); } } } } } /* some clean up and then exits */ for ( i = 0; i < 2 * max_cpu_nr; i++ ) { free(cxstat[i].triggers); free(cxstat[i].residencies); free(cxstat[i].pc); free(cxstat[i].cc); free(pxstat[i].trans_pt); free(pxstat[i].pt); } free(cxstat); free(pxstat); free(sum); free(avgfreq); out: free(cputopo); xc_interface_close(xc_handle); exit(0); } void start_gather_func(int argc, char *argv[]) { int i; struct timeval tv; int timeout = 0; if ( argc == 1 ) { sscanf(argv[0], "%d", &timeout); if ( timeout <= 0 ) fprintf(stderr, "failed to set timeout seconds, falling back...\n"); else printf("Timeout set to %d seconds\n", timeout); } if ( gettimeofday(&tv, NULL) ) { fprintf(stderr, "failed to get timeofday\n"); return ; } usec_start = tv.tv_sec * 1000000UL + tv.tv_usec; sum = calloc(2 * max_cpu_nr, sizeof(*sum)); if ( sum == NULL ) return ; cxstat = calloc(2 * max_cpu_nr, sizeof(*cxstat)); if ( cxstat == NULL ) { free(sum); return ; } pxstat = calloc(2 * max_cpu_nr, sizeof(*pxstat)); if ( pxstat == NULL ) { free(sum); free(cxstat); return ; } avgfreq = calloc(max_cpu_nr, sizeof(*avgfreq)); if ( avgfreq == NULL ) { free(sum); free(cxstat); free(pxstat); return ; } sum_cx = sum; sum_px = sum + max_cpu_nr; cxstat_start = cxstat; cxstat_end = cxstat + max_cpu_nr; pxstat_start = pxstat; pxstat_end = pxstat + max_cpu_nr; if ( get_cxstat_by_cpuid(xc_handle, 0, NULL) == -ENODEV && get_pxstat_by_cpuid(xc_handle, 0, NULL) == -ENODEV ) { fprintf(stderr, "Xen cpu idle and frequency is disabled!\n"); return ; } for ( i = 0; i < max_cpu_nr; i++ ) { get_cxstat_by_cpuid(xc_handle, i, &cxstat_start[i]); get_pxstat_by_cpuid(xc_handle, i, &pxstat_start[i]); get_avgfreq_by_cpuid(xc_handle, i, &avgfreq[i]); } if (signal(SIGINT, signal_int_handler) == SIG_ERR) { fprintf(stderr, "failed to set signal int handler\n"); free(sum); free(pxstat); free(cxstat); free(avgfreq); return ; } if ( timeout > 0 ) { if ( signal(SIGALRM, signal_int_handler) == SIG_ERR ) { fprintf(stderr, "failed to set signal alarm handler\n"); free(sum); free(pxstat); free(cxstat); free(avgfreq); return ; } alarm(timeout); } printf("Start sampling, waiting for CTRL-C or SIGINT or SIGALARM signal ...\n"); pause(); } /* print out parameters about cpu frequency */ static void print_cpufreq_para(int cpuid, struct xc_get_cpufreq_para *p_cpufreq) { int i; printf("cpu id : %d\n", cpuid); printf("affected_cpus :"); for ( i = 0; i < p_cpufreq->cpu_num; i++ ) printf(" %d", p_cpufreq->affected_cpus[i]); printf("\n"); printf("cpuinfo frequency : max [%u] min [%u] cur [%u]\n", p_cpufreq->cpuinfo_max_freq, p_cpufreq->cpuinfo_min_freq, p_cpufreq->cpuinfo_cur_freq); printf("scaling_driver : %s\n", p_cpufreq->scaling_driver); printf("scaling_avail_gov : %s\n", p_cpufreq->scaling_available_governors); printf("current_governor : %s\n", p_cpufreq->scaling_governor); if ( !strncmp(p_cpufreq->scaling_governor, "userspace", CPUFREQ_NAME_LEN) ) { printf(" userspace specific :\n"); printf(" scaling_setspeed : %u\n", p_cpufreq->u.userspace.scaling_setspeed); } else if ( !strncmp(p_cpufreq->scaling_governor, "ondemand", CPUFREQ_NAME_LEN) ) { printf(" ondemand specific :\n"); printf(" sampling_rate : max [%u] min [%u] cur [%u]\n", p_cpufreq->u.ondemand.sampling_rate_max, p_cpufreq->u.ondemand.sampling_rate_min, p_cpufreq->u.ondemand.sampling_rate); printf(" up_threshold : %u\n", p_cpufreq->u.ondemand.up_threshold); } printf("scaling_avail_freq :"); for ( i = 0; i < p_cpufreq->freq_num; i++ ) if ( p_cpufreq->scaling_available_frequencies[i] == p_cpufreq->scaling_cur_freq ) printf(" *%d", p_cpufreq->scaling_available_frequencies[i]); else printf(" %d", p_cpufreq->scaling_available_frequencies[i]); printf("\n"); printf("scaling frequency : max [%u] min [%u] cur [%u]\n", p_cpufreq->scaling_max_freq, p_cpufreq->scaling_min_freq, p_cpufreq->scaling_cur_freq); printf("turbo mode : %s\n", p_cpufreq->turbo_enabled ? "enabled" : "disabled or n/a"); printf("\n"); } /* show cpu frequency parameters information on CPU cpuid */ static int show_cpufreq_para_by_cpuid(xc_interface *xc_handle, int cpuid) { int ret = 0; struct xc_get_cpufreq_para cpufreq_para, *p_cpufreq = &cpufreq_para; p_cpufreq->cpu_num = 0; p_cpufreq->freq_num = 0; p_cpufreq->gov_num = 0; p_cpufreq->affected_cpus = NULL; p_cpufreq->scaling_available_frequencies = NULL; p_cpufreq->scaling_available_governors = NULL; p_cpufreq->turbo_enabled = 0; do { free(p_cpufreq->affected_cpus); free(p_cpufreq->scaling_available_frequencies); free(p_cpufreq->scaling_available_governors); p_cpufreq->affected_cpus = NULL; p_cpufreq->scaling_available_frequencies = NULL; p_cpufreq->scaling_available_governors = NULL; if (!(p_cpufreq->affected_cpus = malloc(p_cpufreq->cpu_num * sizeof(uint32_t)))) { fprintf(stderr, "[CPU%d] failed to malloc for affected_cpus\n", cpuid); ret = -ENOMEM; goto out; } if (!(p_cpufreq->scaling_available_frequencies = malloc(p_cpufreq->freq_num * sizeof(uint32_t)))) { fprintf(stderr, "[CPU%d] failed to malloc for scaling_available_frequencies\n", cpuid); ret = -ENOMEM; goto out; } if (!(p_cpufreq->scaling_available_governors = malloc(p_cpufreq->gov_num * CPUFREQ_NAME_LEN * sizeof(char)))) { fprintf(stderr, "[CPU%d] failed to malloc for scaling_available_governors\n", cpuid); ret = -ENOMEM; goto out; } ret = xc_get_cpufreq_para(xc_handle, cpuid, p_cpufreq); } while ( ret && errno == EAGAIN ); if ( ret == 0 ) print_cpufreq_para(cpuid, p_cpufreq); else if ( errno == ENODEV ) { ret = -ENODEV; fprintf(stderr, "Xen cpufreq is not enabled!\n"); } else fprintf(stderr, "[CPU%d] failed to get cpufreq parameter\n", cpuid); out: free(p_cpufreq->scaling_available_governors); free(p_cpufreq->scaling_available_frequencies); free(p_cpufreq->affected_cpus); return ret; } void cpufreq_para_func(int argc, char *argv[]) { int cpuid = -1; if ( argc > 0 ) parse_cpuid(argv[0], &cpuid); if ( cpuid < 0 ) { /* show cpu freqency information on all cpus */ int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( show_cpufreq_para_by_cpuid(xc_handle, i) == -ENODEV ) break; } else show_cpufreq_para_by_cpuid(xc_handle, cpuid); } void scaling_max_freq_func(int argc, char *argv[]) { int cpuid = -1, freq = -1; parse_cpuid_and_int(argc, argv, &cpuid, &freq, "frequency"); if ( cpuid < 0 ) { int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( xc_set_cpufreq_para(xc_handle, i, SCALING_MAX_FREQ, freq) ) fprintf(stderr, "[CPU%d] failed to set scaling max freq (%d - %s)\n", i, errno, strerror(errno)); } else { if ( xc_set_cpufreq_para(xc_handle, cpuid, SCALING_MAX_FREQ, freq) ) fprintf(stderr, "failed to set scaling max freq (%d - %s)\n", errno, strerror(errno)); } } void scaling_min_freq_func(int argc, char *argv[]) { int cpuid = -1, freq = -1; parse_cpuid_and_int(argc, argv, &cpuid, &freq, "frequency"); if ( cpuid < 0 ) { int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( xc_set_cpufreq_para(xc_handle, i, SCALING_MIN_FREQ, freq) ) fprintf(stderr, "[CPU%d] failed to set scaling min freq (%d - %s)\n", i, errno, strerror(errno)); } else { if ( xc_set_cpufreq_para(xc_handle, cpuid, SCALING_MIN_FREQ, freq) ) fprintf(stderr, "failed to set scaling min freq (%d - %s)\n", errno, strerror(errno)); } } void scaling_speed_func(int argc, char *argv[]) { int cpuid = -1, speed = -1; parse_cpuid_and_int(argc, argv, &cpuid, &speed, "speed"); if ( cpuid < 0 ) { int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( xc_set_cpufreq_para(xc_handle, i, SCALING_SETSPEED, speed) ) fprintf(stderr, "[CPU%d] failed to set scaling speed (%d - %s)\n", i, errno, strerror(errno)); } else { if ( xc_set_cpufreq_para(xc_handle, cpuid, SCALING_SETSPEED, speed) ) fprintf(stderr, "failed to set scaling speed (%d - %s)\n", errno, strerror(errno)); } } void scaling_sampling_rate_func(int argc, char *argv[]) { int cpuid = -1, rate = -1; parse_cpuid_and_int(argc, argv, &cpuid, &rate, "rate"); if ( cpuid < 0 ) { int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( xc_set_cpufreq_para(xc_handle, i, SAMPLING_RATE, rate) ) fprintf(stderr, "[CPU%d] failed to set scaling sampling rate (%d - %s)\n", i, errno, strerror(errno)); } else { if ( xc_set_cpufreq_para(xc_handle, cpuid, SAMPLING_RATE, rate) ) fprintf(stderr, "failed to set scaling sampling rate (%d - %s)\n", errno, strerror(errno)); } } void scaling_up_threshold_func(int argc, char *argv[]) { int cpuid = -1, threshold = -1; parse_cpuid_and_int(argc, argv, &cpuid, &threshold, "threshold"); if ( cpuid < 0 ) { int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( xc_set_cpufreq_para(xc_handle, i, UP_THRESHOLD, threshold) ) fprintf(stderr, "[CPU%d] failed to set up scaling threshold (%d - %s)\n", i, errno, strerror(errno)); } else { if ( xc_set_cpufreq_para(xc_handle, cpuid, UP_THRESHOLD, threshold) ) fprintf(stderr, "failed to set up scaling threshold (%d - %s)\n", errno, strerror(errno)); } } void scaling_governor_func(int argc, char *argv[]) { int cpuid = -1; char *name; if ( argc >= 2 ) { parse_cpuid(argv[0], &cpuid); name = argv[1]; } else if ( argc > 0 ) name = argv[0]; else { fprintf(stderr, "Missing argument(s)\n"); exit(EINVAL); } if ( cpuid < 0 ) { int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( xc_set_cpufreq_gov(xc_handle, i, name) ) fprintf(stderr, "[CPU%d] failed to set governor name (%d - %s)\n", i, errno, strerror(errno)); } else { if ( xc_set_cpufreq_gov(xc_handle, cpuid, name) ) fprintf(stderr, "failed to set governor name (%d - %s)\n", errno, strerror(errno)); } } void cpu_topology_func(int argc, char *argv[]) { xc_cputopo_t *cputopo = NULL; unsigned max_cpus = 0; int i, rc; if ( xc_cputopoinfo(xc_handle, &max_cpus, NULL) != 0 ) { rc = errno; fprintf(stderr, "failed to discover number of CPUs (%d - %s)\n", errno, strerror(errno)); goto out; } cputopo = calloc(max_cpus, sizeof(*cputopo)); if ( cputopo == NULL ) { rc = ENOMEM; fprintf(stderr, "failed to allocate hypercall buffers\n"); goto out; } if ( xc_cputopoinfo(xc_handle, &max_cpus, cputopo) ) { rc = errno; fprintf(stderr, "Cannot get Xen CPU topology (%d - %s)\n", errno, strerror(errno)); goto out; } printf("CPU\tcore\tsocket\tnode\n"); for ( i = 0; i < max_cpus; i++ ) { if ( cputopo[i].core == XEN_INVALID_CORE_ID ) continue; printf("CPU%d\t %d\t %d\t %d\n", i, cputopo[i].core, cputopo[i].socket, cputopo[i].node); } rc = 0; out: free(cputopo); if ( rc ) exit(rc); } void set_sched_smt_func(int argc, char *argv[]) { int value; if ( argc != 1 ) { fprintf(stderr, "Missing or invalid argument(s)\n"); exit(EINVAL); } if ( !strcasecmp(argv[0], "disable") ) value = 0; else if ( !strcasecmp(argv[0], "enable") ) value = 1; else { fprintf(stderr, "Invalid argument: %s\n", argv[0]); exit(EINVAL); } if ( !xc_set_sched_opt_smt(xc_handle, value) ) printf("%s sched_smt_power_savings succeeded\n", argv[0]); else fprintf(stderr, "%s sched_smt_power_savings failed (%d - %s)\n", argv[0], errno, strerror(errno)); } void set_vcpu_migration_delay_func(int argc, char *argv[]) { int value; if ( argc != 1 || (value = atoi(argv[0])) < 0 ) { fprintf(stderr, "Missing or invalid argument(s)\n"); exit(EINVAL); } if ( !xc_set_vcpu_migration_delay(xc_handle, value) ) printf("set vcpu migration delay to %d us succeeded\n", value); else fprintf(stderr, "set vcpu migration delay failed (%d - %s)\n", errno, strerror(errno)); } void get_vcpu_migration_delay_func(int argc, char *argv[]) { uint32_t value; if ( argc ) fprintf(stderr, "Ignoring argument(s)\n"); if ( !xc_get_vcpu_migration_delay(xc_handle, &value) ) printf("Scheduler vcpu migration delay is %d us\n", value); else fprintf(stderr, "Failed to get scheduler vcpu migration delay (%d - %s)\n", errno, strerror(errno)); } void set_max_cstate_func(int argc, char *argv[]) { int value; if ( argc != 1 || sscanf(argv[0], "%d", &value) != 1 || value < 0 ) { fprintf(stderr, "Missing or invalid argument(s)\n"); exit(EINVAL); } if ( !xc_set_cpuidle_max_cstate(xc_handle, (uint32_t)value) ) printf("set max_cstate to C%d succeeded\n", value); else fprintf(stderr, "set max_cstate to C%d failed (%d - %s)\n", value, errno, strerror(errno)); } void enable_turbo_mode(int argc, char *argv[]) { int cpuid = -1; if ( argc > 0 ) parse_cpuid(argv[0], &cpuid); if ( cpuid < 0 ) { /* enable turbo modes on all cpus, * only make effects on dbs governor */ int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( xc_enable_turbo(xc_handle, i) ) fprintf(stderr, "[CPU%d] failed to enable turbo mode (%d - %s)\n", i, errno, strerror(errno)); } else if ( xc_enable_turbo(xc_handle, cpuid) ) fprintf(stderr, "failed to enable turbo mode (%d - %s)\n", errno, strerror(errno)); } void disable_turbo_mode(int argc, char *argv[]) { int cpuid = -1; if ( argc > 0 ) parse_cpuid(argv[0], &cpuid); if ( cpuid < 0 ) { /* disable turbo modes on all cpus, * only make effects on dbs governor */ int i; for ( i = 0; i < max_cpu_nr; i++ ) if ( xc_disable_turbo(xc_handle, i) ) fprintf(stderr, "[CPU%d] failed to disable turbo mode (%d - %s)\n", i, errno, strerror(errno)); } else if ( xc_disable_turbo(xc_handle, cpuid) ) fprintf(stderr, "failed to disable turbo mode (%d - %s)\n", errno, strerror(errno)); } struct { const char *name; void (*function)(int argc, char *argv[]); } main_options[] = { { "help", help_func }, { "get-cpuidle-states", cxstat_func }, { "get-cpufreq-states", pxstat_func }, { "get-cpufreq-average", cpufreq_func }, { "start", start_gather_func }, { "get-cpufreq-para", cpufreq_para_func }, { "set-scaling-maxfreq", scaling_max_freq_func }, { "set-scaling-minfreq", scaling_min_freq_func }, { "set-scaling-governor", scaling_governor_func }, { "set-scaling-speed", scaling_speed_func }, { "set-sampling-rate", scaling_sampling_rate_func }, { "set-up-threshold", scaling_up_threshold_func }, { "get-cpu-topology", cpu_topology_func}, { "set-sched-smt", set_sched_smt_func}, { "get-vcpu-migration-delay", get_vcpu_migration_delay_func}, { "set-vcpu-migration-delay", set_vcpu_migration_delay_func}, { "set-max-cstate", set_max_cstate_func}, { "enable-turbo-mode", enable_turbo_mode }, { "disable-turbo-mode", disable_turbo_mode }, }; int main(int argc, char *argv[]) { int i, ret = 0; xc_physinfo_t physinfo = { 0 }; int nr_matches = 0; int matches_main_options[ARRAY_SIZE(main_options)]; if ( argc < 2 ) { show_help(); return 0; } xc_handle = xc_interface_open(0,0,0); if ( !xc_handle ) { fprintf(stderr, "failed to get the handler\n"); return EIO; } ret = xc_physinfo(xc_handle, &physinfo); if ( ret ) { ret = errno; fprintf(stderr, "failed to get processor information (%d - %s)\n", ret, strerror(ret)); xc_interface_close(xc_handle); return ret; } max_cpu_nr = physinfo.nr_cpus; /* calculate how many options match with user's input */ for ( i = 0; i < ARRAY_SIZE(main_options); i++ ) if ( !strncmp(main_options[i].name, argv[1], strlen(argv[1])) ) matches_main_options[nr_matches++] = i; if ( nr_matches > 1 ) { fprintf(stderr, "Ambiguous options: "); for ( i = 0; i < nr_matches; i++ ) fprintf(stderr, " %s", main_options[matches_main_options[i]].name); fprintf(stderr, "\n"); ret = EINVAL; } else if ( nr_matches == 1 ) /* dispatch to the corresponding function handler */ main_options[matches_main_options[0]].function(argc - 2, argv + 2); else { show_help(); ret = EINVAL; } xc_interface_close(xc_handle); return ret; } xen-4.9.2/tools/misc/xenlockprof.c0000664000175000017500000000651113256712137015251 0ustar smbsmb/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- **************************************************************************** * (C) 2009 - Juergen Gross - Fujitsu Technology Solutions **************************************************************************** * * File: xenlockprof.c * Author: Juergen Gross (juergen.gross@ts.fujitsu.com) * Date: Oct 2009 * * Description: */ #include #include #include #include #include #include #include int main(int argc, char *argv[]) { xc_interface *xc_handle; uint32_t i, j, n; uint64_t time; double l, b, sl, sb; char name[100]; DECLARE_HYPERCALL_BUFFER(xc_lockprof_data_t, data); if ( (argc > 2) || ((argc == 2) && (strcmp(argv[1], "-r") != 0)) ) { printf("%s: [-r]\n", argv[0]); printf("no args: print lock profile data\n"); printf(" -r : reset profile data\n"); return 1; } if ( (xc_handle = xc_interface_open(0,0,0)) == 0 ) { fprintf(stderr, "Error opening xc interface: %d (%s)\n", errno, strerror(errno)); return 1; } if ( argc > 1 ) { if ( xc_lockprof_reset(xc_handle) != 0 ) { fprintf(stderr, "Error reseting profile data: %d (%s)\n", errno, strerror(errno)); return 1; } return 0; } n = 0; if ( xc_lockprof_query_number(xc_handle, &n) != 0 ) { fprintf(stderr, "Error getting number of profile records: %d (%s)\n", errno, strerror(errno)); return 1; } n += 32; /* just to be sure */ data = xc_hypercall_buffer_alloc(xc_handle, data, sizeof(*data) * n); if ( data == NULL ) { fprintf(stderr, "Could not allocate buffers: %d (%s)\n", errno, strerror(errno)); return 1; } i = n; if ( xc_lockprof_query(xc_handle, &i, &time, HYPERCALL_BUFFER(data)) != 0 ) { fprintf(stderr, "Error getting profile records: %d (%s)\n", errno, strerror(errno)); return 1; } if ( i > n ) { printf("data incomplete, %d records are missing!\n\n", i - n); i = n; } sl = 0; sb = 0; for ( j = 0; j < i; j++ ) { switch ( data[j].type ) { case LOCKPROF_TYPE_GLOBAL: sprintf(name, "global lock %s", data[j].name); break; case LOCKPROF_TYPE_PERDOM: sprintf(name, "domain %d lock %s", data[j].idx, data[j].name); break; default: sprintf(name, "unknown type(%d) %d lock %s", data[j].type, data[j].idx, data[j].name); break; } l = (double)(data[j].lock_time) / 1E+09; b = (double)(data[j].block_time) / 1E+09; sl += l; sb += b; printf("%-50s: lock:%12"PRId64"(%20.9fs), " "block:%12"PRId64"(%20.9fs)\n", name, data[j].lock_cnt, l, data[j].block_cnt, b); } l = (double)time / 1E+09; printf("total profiling time: %20.9fs\n", l); printf("total locked time: %20.9fs\n", sl); printf("total blocked time: %20.9fs\n", sb); xc_hypercall_buffer_free(xc_handle, data); return 0; } xen-4.9.2/tools/misc/xen-cpuid.c0000664000175000017500000002447213256712137014621 0ustar smbsmb#include #include #include #include #include #include #define ARRAY_SIZE(a) (sizeof a / sizeof *a) static uint32_t nr_features; static const char *str_1d[32] = { [ 0] = "fpu", [ 1] = "vme", [ 2] = "de", [ 3] = "pse", [ 4] = "tsc", [ 5] = "msr", [ 6] = "pae", [ 7] = "mce", [ 8] = "cx8", [ 9] = "apic", [10] = "REZ", [11] = "sysenter", [12] = "mtrr", [13] = "pge", [14] = "mca", [15] = "cmov", [16] = "pat", [17] = "pse36", [18] = "psn", [19] = "clflush", [20] = "REZ", [21] = "ds", [22] = "acpi", [23] = "mmx", [24] = "fxsr", [25] = "sse", [26] = "sse2", [27] = "ss", [28] = "htt", [29] = "tm", [30] = "ia64", [31] = "pbe", }; static const char *str_1c[32] = { [ 0] = "sse3", [ 1] = "pclmulqdq", [ 2] = "dtes64", [ 3] = "monitor", [ 4] = "ds-cpl", [ 5] = "vmx", [ 6] = "smx", [ 7] = "est", [ 8] = "tm2", [ 9] = "ssse3", [10] = "cntx-id", [11] = "sdgb", [12] = "fma", [13] = "cx16", [14] = "xtpr", [15] = "pdcm", [16] = "REZ", [17] = "pcid", [18] = "dca", [19] = "sse41", [20] = "sse42", [21] = "x2apic", [22] = "movebe", [23] = "popcnt", [24] = "tsc-dl", [25] = "aesni", [26] = "xsave", [27] = "osxsave", [28] = "avx", [29] = "f16c", [30] = "rdrnd", [31] = "hyper", }; static const char *str_e1d[32] = { [ 0] = "fpu", [ 1] = "vme", [ 2] = "de", [ 3] = "pse", [ 4] = "tsc", [ 5] = "msr", [ 6] = "pae", [ 7] = "mce", [ 8] = "cx8", [ 9] = "apic", [10] = "REZ", [11] = "syscall", [12] = "mtrr", [13] = "pge", [14] = "mca", [15] = "cmov", [16] = "fcmov", [17] = "pse36", [18] = "REZ", [19] = "mp", [20] = "nx", [21] = "REZ", [22] = "mmx+", [23] = "mmx", [24] = "fxsr", [25] = "fxsr+", [26] = "pg1g", [27] = "rdtscp", [28] = "REZ", [29] = "lm", [30] = "3dnow+", [31] = "3dnow", }; static const char *str_e1c[32] = { [ 0] = "lahf_lm", [ 1] = "cmp", [ 2] = "svm", [ 3] = "extapic", [ 4] = "cr8d", [ 5] = "lzcnt", [ 6] = "sse4a", [ 7] = "msse", [ 8] = "3dnowpf", [ 9] = "osvw", [10] = "ibs", [11] = "xop", [12] = "skinit", [13] = "wdt", [14] = "REZ", [15] = "lwp", [16] = "fma4", [17] = "tce", [18] = "REZ", [19] = "nodeid", [20] = "REZ", [21] = "tbm", [22] = "topoext", [23] = "perfctr_core", [24] = "perfctr_nb", [25] = "REZ", [26] = "dbx", [27] = "perftsc", [28] = "pcx_l2i", [29] = "monitorx", [30 ... 31] = "REZ", }; static const char *str_7b0[32] = { [ 0] = "fsgsbase", [ 1] = "tsc-adj", [ 2] = "sgx", [ 3] = "bmi1", [ 4] = "hle", [ 5] = "avx2", [ 6] = "REZ", [ 7] = "smep", [ 8] = "bmi2", [ 9] = "erms", [10] = "invpcid", [11] = "rtm", [12] = "pqm", [13] = "depfpp", [14] = "mpx", [15] = "pqe", [16] = "avx512f", [17] = "avx512dq", [18] = "rdseed", [19] = "adx", [20] = "smap", [21] = "avx512ifma", [22] = "pcomit", [23] = "clflushopt", [24] = "clwb", [25] = "pt", [26] = "avx512pf", [27] = "avx512er", [28] = "avx512cd", [29] = "sha", [30] = "avx512bw", [31] = "avx512vl", }; static const char *str_Da1[32] = { [ 0] = "xsaveopt", [ 1] = "xsavec", [ 2] = "xgetbv1", [ 3] = "xsaves", [4 ... 31] = "REZ", }; static const char *str_7c0[32] = { [ 0] = "prechwt1", [ 1] = "avx512vbmi", [ 2] = "REZ", [ 3] = "pku", [ 4] = "ospke", [5 ... 13] = "REZ", [14] = "avx512_vpopcntdq", [15 ... 31] = "REZ", }; static const char *str_e7d[32] = { [0 ... 7] = "REZ", [ 8] = "itsc", [9 ... 31] = "REZ", }; static const char *str_e8b[32] = { [ 0] = "clzero", [1 ... 11] = "REZ", [12] = "ibpb", [13 ... 31] = "REZ", }; static const char *str_7d0[32] = { [0 ... 1] = "REZ", [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", [4 ... 25] = "REZ", [26] = "ibrsb", [27] = "stibp", [28 ... 31] = "REZ", }; static struct { const char *name; const char *abbr; const char **strs; } decodes[] = { { "0x00000001.edx", "1d", str_1d }, { "0x00000001.ecx", "1c", str_1c }, { "0x80000001.edx", "e1d", str_e1d }, { "0x80000001.ecx", "e1c", str_e1c }, { "0x0000000d:1.eax", "Da1", str_Da1 }, { "0x00000007:0.ebx", "7b0", str_7b0 }, { "0x00000007:0.ecx", "7c0", str_7c0 }, { "0x80000007.edx", "e7d", str_e7d }, { "0x80000008.ebx", "e8b", str_e8b }, { "0x00000007:0.edx", "7d0", str_7d0 }, }; #define COL_ALIGN "18" static struct fsinfo { const char *name; uint32_t len; uint32_t *fs; } featuresets[] = { [XEN_SYSCTL_cpu_featureset_host] = { "Host", 0, NULL }, [XEN_SYSCTL_cpu_featureset_raw] = { "Raw", 0, NULL }, [XEN_SYSCTL_cpu_featureset_pv] = { "PV", 0, NULL }, [XEN_SYSCTL_cpu_featureset_hvm] = { "HVM", 0, NULL }, }; static void dump_leaf(uint32_t leaf, const char **strs) { unsigned i; if ( !strs ) { printf(" ???"); return; } for ( i = 0; i < 32; ++i ) if ( leaf & (1u << i) ) printf(" %s", strs[i] ?: "???" ); } static void decode_featureset(const uint32_t *features, const uint32_t length, const char *name, bool detail) { unsigned int i; printf("%-"COL_ALIGN"s ", name); for ( i = 0; i < length; ++i ) printf("%08x%c", features[i], i < length - 1 ? ':' : '\n'); if ( !detail ) return; for ( i = 0; i < length && i < ARRAY_SIZE(decodes); ++i ) { printf(" [%02u] %-"COL_ALIGN"s", i, decodes[i].name ?: ""); if ( decodes[i].name ) dump_leaf(features[i], decodes[i].strs); printf("\n"); } } static void get_featureset(xc_interface *xch, unsigned int idx) { struct fsinfo *f = &featuresets[idx]; f->len = xc_get_cpu_featureset_size(); f->fs = calloc(nr_features, sizeof(*f->fs)); if ( !f->fs ) err(1, "calloc(, featureset)"); if ( xc_get_cpu_featureset(xch, idx, &f->len, f->fs) ) err(1, "xc_get_featureset()"); } static void dump_info(xc_interface *xch, bool detail) { unsigned int i; printf("nr_features: %u\n", nr_features); if ( !detail ) { printf(" %"COL_ALIGN"s ", "KEY"); for ( i = 0; i < ARRAY_SIZE(decodes); ++i ) printf("%-8s ", decodes[i].abbr ?: "???"); printf("\n"); } printf("\nStatic sets:\n"); decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_KNOWN), nr_features, "Known", detail); decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_SPECIAL), nr_features, "Special", detail); decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_PV), nr_features, "PV Mask", detail); decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_HVM_SHADOW), nr_features, "HVM Shadow Mask", detail); decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_HVM_HAP), nr_features, "HVM Hap Mask", detail); printf("\nDynamic sets:\n"); for ( i = 0; i < ARRAY_SIZE(featuresets); ++i ) { get_featureset(xch, i); decode_featureset(featuresets[i].fs, featuresets[i].len, featuresets[i].name, detail); } for ( i = 0; i < ARRAY_SIZE(featuresets); ++i ) free(featuresets[i].fs); } int main(int argc, char **argv) { enum { MODE_UNKNOWN, MODE_INFO, MODE_DETAIL, MODE_INTERPRET } mode = MODE_UNKNOWN; nr_features = xc_get_cpu_featureset_size(); for ( ;; ) { int option_index = 0, c; static struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "info", no_argument, NULL, 'i' }, { "detail", no_argument, NULL, 'd' }, { "verbose", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 }, }; c = getopt_long(argc, argv, "hidv", long_options, &option_index); if ( c == -1 ) break; switch ( c ) { default: printf("Bad option '%c'\n", c); /* Fallthough */ case 'h': printf("Usage: %s [ info | detail | * ]\n", argv[0]); return 0; case 'i': mode = MODE_INFO; break; case 'd': case 'v': mode = MODE_DETAIL; break; } } if ( mode == MODE_UNKNOWN ) { if ( optind == argc ) mode = MODE_INFO; else if ( optind < argc ) { if ( !strcmp(argv[optind], "info") ) { mode = MODE_INFO; optind++; } else if ( !strcmp(argv[optind], "detail") ) { mode = MODE_DETAIL; optind++; } else mode = MODE_INTERPRET; } else mode = MODE_INTERPRET; } if ( mode == MODE_INFO || mode == MODE_DETAIL ) { xc_interface *xch = xc_interface_open(0, 0, 0); if ( !xch ) err(1, "xc_interface_open"); if ( xc_get_cpu_featureset(xch, 0, &nr_features, NULL) ) err(1, "xc_get_featureset(, NULL)"); dump_info(xch, mode == MODE_DETAIL); xc_interface_close(xch); } else { uint32_t fs[nr_features + 1]; while ( optind < argc ) { char *ptr = argv[optind++]; unsigned int i = 0; int offset; memset(fs, 0, sizeof(fs)); while ( sscanf(ptr, "%x%n", &fs[i], &offset) == 1 ) { i++; ptr += offset; if ( i == nr_features ) break; if ( *ptr == ':' ) { ptr++; continue; } break; } decode_featureset(fs, i, "Raw", true); } } return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/misc/xenperf.c0000664000175000017500000001237513256712137014373 0ustar smbsmb/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*- **************************************************************************** * (C) 2004 - Rolf Neugebauer - Intel Research Cambridge **************************************************************************** * * File: xenperf.c * Author: Rolf Neugebauer (rolf.neugebauer@intel.com) * Date: Nov 2004 * * Description: */ #include #include #include #include #include #include #define X(name) [__HYPERVISOR_##name] = #name const char *hypercall_name_table[64] = { X(set_trap_table), X(mmu_update), X(set_gdt), X(stack_switch), X(set_callbacks), X(fpu_taskswitch), X(sched_op_compat), X(platform_op), X(set_debugreg), X(get_debugreg), X(update_descriptor), X(memory_op), X(multicall), X(update_va_mapping), X(set_timer_op), X(event_channel_op_compat), X(xen_version), X(console_io), X(physdev_op_compat), X(grant_table_op), X(vm_assist), X(update_va_mapping_otherdomain), X(iret), X(vcpu_op), X(set_segment_base), X(mmuext_op), X(xsm_op), X(nmi_op), X(sched_op), X(callback_op), X(xenoprof_op), X(event_channel_op), X(physdev_op), X(hvm_op), X(sysctl), X(domctl), X(kexec_op), X(arch_0), X(arch_1), X(arch_2), X(arch_3), X(arch_4), X(arch_5), X(arch_6), X(arch_7), }; #undef X int main(int argc, char *argv[]) { int i, j; xc_interface *xc_handle; DECLARE_HYPERCALL_BUFFER(xc_perfc_desc_t, pcd); DECLARE_HYPERCALL_BUFFER(xc_perfc_val_t, pcv); xc_perfc_val_t *val; int num_desc, num_val; unsigned int sum, reset = 0, full = 0, pretty = 0; char hypercall_name[36]; if ( argc > 1 ) { char *p = argv[1]; if ( p[0] == '-' ) { switch ( p[1] ) { case 'f': full = 1; break; case 'p': full = 1; pretty = 1; break; case 'r': reset = 1; break; default: goto error; } } else { error: printf("%s: [-r]\n", argv[0]); printf("no args: print digested counters\n"); printf(" -f : print full arrays/histograms\n"); printf(" -p : print full arrays/histograms in pretty format\n"); printf(" -r : reset counters\n"); return 0; } } if ( (xc_handle = xc_interface_open(0,0,0)) == 0 ) { fprintf(stderr, "Error opening xc interface: %d (%s)\n", errno, strerror(errno)); return 1; } if ( reset ) { if ( xc_perfc_reset(xc_handle) != 0 ) { fprintf(stderr, "Error reseting performance counters: %d (%s)\n", errno, strerror(errno)); return 1; } return 0; } if ( xc_perfc_query_number(xc_handle, &num_desc, &num_val) != 0 ) { fprintf(stderr, "Error getting number of perf counters: %d (%s)\n", errno, strerror(errno)); return 1; } pcd = xc_hypercall_buffer_alloc(xc_handle, pcd, sizeof(*pcd) * num_desc); pcv = xc_hypercall_buffer_alloc(xc_handle, pcv, sizeof(*pcv) * num_val); if ( pcd == NULL || pcv == NULL) { fprintf(stderr, "Could not allocate buffers: %d (%s)\n", errno, strerror(errno)); exit(-1); } if ( xc_perfc_query(xc_handle, HYPERCALL_BUFFER(pcd), HYPERCALL_BUFFER(pcv)) != 0 ) { fprintf(stderr, "Error getting perf counter: %d (%s)\n", errno, strerror(errno)); return 1; } val = pcv; for ( i = 0; i < num_desc; i++ ) { printf ("%-35s ", pcd[i].name); sum = 0; for ( j = 0; j < pcd[i].nr_vals; j++ ) sum += val[j]; printf ("T=%10u ", (unsigned int)sum); if ( full || (pcd[i].nr_vals <= 4) ) { if ( pretty && (strcmp(pcd[i].name, "hypercalls") == 0) ) { printf("\n"); for( j = 0; j < pcd[i].nr_vals; j++ ) { if ( val[j] == 0 ) continue; if ( (j < 64) && hypercall_name_table[j] ) strncpy(hypercall_name, hypercall_name_table[j], sizeof(hypercall_name)); else snprintf(hypercall_name, sizeof(hypercall_name), "[%d]", j); hypercall_name[sizeof(hypercall_name)-1]='\0'; printf("%-35s ", hypercall_name); printf("%12u\n", (unsigned int)val[j]); } } else { for ( j = 0; j < pcd[i].nr_vals; j++ ) printf(" %10u", (unsigned int)val[j]); printf("\n"); } } else { printf("\n"); } val += pcd[i].nr_vals; } xc_hypercall_buffer_free(xc_handle, pcd); xc_hypercall_buffer_free(xc_handle, pcv); return 0; } xen-4.9.2/tools/misc/xen-ringwatch0000664000175000017500000003654113256712137015262 0ustar smbsmb#!/usr/bin/python # # Copyright (C) 2011 Citrix Systems, Inc. # # This library is free software; you can redistribute it and/or modify # it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; If not, see . # """Overview: - Gather Xen I/O ring states (from %s/*/ring) - Update ring states every -T seconds. - Determine if rings are idle or make progress. - Determine if idle rings dropped notifications (%s). - Instruct stuck backends to reissue notifications. """ import os import glob class Pattern(object): """A regex pattern. Compiled on demand, then persisted.""" def __init__(self, regex): self.regex = regex self.__pattern = None def get(self): import re if not self.__pattern: self.__pattern = re.compile(self.regex) return self.__pattern def search(self, s): return self.get().search(s) class XenBackend(object): """A Xen I/O backend.""" SYSFS_BASEDIR = "/sys/devices/xen-backend" def __init__(self, rd, devid): self.rd = int(rd) self.devid = int(devid) def __repr__(self): return "%s(%d, %d)" % (type(self).__name__, self.rd, self.devid) def name(self): raise NotImplementedError def path(self): return "%s/%s" % (self.SYSFS_BASEDIR, self.name()) _name_pattern = None @classmethod def from_name(cls, name): match = cls._name_pattern.search(name) if not match: raise Exception, "Malformed %s name: %s" % \ (type(self).__name__, name) rd = match.group(1) devid = match.group(2) return cls(rd, devid) _name_glob = None @classmethod def find(cls): paths = glob.glob("%s/%s" % (cls.SYSFS_BASEDIR, cls._name_glob)) for path in paths: name = os.path.basename(path) yield cls.from_name(name) def find_rings(self): for ring in self.Ring.find(self): yield ring class Ring(object): def __init__(self, backend, name): self.backend = backend self.name = name __size = None def key(self): return "%s/%s" % (self.backend.name(), self.name) def __str__(self): return "%s(%s)" % (type(self).__name__, self.key()) @classmethod def from_name(cls, backend, name): return cls(backend, name) _name_glob = None @classmethod def find(cls, backend): paths = glob.glob("%s/%s" % (backend.path(), cls._name_glob)) for path in paths: name = os.path.basename(path) yield cls.from_name(backend, name) def path(self): return "%s/%s" % (self.backend.path(), self.name) def read(self): state = RingState.from_sysfs(self.path()) return state def write(self, cmd): f = file(self.path(), 'w') try: f.write(cmd.rstrip()) finally: f.close() def kick(self): self.write("kick") def poll(self): self.write("poll") __ring = None TYPES = {} XEN_BACKEND_NAME = None @classmethod def register(cls): XenBackend.TYPES[cls.XEN_BACKEND_NAME] = cls class VBD(XenBackend): """Xen blkif backends.""" XEN_BACKEND_NAME = 'vbd' _name_pattern = Pattern("vbd-(\d+)-(\d+)") _name_glob = "vbd-*-*" def name(self): return "vbd-%d-%d" % (self.rd, self.devid) class Ring(XenBackend.Ring): _name_glob = "io_ring" VBD.register() class VIF(XenBackend): """Xen netif backends.""" XEN_BACKEND_NAME = 'vif' _name_pattern = Pattern("vif-(\d+)-(\d+)") _name_glob = "vif-*-*" def name(self): return "vif-%d-%d" % (self.rd, self.devid) class Ring(XenBackend.Ring): _name_glob = "{rx,tx}_ring" #VIF.register() class RingState(object): """Overall backend ring state. Comprising req and rsp queue indexes, and analysis.""" def __init__(self, size, req, rsp): self.size = int(size) self.req = req self.rsp = rsp _size_pattern = Pattern("nr_ents (\d+)") @classmethod def from_sysfs(cls, path): f = file(path, "r") try: s = f.read() finally: f.close() try: (_nr_ents, _req, _rsp, _) = s.split("\n") match = cls._size_pattern.search(_nr_ents) nr_ents = int(match.group(1)) except Exception, e: raise Exception, "Malformed %s input: %s (%s)" % \ (cls.__name__, repr(s), str(e)) req = cls.Req.from_sysfs(_req, size=nr_ents) rsp = cls.Rsp.from_sysfs(_rsp, size=nr_ents) return cls(nr_ents, req, rsp) class Queue(dict): def __init__(self, size): self.size = int(size) prod = None @classmethod def from_sysfs(cls, line, **d): match = cls._pattern.search(line) if not match: raise Exception, "Malformed %s input: %s" % \ (cls.__name__, repr(s)) i = iter(match.groups()) for k in i: d[k] = i.next() return cls(**d) def is_consumed(self): return self.prod == self._cons() class Req(Queue): _pattern = Pattern("req (prod) (\d+) (cons) (\d+) (event) (\d+)") def __init__(self, prod, cons, event, **d): RingState.Queue.__init__(self, **d) self.prod = int(prod) self.cons = int(cons) self.event = int(event) def __repr__(self): return "%s(prod=%d, cons=%d, event=%d)" % \ (type(self).__name__, self.prod, self.cons, self.event) def _cons(self): return self.cons def __eq__(self, other): return \ self.prod == other.prod and \ self.cons == other.cons and \ self.event == other.event class Rsp(Queue): _pattern = Pattern("rsp (prod) (\d+) (pvt) (\d+) (event) (\d+)") def __init__(self, prod, pvt, event, **d): RingState.Queue.__init__(self, **d) self.prod = int(prod) self.pvt = int(pvt) self.event = int(event) def __repr__(self): return "%s(prod=%d, pvt=%d, event=%d)" % \ (type(self).__name__, self.prod, self.pvt, self.event) def _cons(self): return self.event - 1 def __eq__(self, other): return \ self.prod == other.prod and \ self.pvt == other.pvt and \ self.event == other.event def is_consumed(self): return \ self.rsp.is_consumed() and \ self.req.is_consumed() def is_pending(self): return self.rsp.prod != self.req.prod def kick(self, ring): action = False if not self.req.is_consumed(): action |= True ring.poll() if not self.rsp.is_consumed(): action |= True ring.kick() return action def __eq__(self, other): return \ self.size == other.size and \ self.req == other.req and \ self.rsp == other.rsp def __repr__(self): return "%s(size=%d, %s, %s)" % \ (type(self).__name__, self.size, self.req, self.rsp) def display(self): complete = { True: "complete", False: "pending" } io = complete[not self.is_pending()] req = complete[self.req.is_consumed()] rsp = complete[self.rsp.is_consumed()] return "%s: io: %s, req: %s, rsp: %s" % (self, io, req, rsp) class RingWatch(object): """State machine watching I/O individual ring state""" _NEW = "_NEW" BUSY = "BUSY" IDLE = "IDLE" STCK = "STCK" COMMENTS = { BUSY: "Message traffic observed (OK)", IDLE: "No messages observed (Ring OK, I/O depends)", STCK: "No pending req/rsp consumer progress observed (BUG)" } def __init__(self, ring, state): self.ring = ring self.state = state self.status = RingWatch._NEW @classmethod def new(cls, ring): state = ring.read() return cls(ring, state) def __str__(self): return "%s(%s)[%s]" % \ (type(self).__name__, self.ring.key(), self.status) def is_stuck(self): return self.status == self.STCK def is_idle(self): return self.status == self.IDLE def kick(self): if self.is_stuck(): return self.state.kick(self.ring) def update(self): prev = self.state curr = self.ring.read() if curr == prev: if not curr.is_consumed(): self.status = self.STCK else: self.status = self.IDLE else: self.status = self.BUSY self.state = curr def display(self): return "%s: %s" % (self, self.state.display()) class WatchList(object): """Managed collection of I/O rings under surveillance.""" def __init__(self, gen): self.gen = gen self.list = {} def update(self): # NB. clear the watch list, then rebuild it. new entries get # added, existing ones updates, those gone discarded. prev = self.list self.list = {} for ring in self.gen(): key = ring.key() entry = prev.get(key) try: if not entry: entry = RingWatch.new(ring) else: entry.update() except IOError, e: pass # NB. racing unplug, any ring.read() may raise. # nothing left to memorize then. else: self.list[key] = entry def __iter__(self): return self.list.itervalues() def pending(self): for entry in self: if entry.is_idle() and entry.state.is_pending(): yield entry def stuck(self): for entry in self: if entry.is_stuck(): yield entry def kick(self): for entry in self.stuck(): try: entry.kick() except IOError: # NB. racing unplug, any ring.write() may raise. pass if __name__ == '__main__': from sys import argv, stdout, stderr, exit from getopt import gnu_getopt, GetoptError from pprint import pprint DEFAULT_PERIOD = 1 # secs verbose = 0 period = DEFAULT_PERIOD backends = XenBackend.TYPES.values() kick = False iowatch = False OPTIONS = ((('h', 'help'), "Print this help screen."), (('v', 'verbose'), "Increase output verbosity level (use n-times)."), (('I', 'io'), "Watch out for stuck I/O (not messaging), too. (%s)" % \ (iowatch)), (('t', 'types'), "Comma separated list of backend types to watch. (%s)" % \ ",".join(map(lambda t: t.XEN_BACKEND_NAME, backends))), (('T', 'period'), "Watch update period. (%d) [secs]" % \ (period)), (('k', 'kick'), "Kick broken guests out of cardiac arrest. (%s)" % \ (kick)) ) COMMANDS = {"check": "Single iteration quick test (takes -T seconds)."} def usage(stream): prog = os.path.basename(argv[0]) print >>stream print >>stream, "Usage:" print >>stream, "\t%s [options] {%s}" % (prog, "|".join(COMMANDS)) print >>stream print >>stream, "Commands:" for (name, desc) in COMMANDS.iteritems(): print >>stream, "\t%s: \t%s" % (name, desc) print >>stream print >>stream, "Options:" for ((short, _long), desc) in OPTIONS: print >>stream, "\t-%s, --%s: \t%s" % (short, _long, desc) print >>stream def fail(msg = None): if msg: print >>stderr, "Error: %s" % msg usage(stderr) exit(1) def help(): usage(stdout) print __doc__ % (XenBackend.SYSFS_BASEDIR, RingWatch.STCK) print "Backend Types:" for k, v in XenBackend.TYPES.iteritems(): print "\t%s: \t%s (%s)" % (k, v.__doc__, v._name_glob) print print "Ring States:" for k, v in RingWatch.COMMENTS.iteritems(): print "\t%s: \t%s" % (k, v) print try: opts, args = gnu_getopt(argv[1:], "hIkt:vT:", ["help", "io", "kick", "type=", "verbose", "period="]) except GetoptError, e: fail(str(e)) for (o, arg) in opts: try: if o in ('-h', '--help'): help() exit(0) elif o in ['-v', '--verbose']: verbose += 1 elif o in ['-I', '--io']: iowatch = True elif o in ('-T', '--period'): period = int(arg) elif o in ('-t', '--type'): backends = ",".split(arg) backends = map(lambda t: XenBackend.TYPES[t], backends) elif o in ('-k', '--kick'): kick = True else: raise "BUG: option %s unhandled." % o except ValueError: fail("%s: invalid argument '%s'." % (o, arg)) try: cmd = args[0] except IndexError: fail("Missing command.") def ring_select(): for _type in backends: for backend in _type.find(): for ring in backend.find_rings(): yield ring def show(entries): for watch in entries: print watch.display() def pause(): import time time.sleep(period) watches = WatchList(ring_select) if cmd == "check": # init watches.update() if verbose >= 2: show(watches) # watch for one round pause() watches.update() # show result crit = list(watches.stuck()) stuck = bool(crit) if (iowatch): crit.extend(watches.pending()) if verbose >= 1: show(watches) elif crit: show(crit) if stuck and kick: # deal with it watches.kick() else: fail("Invalid command.") xen-4.9.2/tools/misc/xen-detect.c0000664000175000017500000001207613256712137014762 0ustar smbsmb/****************************************************************************** * xen_detect.c * * Simple GNU C / POSIX application to detect execution on Xen VMM platform. * * Copyright (c) 2007, XenSource Inc. * * 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 #include #include #include #include #include #include #include static void cpuid(uint32_t idx, uint32_t *regs, int pv_context) { #ifdef __i386__ /* Use the stack to avoid reg constraint failures with some gcc flags */ asm volatile ( "push %%eax; push %%ebx; push %%ecx; push %%edx\n\t" "test %1,%1 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t" "mov %%eax,(%2); mov %%ebx,4(%2)\n\t" "mov %%ecx,8(%2); mov %%edx,12(%2)\n\t" "pop %%edx; pop %%ecx; pop %%ebx; pop %%eax\n\t" : : "a" (idx), "c" (pv_context), "S" (regs) : "memory" ); #else asm volatile ( "test %5,%5 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) : "0" (idx), "1" (pv_context), "2" (0) ); #endif } static int check_for_xen(int pv_context) { uint32_t regs[4]; char signature[13]; uint32_t base; for ( base = 0x40000000; base < 0x40010000; base += 0x100 ) { cpuid(base, regs, pv_context); *(uint32_t *)(signature + 0) = regs[1]; *(uint32_t *)(signature + 4) = regs[2]; *(uint32_t *)(signature + 8) = regs[3]; signature[12] = '\0'; if ( !strcmp("XenVMMXenVMM", signature) && (regs[0] >= (base + 2)) ) goto found; } return 0; found: cpuid(base + 1, regs, pv_context); return regs[0]; } static jmp_buf sigill_jmp; void sigill_handler(int sig) { longjmp(sigill_jmp, 1); } static void usage(void) { printf("Usage: xen_detect [options]\n"); printf("Options:\n"); printf(" -h, --help Display this information\n"); printf(" -q, --quiet Quiesce normal informational output\n"); printf(" -P, --pv Exit status 1 if not running as PV guest\n"); printf(" -H, --hvm Exit status 1 if not running as HVM guest.\n"); printf(" -N, --none Exit status 1 if running on Xen (PV or HVM)\n"); } int main(int argc, char **argv) { enum { XEN_PV = 1, XEN_HVM = 2, XEN_NONE = 3 } detected = 0, expected = 0; uint32_t version = 0; int ch, quiet = 0; const static char sopts[] = "hqPHN"; const static struct option lopts[] = { { "help", 0, NULL, 'h' }, { "quiet", 0, NULL, 'q' }, { "pv", 0, NULL, 'P' }, { "hvm", 0, NULL, 'H' }, { "none", 0, NULL, 'N' }, { 0, 0, 0, 0} }; while ( (ch = getopt_long(argc, argv, sopts, lopts, NULL)) != -1 ) { switch ( ch ) { case 'q': quiet = 1; break; case 'P': expected = XEN_PV; break; case 'H': expected = XEN_HVM; break; case 'N': expected = XEN_NONE; break; default: usage(); exit(1); } } /* Check for execution in HVM context. */ detected = XEN_HVM; if ( (version = check_for_xen(0)) != 0 ) goto out; /* * Set up a signal handler to test the paravirtualised CPUID instruction. * If executed outside Xen PV context, the extended opcode will fault, we * will longjmp via the signal handler, and print "Not running on Xen". */ detected = XEN_PV; if ( !setjmp(sigill_jmp) && (signal(SIGILL, sigill_handler) != SIG_ERR) && ((version = check_for_xen(1)) != 0) ) goto out; detected = XEN_NONE; out: if ( quiet ) /* nothing */; else if ( detected == XEN_NONE ) printf("Not running on Xen.\n"); else printf("Running in %s context on Xen v%d.%d.\n", (detected == XEN_PV) ? "PV" : "HVM", (uint16_t)(version >> 16), (uint16_t)version); return expected && (expected != detected); } xen-4.9.2/tools/misc/xensymoops0000775000175000017500000000627613256712137014735 0ustar smbsmb#!/usr/bin/env python # An oops analyser for Xen # Usage: xensymoops path-to-xen.s < oops-message # There's probably some more features that could go in here but this # is sufficient to analyse most errors in my code ;-) # by Mark Williamson (C) 2004 Intel Research Cambridge import re, sys def read_oops(): """Process an oops message on stdin and return (eip_addr, stack_addrs) eip_addr is the location of EIP at the point of the crash. stack_addrs is a dictionary mapping potential code addresses in the stack to their order in the stack trace. """ stackaddr_ptn = "\[([a-z,0-9]*)\]" stackaddr_re = re.compile(stackaddr_ptn) eip_ptn = ".*EIP:.*<([a-z,0-9]*)>.*" eip_re = re.compile(eip_ptn) matches = 0 stack_addresses = {} eip_addr = "Not known" while True: line = sys.stdin.readline() if not line: break m = eip_re.match(line) if m: eip_addr = m.group(1) m = stackaddr_re.findall(line) for i in m: stack_addresses[i] = matches matches += 1 return (eip_addr, stack_addresses) def usage(): print >> sys.stderr, """Usage: %s path-to-asm < oops-msg The oops message should be fed to the standard input. The command-line argument specifies the path to the Xen assembly dump produced by \"make debug\". The location of EIP and the backtrace will be output to standard output. """ % sys.argv[0] sys.exit() ##### main if len(sys.argv) != 2: usage() # get address of EIP and the potential code addresses from the stack (eip_addr, stk_addrs) = read_oops() # open Xen disassembly asm_file = open(sys.argv[1]) # regexp to match addresses of code lines in the objdump addr_ptn = "([a-z,0-9]*):" addr_re = re.compile(addr_ptn) # regexp to match the start of functions in the objdump func_ptn = "(.*<[\S]*>):" func_re = re.compile(func_ptn) func = "" # holds the name of the current function being scanned eip_func = "" # name of the function EIP was in # list of (position in original backtrace, code address, function) tuples # describing all the potential code addresses we identified in the backtrace # whose addresses we also located in the objdump output backtrace = [] while True: line = asm_file.readline() if not line: break # if we've read the start of the function, record the name and address fm = func_re.match(line) if fm: func = fm.group(1) continue # try match the address at the start of the line m = addr_re.match(line) if not m: continue # we're on a code line... address = m.group(1) # if this address was seen as a potential code address in the backtrace then # record it in the backtrace list if stk_addrs.has_key(address): backtrace.append((stk_addrs[address], address, func)) # if this was the address that EIP... if address == eip_addr: eip_func = func print "EIP %s in function %s" % (eip_addr, eip_func) print "Backtrace:" # sorting will order primarily by the first element of each tuple, # i.e. the order in the original oops backtrace.sort() for (i, a, f) in backtrace: print "%s in function %s" % ( a, f ) xen-4.9.2/tools/xenbackendd/0000775000175000017500000000000013256712137014063 5ustar smbsmbxen-4.9.2/tools/xenbackendd/xenbackendd.c0000664000175000017500000001502313256712137016476 0ustar smbsmb/* $NetBSD: xenbackendd.c,v 1.1.1.1 2008/08/07 20:26:57 cegger Exp $ */ /* * Copyright (C) 2006 Manuel Bouyer * Copyright (C) 2009 Christoph Egger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "_paths.h" #define DEVTYPE_UNKNOWN 0 #define DEVTYPE_VIF 1 #define DEVTYPE_VBD 2 #define DISABLE_EXEC "libxl/disable_udev" #define DOMAIN_PATH "/local/domain/0" #ifndef XEN_SCRIPT_DIR #error XEN_SCRIPT_DIR not defined #endif #ifndef VBD_SCRIPT #define VBD_SCRIPT XEN_SCRIPT_DIR"/block" #endif #ifndef LOG_FILE #define LOG_FILE XEN_LOG_DIR "xenbackendd.log" #endif #ifndef PID_FILE #define PID_FILE XEN_RUN_DIR "xenbackendd.pid" #endif struct xs_handle *xs; int fflag = 0; int dflag = 0; const char *vbd_script = NULL; const char *log_file = NULL; const char *pidfile = NULL; static void dolog(int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); fflush(stderr); va_start(ap, fmt); vsyslog(pri, fmt, ap); va_end(ap); } static void dodebug(const char *fmt, ...) { va_list ap; if (dflag == 0) return; va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n"); fflush(stdout); } static void doexec(const char *cmd, const char *arg1, const char *arg2) { dodebug("exec %s %s %s", cmd, arg1, arg2); switch(vfork()) { case -1: dolog(LOG_ERR, "can't vfork: %s", strerror(errno)); break; case 0: execl(cmd, cmd, arg1, arg2, NULL); dolog(LOG_ERR, "can't exec %s: %s", cmd, strerror(errno)); exit(EXIT_FAILURE); /* NOTREACHED */ break; default: wait(NULL); break; } } static void usage(void) { fprintf(stderr, "usage: %s [-d] [-f] [-l log_file] [-p pif_file] [-s vbd_script]\n", getprogname()); exit(EXIT_FAILURE); } static int xen_setup(void) { xs = xs_daemon_open(); if (xs == NULL) { dolog(LOG_ERR, "Failed to contact xenstore (%s). Is it running?", strerror(errno)); goto out; } if (!xs_watch(xs, DOMAIN_PATH, "backend")) { dolog(LOG_ERR, "xenstore watch on backend fails."); goto out; } return 0; out: if (xs) { xs_daemon_close(xs); xs = NULL; } return -1; } int main(int argc, char * const argv[]) { char **vec; unsigned int num; char *s; int state; char *sstate, *sdisable; char *p; char buf[80]; int type; int ch; int debug_fd; FILE *pidfile_f; while ((ch = getopt(argc, argv, "dfl:p:s:")) != -1) { switch (ch) { case 'd': dflag = 1; break; case 'f': fflag = 1; break; case 'l': log_file = optarg; break; case 'p': pidfile = optarg; case 's': vbd_script = optarg; break; default: usage(); } } if (vbd_script == NULL) vbd_script = VBD_SCRIPT; if (pidfile == NULL) pidfile = PID_FILE; if (log_file == NULL) log_file = LOG_FILE; openlog("xenbackendd", LOG_PID | LOG_NDELAY, LOG_DAEMON); if (fflag == 0) { /* open log file */ debug_fd = open(log_file, O_RDWR | O_CREAT | O_TRUNC, 0644); if (debug_fd == -1) { dolog(LOG_ERR, "can't open %s: %s", log_file, strerror(errno)); exit(EXIT_FAILURE); } } if (fflag == 0) { /* daemonize */ pidfile_f = fopen(pidfile, "w"); if (pidfile_f == NULL) { dolog(LOG_ERR, "can't open %s: %s", pidfile, strerror(errno)); exit(EXIT_FAILURE); } if (daemon(0, 0) < 0) { dolog(LOG_ERR, "can't daemonize: %s", strerror(errno)); exit(EXIT_FAILURE); } fprintf(pidfile_f, "%d\n", (int)getpid()); fclose(pidfile_f); /* redirect stderr to log file */ if (dup2(debug_fd, STDERR_FILENO) < 0) { dolog(LOG_ERR, "can't redirect stderr to %s: %s\n", log_file, strerror(errno)); exit(EXIT_FAILURE); } /* also redirect stdout if we're in debug mode */ if (dflag) { if (dup2(debug_fd, STDOUT_FILENO) < 0) { dolog(LOG_ERR, "can't redirect stdout to %s: %s\n", log_file, strerror(errno)); exit(EXIT_FAILURE); } } close(debug_fd); debug_fd = -1; } if (xen_setup() < 0) exit(EXIT_FAILURE); for (;;) { vec = xs_read_watch(xs, &num); dodebug("read from xen watch: %s", *vec); if (!vec) { dolog(LOG_ERR, "xs_read_watch: NULL\n"); continue; } sdisable = xs_read(xs, XBT_NULL, DISABLE_EXEC, 0); if (sdisable) goto next1; if (strlen(vec[XS_WATCH_PATH]) < sizeof("state")) goto next1; /* find last component of path, check if it's "state" */ p = &vec[XS_WATCH_PATH][ strlen(vec[XS_WATCH_PATH]) - sizeof("state")]; if (p[0] != '/') goto next1; p[0] = '\0'; p++; if (strcmp(p, "state") != 0) goto next1; snprintf(buf, sizeof(buf), "%s/state", vec[XS_WATCH_PATH]); sstate = xs_read(xs, XBT_NULL, buf, 0); if (sstate == NULL) { dolog(LOG_ERR, "Failed to read %s (%s)", buf, strerror(errno)); goto next1; } state = atoi(sstate); snprintf(buf, sizeof(buf), "%s/hotplug-status", vec[XS_WATCH_PATH]); s = xs_read(xs, XBT_NULL, buf, 0); if (s != NULL && state != 6 /* XenbusStateClosed */) goto next2; type = DEVTYPE_UNKNOWN; if (strncmp(vec[XS_WATCH_PATH], DOMAIN_PATH "/backend/vif", strlen(DOMAIN_PATH "/backend/vif")) == 0) type = DEVTYPE_VIF; if (strncmp(vec[XS_WATCH_PATH], DOMAIN_PATH "/backend/vbd", strlen(DOMAIN_PATH "/backend/vbd")) == 0) type = DEVTYPE_VBD; switch(type) { case DEVTYPE_VIF: free(s); snprintf(buf, sizeof(buf), "%s/script", vec[XS_WATCH_PATH]); s = xs_read(xs, XBT_NULL, buf, 0); if (s == NULL) { dolog(LOG_ERR, "Failed to read %s (%s)", buf, strerror(errno)); goto next2; } doexec(s, vec[XS_WATCH_PATH], sstate); break; case DEVTYPE_VBD: doexec(vbd_script, vec[XS_WATCH_PATH], sstate); break; default: break; } next2: free(s); free(sstate); next1: free(sdisable); free(vec); } return 0; } xen-4.9.2/tools/xenbackendd/Makefile0000664000175000017500000000225113256712137015523 0ustar smbsmb# Copyright (c) 2009 Advanced Micro Devices, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; under version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. XEN_ROOT=$(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk CFLAGS += -Werror CFLAGS += $(CFLAGS_libxenstore) CPPFLAGS += -DXEN_SCRIPT_DIR="\"$(XEN_SCRIPT_DIR)\"" LDLIBS += $(LDLIBS_libxenstore) .PHONY: all all: build .PHONY: build build: xenbackendd .PHONY: install install: build $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_PROG) xenbackendd $(DESTDIR)$(sbindir) .PHONY: clean clean: $(RM) *.a *.so *.o $(DEPS) xenbackendd _paths.h .PHONY: distclean distclean: clean xenbackendd.o: _paths.h xenbackendd: xenbackendd.o $(CC) $(LDFLAGS) $< -o $@ $(LDLIBS) $(APPEND_LDFLAGS) genpath-target = $(call buildmakevars2header,_paths.h) $(eval $(genpath-target)) -include $(DEPS) xen-4.9.2/tools/libacpi/0000775000175000017500000000000013256712137013220 5ustar smbsmbxen-4.9.2/tools/libacpi/ssdt_laptop_slate.asl0000664000175000017500000000207613256712137017452 0ustar smbsmb/* * ssdt_conv.asl * * Copyright (c) 2017 Citrix Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * Windows laptop/slate mode device * * See https://msdn.microsoft.com/en-us/windows/hardware/commercialize/design/device-experiences/continuum#method-2----use-the-injection-interface */ DefinitionBlock ("SSDT_LAPTOP_SLATE.aml", "SSDT", 2, "Xen", "HVM", 0) { Device (CONV) { Method (_HID, 0x0, NotSerialized) { Return("ID9001") } Name (_CID, "PNP0C60") } } /* * Local variables: * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libacpi/COPYING0000664000175000017500000006062613256712137014265 0ustar smbsmbThis library is licensed under LGPL v2.1 to allow its usage in LGPL-2.1 libraries such as libxl. Note that the only valid version of the LGPL as far as the files in this directory (and its subdirectories) are concerned is _this_ particular version of the license (i.e., *only* v2.1, not v2.2 or v3.x, unless explicitly otherwise stated. Where clause 3 is invoked in order to relicense under the GPL then this shall be considered to be GPL v2 only for files which have specified LGPL v2.1 only. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS xen-4.9.2/tools/libacpi/static_tables.c0000664000175000017500000001242013256712137016204 0ustar smbsmb/* * Copyright (c) 2004, Intel Corporation. * Copyright (c) 2006, Keir Fraser, XenSource Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "acpi2_0.h" #include "../config.h" /* * Firmware ACPI Control Structure (FACS). */ struct acpi_20_facs Facs = { .signature = ACPI_2_0_FACS_SIGNATURE, .length = sizeof(struct acpi_20_facs), .version = ACPI_2_0_FACS_VERSION }; /* * Fixed ACPI Description Table (FADT). */ /* * These values must match register definitions in struct hvm_hw_acpi * (in xen/include/public/arch-x86/hvm/save.h). */ #define ACPI_PM1A_EVT_BLK_BIT_WIDTH 0x20 #define ACPI_PM1A_EVT_BLK_BIT_OFFSET 0x00 #define ACPI_PM1A_CNT_BLK_BIT_WIDTH 0x10 #define ACPI_PM1A_CNT_BLK_BIT_OFFSET 0x00 #define ACPI_PM_TMR_BLK_BIT_WIDTH 0x20 #define ACPI_PM_TMR_BLK_BIT_OFFSET 0x00 struct acpi_fadt Fadt = { .header = { .signature = ACPI_FADT_SIGNATURE, .oem_id = ACPI_OEM_ID, .oem_table_id = ACPI_OEM_TABLE_ID, .oem_revision = ACPI_OEM_REVISION, .creator_id = ACPI_CREATOR_ID, .creator_revision = ACPI_CREATOR_REVISION }, .sci_int = 9, .pm1a_evt_blk = ACPI_PM1A_EVT_BLK_ADDRESS_V1, .pm1a_cnt_blk = ACPI_PM1A_CNT_BLK_ADDRESS_V1, .pm_tmr_blk = ACPI_PM_TMR_BLK_ADDRESS_V1, .gpe0_blk = ACPI_GPE0_BLK_ADDRESS_V1, .pm1_evt_len = ACPI_PM1A_EVT_BLK_BIT_WIDTH / 8, .pm1_cnt_len = ACPI_PM1A_CNT_BLK_BIT_WIDTH / 8, .pm_tmr_len = ACPI_PM_TMR_BLK_BIT_WIDTH / 8, .gpe0_blk_len = ACPI_GPE0_BLK_LEN_V1, .p_lvl2_lat = 0x0fff, /* >100, means we do not support C2 state */ .p_lvl3_lat = 0x0fff, /* >1000, means we do not support C3 state */ .flags = (ACPI_PROC_C1 | ACPI_WBINVD | ACPI_FIX_RTC | ACPI_TMR_VAL_EXT | ACPI_USE_PLATFORM_CLOCK), .reset_reg = { .address_space_id = ACPI_SYSTEM_IO, .register_bit_width = 8, /* *must* be 8 */ .register_bit_offset = 0, /* *must* be 0 */ .address = 0xcf9 }, .reset_value = 6, .x_pm1a_evt_blk = { .address_space_id = ACPI_SYSTEM_IO, .register_bit_width = ACPI_PM1A_EVT_BLK_BIT_WIDTH, .register_bit_offset = ACPI_PM1A_EVT_BLK_BIT_OFFSET, .address = ACPI_PM1A_EVT_BLK_ADDRESS_V1, }, .x_pm1a_cnt_blk = { .address_space_id = ACPI_SYSTEM_IO, .register_bit_width = ACPI_PM1A_CNT_BLK_BIT_WIDTH, .register_bit_offset = ACPI_PM1A_CNT_BLK_BIT_OFFSET, .address = ACPI_PM1A_CNT_BLK_ADDRESS_V1, }, .x_pm_tmr_blk = { .address_space_id = ACPI_SYSTEM_IO, .register_bit_width = ACPI_PM_TMR_BLK_BIT_WIDTH, .register_bit_offset = ACPI_PM_TMR_BLK_BIT_OFFSET, .address = ACPI_PM_TMR_BLK_ADDRESS_V1, } }; struct acpi_20_rsdt Rsdt = { .header = { .signature = ACPI_2_0_RSDT_SIGNATURE, .length = sizeof(struct acpi_header), .revision = ACPI_2_0_RSDT_REVISION, .oem_id = ACPI_OEM_ID, .oem_table_id = ACPI_OEM_TABLE_ID, .oem_revision = ACPI_OEM_REVISION, .creator_id = ACPI_CREATOR_ID, .creator_revision = ACPI_CREATOR_REVISION } }; struct acpi_20_xsdt Xsdt = { .header = { .signature = ACPI_2_0_XSDT_SIGNATURE, .length = sizeof(struct acpi_header), .revision = ACPI_2_0_XSDT_REVISION, .oem_id = ACPI_OEM_ID, .oem_table_id = ACPI_OEM_TABLE_ID, .oem_revision = ACPI_OEM_REVISION, .creator_id = ACPI_CREATOR_ID, .creator_revision = ACPI_CREATOR_REVISION } }; struct acpi_20_rsdp Rsdp = { .signature = ACPI_2_0_RSDP_SIGNATURE, .oem_id = ACPI_OEM_ID, .revision = ACPI_2_0_RSDP_REVISION, .length = sizeof(struct acpi_20_rsdp) }; #define ACPI_WAET_RTC_NO_ACK (1<<0) /* RTC requires no int acknowledge */ #define ACPI_WAET_TIMER_ONE_READ (1<<1) /* PM timer requires only one read */ /* * The state of the RTC flag getting passed to the guest must be in * sync with the mode selection in the hypervisor RTC emulation code. */ #define ACPI_WAET_FLAGS (ACPI_WAET_RTC_NO_ACK | \ ACPI_WAET_TIMER_ONE_READ) struct acpi_20_waet Waet = { .header = { .signature = ACPI_2_0_WAET_SIGNATURE, .length = sizeof(struct acpi_20_waet), .revision = ACPI_2_0_WAET_REVISION, .oem_id = ACPI_OEM_ID, .oem_table_id = ACPI_OEM_TABLE_ID, .oem_revision = ACPI_OEM_REVISION, .creator_id = ACPI_CREATOR_ID, .creator_revision = ACPI_CREATOR_REVISION }, .flags = ACPI_WAET_FLAGS }; /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libacpi/dsdt.asl0000664000175000017500000003752613256712137014674 0ustar smbsmb/****************************************************************************** * DSDT for Xen with Qemu device model * * Copyright (c) 2004, Intel Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ DefinitionBlock ("DSDT.aml", "DSDT", 2, "Xen", "HVM", 0) { Name (\PMBS, 0x0C00) Name (\PMLN, 0x08) Name (\IOB1, 0x00) Name (\IOL1, 0x00) Name (\APCB, 0xFEC00000) Name (\APCL, 0x00010000) Name (\PUID, 0x00) Scope (\_SB) { /* Fix HCT test for 0x400 pci memory: * - need to report low 640 MB mem as motherboard resource */ Device(MEM0) { Name(_HID, EISAID("PNP0C02")) Name(_CRS, ResourceTemplate() { QWordMemory( ResourceConsumer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x00000000, 0x00000000, 0x0009ffff, 0x00000000, 0x000a0000) }) } Device (PCI0) { Name (_HID, EisaId ("PNP0A03")) Name (_UID, 0x00) Name (_ADR, 0x00) Name (_BBN, 0x00) /* Make cirrues VGA S3 suspend/resume work in Windows XP/2003 */ Device (VGA) { Name (_ADR, 0x00020000) Method (_S1D, 0, NotSerialized) { Return (0x00) } Method (_S2D, 0, NotSerialized) { Return (0x00) } Method (_S3D, 0, NotSerialized) { Return (0x00) } } Method (_CRS, 0, NotSerialized) { Store (ResourceTemplate () { /* bus number is from 0 - 255*/ WordBusNumber( ResourceProducer, MinFixed, MaxFixed, SubDecode, 0x0000, 0x0000, 0x00FF, 0x0000, 0x0100) IO (Decode16, 0x0CF8, 0x0CF8, 0x01, 0x08) WordIO( ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, 0x0000, 0x0000, 0x0CF7, 0x0000, 0x0CF8) WordIO( ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, 0x0000, 0x0D00, 0xFFFF, 0x0000, 0xF300) /* reserve memory for pci devices */ DWordMemory( ResourceProducer, PosDecode, MinFixed, MaxFixed, WriteCombining, ReadWrite, 0x00000000, 0x000A0000, 0x000BFFFF, 0x00000000, 0x00020000) DWordMemory( ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x00000000, 0xF0000000, 0xF4FFFFFF, 0x00000000, 0x05000000, ,, _Y01) QWordMemory ( ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x0000000000000000, 0x0000000FFFFFFFF0, 0x0000000FFFFFFFFF, 0x0000000000000000, 0x0000000000000010, ,, _Y02) }, Local1) CreateDWordField(Local1, \_SB.PCI0._CRS._Y01._MIN, MMIN) CreateDWordField(Local1, \_SB.PCI0._CRS._Y01._MAX, MMAX) CreateDWordField(Local1, \_SB.PCI0._CRS._Y01._LEN, MLEN) Store(\_SB.PMIN, MMIN) Store(\_SB.PLEN, MLEN) Add(MMIN, MLEN, MMAX) Subtract(MMAX, One, MMAX) /* * WinXP / Win2K3 blue-screen for operations on 64-bit values. * Therefore we need to split the 64-bit calculations needed * here, but different iasl versions evaluate name references * to integers differently: * Year (approximate) 2006 2008 2012 * \_SB.PCI0._CRS._Y02 zero valid valid * \_SB.PCI0._CRS._Y02._MIN valid valid huge */ If(LEqual(Zero, \_SB.PCI0._CRS._Y02)) { Subtract(\_SB.PCI0._CRS._Y02._MIN, 14, Local0) } Else { Store(\_SB.PCI0._CRS._Y02, Local0) } CreateDWordField(Local1, Add(Local0, 14), MINL) CreateDWordField(Local1, Add(Local0, 18), MINH) CreateDWordField(Local1, Add(Local0, 22), MAXL) CreateDWordField(Local1, Add(Local0, 26), MAXH) CreateDWordField(Local1, Add(Local0, 38), LENL) CreateDWordField(Local1, Add(Local0, 42), LENH) Store(\_SB.LMIN, MINL) Store(\_SB.HMIN, MINH) Store(\_SB.LLEN, LENL) Store(\_SB.HLEN, LENH) Add(MINL, LENL, MAXL) Add(MINH, LENH, MAXH) If(LLess(MAXL, MINL)) { Add(MAXH, One, MAXH) } If(LOr(MINH, LENL)) { If(LEqual(MAXL, 0)) { Subtract(MAXH, One, MAXH) } Subtract(MAXL, One, MAXL) } Return (Local1) } Device(HPET) { Name(_HID, EISAID("PNP0103")) Name(_UID, 0) Method (_STA, 0, NotSerialized) { If(LEqual(\_SB.HPET, 0)) { Return(0x00) } Else { Return(0x0F) } } Name(_CRS, ResourceTemplate() { DWordMemory( ResourceConsumer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x00000000, 0xFED00000, 0xFED003FF, 0x00000000, 0x00000400 /* 1K memory: FED00000 - FED003FF */ ) }) } Device (ISA) { Name (_ADR, 0x00010000) /* device 1, fn 0 */ OperationRegion(PIRQ, PCI_Config, 0x60, 0x4) Scope(\) { Field (\_SB.PCI0.ISA.PIRQ, ByteAcc, NoLock, Preserve) { PIRA, 8, PIRB, 8, PIRC, 8, PIRD, 8 } } Device (SYSR) { Name (_HID, EisaId ("PNP0C02")) Name (_UID, 0x01) Name (CRS, ResourceTemplate () { /* TODO: list hidden resources */ IO (Decode16, 0x0010, 0x0010, 0x00, 0x10) IO (Decode16, 0x0022, 0x0022, 0x00, 0x0C) IO (Decode16, 0x0030, 0x0030, 0x00, 0x10) IO (Decode16, 0x0044, 0x0044, 0x00, 0x1C) IO (Decode16, 0x0062, 0x0062, 0x00, 0x02) IO (Decode16, 0x0065, 0x0065, 0x00, 0x0B) IO (Decode16, 0x0072, 0x0072, 0x00, 0x0E) IO (Decode16, 0x0080, 0x0080, 0x00, 0x01) IO (Decode16, 0x0084, 0x0084, 0x00, 0x03) IO (Decode16, 0x0088, 0x0088, 0x00, 0x01) IO (Decode16, 0x008C, 0x008C, 0x00, 0x03) IO (Decode16, 0x0090, 0x0090, 0x00, 0x10) IO (Decode16, 0x00A2, 0x00A2, 0x00, 0x1C) IO (Decode16, 0x00E0, 0x00E0, 0x00, 0x10) IO (Decode16, 0x08A0, 0x08A0, 0x00, 0x04) IO (Decode16, 0x0CC0, 0x0CC0, 0x00, 0x10) IO (Decode16, 0x04D0, 0x04D0, 0x00, 0x02) }) Method (_CRS, 0, NotSerialized) { Return (CRS) } } Device (PIC) { Name (_HID, EisaId ("PNP0000")) Name (_CRS, ResourceTemplate () { IO (Decode16, 0x0020, 0x0020, 0x01, 0x02) IO (Decode16, 0x00A0, 0x00A0, 0x01, 0x02) IRQNoFlags () {2} }) } Device (DMA0) { Name (_HID, EisaId ("PNP0200")) Name (_CRS, ResourceTemplate () { DMA (Compatibility, BusMaster, Transfer8) {4} IO (Decode16, 0x0000, 0x0000, 0x00, 0x10) IO (Decode16, 0x0081, 0x0081, 0x00, 0x03) IO (Decode16, 0x0087, 0x0087, 0x00, 0x01) IO (Decode16, 0x0089, 0x0089, 0x00, 0x03) IO (Decode16, 0x008F, 0x008F, 0x00, 0x01) IO (Decode16, 0x00C0, 0x00C0, 0x00, 0x20) IO (Decode16, 0x0480, 0x0480, 0x00, 0x10) }) } Device (TMR) { Name (_HID, EisaId ("PNP0100")) Name (_CRS, ResourceTemplate () { IO (Decode16, 0x0040, 0x0040, 0x00, 0x04) IRQNoFlags () {0} }) } Device (RTC) { Name (_HID, EisaId ("PNP0B00")) Name (_CRS, ResourceTemplate () { IO (Decode16, 0x0070, 0x0070, 0x00, 0x02) IRQNoFlags () {8} }) } Device (SPKR) { Name (_HID, EisaId ("PNP0800")) Name (_CRS, ResourceTemplate () { IO (Decode16, 0x0061, 0x0061, 0x00, 0x01) }) } Device (PS2M) { Name (_HID, EisaId ("PNP0F13")) Name (_CID, 0x130FD041) Method (_STA, 0, NotSerialized) { Return (0x0F) } Name (_CRS, ResourceTemplate () { IRQNoFlags () {12} }) } Device (PS2K) { Name (_HID, EisaId ("PNP0303")) Name (_CID, 0x0B03D041) Method (_STA, 0, NotSerialized) { Return (0x0F) } Name (_CRS, ResourceTemplate () { IO (Decode16, 0x0060, 0x0060, 0x00, 0x01) IO (Decode16, 0x0064, 0x0064, 0x00, 0x01) IRQNoFlags () {1} }) } Device (FDC0) { Name (_HID, EisaId ("PNP0700")) Method (_STA, 0, NotSerialized) { Return (0x0F) } Name (_CRS, ResourceTemplate () { IO (Decode16, 0x03F0, 0x03F0, 0x01, 0x06) IO (Decode16, 0x03F7, 0x03F7, 0x01, 0x01) IRQNoFlags () {6} DMA (Compatibility, NotBusMaster, Transfer8) {2} }) } Device (UAR1) { Name (_HID, EisaId ("PNP0501")) Name (_UID, 0x01) Method (_STA, 0, NotSerialized) { If(LEqual(\_SB.UAR1, 0)) { Return(0x00) } Else { Return(0x0F) } } Name (_CRS, ResourceTemplate() { IO (Decode16, 0x03F8, 0x03F8, 8, 8) IRQNoFlags () {4} }) } Device (UAR2) { Name (_HID, EisaId ("PNP0501")) Name (_UID, 0x02) Method (_STA, 0, NotSerialized) { If(LEqual(\_SB.UAR2, 0)) { Return(0x00) } Else { Return(0x0F) } } Name (_CRS, ResourceTemplate() { IO (Decode16, 0x02F8, 0x02F8, 8, 8) IRQNoFlags () {3} }) } Device (LTP1) { Name (_HID, EisaId ("PNP0400")) Name (_UID, 0x02) Method (_STA, 0, NotSerialized) { If(LEqual(\_SB.LTP1, 0)) { Return(0x00) } Else { Return(0x0F) } } Name (_CRS, ResourceTemplate() { IO (Decode16, 0x0378, 0x0378, 0x08, 0x08) IRQNoFlags () {7} }) } Device(VGID) { Name(_HID, EisaId ("XEN0000")) Name(_UID, 0x00) Name(_CID, "VM_Gen_Counter") Name(_DDN, "VM_Gen_Counter") Method(_STA, 0, NotSerialized) { If(LEqual(\_SB.VGIA, 0x00000000)) { Return(0x00) } Else { Return(0x0F) } } Name(PKG, Package () { 0x00000000, 0x00000000 }) Method(ADDR, 0, NotSerialized) { Store(\_SB.VGIA, Index(PKG, 0)) Return(PKG) } } } } } /* _S3 and _S4 are in separate SSDTs */ Name (\_S5, Package (0x04) { 0x00, /* PM1a_CNT.SLP_TYP */ 0x00, /* PM1b_CNT.SLP_TYP */ 0x00, /* reserved */ 0x00 /* reserved */ }) Name(PICD, 0) Method(_PIC, 1) { Store(Arg0, PICD) } } xen-4.9.2/tools/libacpi/ssdt_s3.asl0000664000175000017500000000156013256712137015305 0ustar smbsmb/* * ssdt_s3.asl * * Copyright (c) 2011 Citrix Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ DefinitionBlock ("SSDT_S3.aml", "SSDT", 2, "Xen", "HVM", 0) { /* Must match piix emulation */ Name (\_S3, Package (0x04) { 0x01, /* PM1a_CNT.SLP_TYP */ 0x01, /* PM1b_CNT.SLP_TYP */ 0x0, /* reserved */ 0x0 /* reserved */ }) } xen-4.9.2/tools/libacpi/Makefile0000664000175000017500000000727113256712137014667 0ustar smbsmb# # Copyright (c) 2004, Intel Corporation. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; version 2.1 only. with the special # exception on linking described in file LICENSE. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # XEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk ifeq ($(ACPI_BUILD_DIR),) $(error ACPI_BUILD_DIR not set) endif MK_DSDT = $(ACPI_BUILD_DIR)/mk_dsdt C_SRC-$(CONFIG_X86) = dsdt_anycpu.c dsdt_15cpu.c dsdt_anycpu_qemu_xen.c dsdt_pvh.c C_SRC-$(CONFIG_ARM_64) = dsdt_anycpu_arm.c DSDT_FILES ?= $(C_SRC-y) C_SRC = $(addprefix $(ACPI_BUILD_DIR)/, $(DSDT_FILES)) H_SRC = $(addprefix $(ACPI_BUILD_DIR)/, ssdt_s3.h ssdt_s4.h ssdt_pm.h ssdt_tpm.h ssdt_laptop_slate.h) MKDSDT_CFLAGS-$(CONFIG_ARM_64) = -DCONFIG_ARM_64 MKDSDT_CFLAGS-$(CONFIG_X86) = -DCONFIG_X86 # Suffix for temporary files. # # We will also use this suffix to workaround a bug in older iasl # versions where the tool will ignore everything after last '.' in the # path ('-p' argument). By adding "." we force iasl to use # complete $(ACPI_BUILD_DIR) as path, even if it has '.' symbols. TMP_SUFFIX = tmp vpath iasl $(PATH) all: $(C_SRC) $(H_SRC) $(H_SRC): $(ACPI_BUILD_DIR)/%.h: %.asl iasl iasl -vs -p $(ACPI_BUILD_DIR)/$*.$(TMP_SUFFIX) -tc $< sed -e 's/AmlCode/$*/g' $(ACPI_BUILD_DIR)/$*.hex >$@ rm -f $(addprefix $(ACPI_BUILD_DIR)/, $*.aml $*.hex) $(MK_DSDT): mk_dsdt.c $(HOSTCC) $(HOSTCFLAGS) $(MKDSDT_CFLAGS-y) $(CFLAGS_xeninclude) -D__XEN_TOOLS__ -o $@ mk_dsdt.c $(ACPI_BUILD_DIR)/dsdt_anycpu_qemu_xen.asl: dsdt.asl dsdt_acpi_info.asl $(MK_DSDT) # Remove last bracket awk 'NR > 1 {print s} {s=$$0}' $< > $@.$(TMP_SUFFIX) cat dsdt_acpi_info.asl >> $@.$(TMP_SUFFIX) $(MK_DSDT) --debug=$(debug) --dm-version qemu-xen >> $@.$(TMP_SUFFIX) mv -f $@.$(TMP_SUFFIX) $@ # NB. awk invocation is a portable alternative to 'head -n -1' $(ACPI_BUILD_DIR)/dsdt_%cpu.asl: dsdt.asl dsdt_acpi_info.asl $(MK_DSDT) # Remove last bracket awk 'NR > 1 {print s} {s=$$0}' $< > $@.$(TMP_SUFFIX) cat dsdt_acpi_info.asl >> $@.$(TMP_SUFFIX) $(MK_DSDT) --debug=$(debug) --maxcpu $* >> $@.$(TMP_SUFFIX) mv -f $@.$(TMP_SUFFIX) $@ $(ACPI_BUILD_DIR)/dsdt_pvh.asl: dsdt_acpi_info.asl $(MK_DSDT) printf "DefinitionBlock (\"DSDT.aml\", \"DSDT\", 5, \"Xen\", \"HVM\", 0)\n{" > $@ cat dsdt_acpi_info.asl >> $@ $(MK_DSDT) --debug=$(debug) --maxcpu any --dm-version none >> $@ $(ACPI_BUILD_DIR)/dsdt_anycpu_arm.asl: $(MK_DSDT) printf "DefinitionBlock (\"DSDT.aml\", \"DSDT\", 3, \"Xen\", \"ARM\", 1)\n{" > $@.$(TMP_SUFFIX) $(MK_DSDT) --debug=$(debug) >> $@.$(TMP_SUFFIX) mv -f $@.$(TMP_SUFFIX) $@ $(C_SRC): $(ACPI_BUILD_DIR)/%.c: iasl $(ACPI_BUILD_DIR)/%.asl iasl -vs -p $(ACPI_BUILD_DIR)/$*.$(TMP_SUFFIX) -tc $(ACPI_BUILD_DIR)/$*.asl sed -e 's/AmlCode/$*/g' $(ACPI_BUILD_DIR)/$*.hex > $@.$(TMP_SUFFIX) echo "int $*_len=sizeof($*);" >> $@.$(TMP_SUFFIX) mv -f $@.$(TMP_SUFFIX) $@ rm -f $(addprefix $(ACPI_BUILD_DIR)/, $*.aml $*.hex) iasl: @echo @echo "ACPI ASL compiler (iasl) is needed" @echo "Download and install Intel ACPI CA from" @echo "http://acpica.org/downloads/" @echo @exit 1 build.o: ssdt_s3.h ssdt_s4.h ssdt_pm.h ssdt_tpm.h ssdt_laptop_slate.h acpi.a: $(OBJS) $(AR) rc $@ $(OBJS) clean: rm -f $(C_SRC) $(H_SRC) $(MK_DSDT) $(C_SRC:=.$(TMP_SUFFIX)) rm -f $(patsubst %.c,%.hex,$(C_SRC)) $(patsubst %.c,%.aml,$(C_SRC)) $(patsubst %.c,%.asl,$(C_SRC)) distclean: clean install: all -include $(DEPS) xen-4.9.2/tools/libacpi/libacpi.h0000664000175000017500000000615113256712137014777 0ustar smbsmb/****************************************************************************** * libacpi.h * * libacpi interfaces * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. */ #ifndef __LIBACPI_H__ #define __LIBACPI_H__ #define ACPI_HAS_COM1 (1<<0) #define ACPI_HAS_COM2 (1<<1) #define ACPI_HAS_LPT1 (1<<2) #define ACPI_HAS_HPET (1<<3) #define ACPI_HAS_SSDT_PM (1<<4) #define ACPI_HAS_SSDT_S3 (1<<5) #define ACPI_HAS_SSDT_S4 (1<<6) #define ACPI_HAS_TCPA (1<<7) #define ACPI_HAS_IOAPIC (1<<8) #define ACPI_HAS_WAET (1<<9) #define ACPI_HAS_PMTIMER (1<<10) #define ACPI_HAS_BUTTONS (1<<11) #define ACPI_HAS_VGA (1<<12) #define ACPI_HAS_8042 (1<<13) #define ACPI_HAS_CMOS_RTC (1<<14) #define ACPI_HAS_SSDT_LAPTOP_SLATE (1<<15) struct xen_vmemrange; struct acpi_numa { uint32_t nr_vmemranges; uint32_t nr_vnodes; const unsigned int *vcpu_to_vnode; const unsigned int *vdistance; const struct xen_vmemrange *vmemrange; }; struct acpi_ctxt { struct acpi_mem_ops { void *(*alloc)(struct acpi_ctxt *ctxt, uint32_t size, uint32_t align); void (*free)(struct acpi_ctxt *ctxt, void *v, uint32_t size); unsigned long (*v2p)(struct acpi_ctxt *ctxt, void *v); } mem_ops; }; struct acpi_config { const unsigned char *dsdt_anycpu; unsigned int dsdt_anycpu_len; const unsigned char *dsdt_15cpu; unsigned int dsdt_15cpu_len; /* PCI I/O hole */ uint32_t pci_start, pci_len; uint64_t pci_hi_start, pci_hi_len; uint32_t table_flags; uint8_t acpi_revision; uint64_t vm_gid[2]; unsigned long vm_gid_addr; /* OUT parameter */ struct { uint32_t addr; uint32_t length; } pt; struct acpi_numa numa; const struct hvm_info_table *hvminfo; const uint16_t *tis_hdr; /* * Address where acpi_info should be placed. * This must match the OperationRegion(BIOS, SystemMemory, ....) * definition in the DSDT */ unsigned long infop; /* RSDP address */ unsigned long rsdp; /* x86-specific parameters */ uint8_t (*lapic_id)(unsigned cpu); uint32_t lapic_base_address; uint32_t ioapic_base_address; uint16_t pci_isa_irq_mask; uint8_t ioapic_id; }; int acpi_build_tables(struct acpi_ctxt *ctxt, struct acpi_config *config); #endif /* __LIBACPI_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libacpi/dsdt_acpi_info.asl0000664000175000017500000000126413256712137016671 0ustar smbsmb Scope (\_SB) { /* * BIOS region must match struct acpi_info in build.c and * be located at ACPI_INFO_PHYSICAL_ADDRESS = 0xFC000000 */ OperationRegion(BIOS, SystemMemory, 0xFC000000, 40) Field(BIOS, ByteAcc, NoLock, Preserve) { UAR1, 1, UAR2, 1, LTP1, 1, HPET, 1, Offset(2), NCPU, 16, PMIN, 32, PLEN, 32, MSUA, 32, /* MADT checksum address */ MAPA, 32, /* MADT LAPIC0 address */ VGIA, 32, /* VM generation id address */ LMIN, 32, HMIN, 32, LLEN, 32, HLEN, 32 } } xen-4.9.2/tools/libacpi/ssdt_pm.asl0000664000175000017500000003043713256712137015401 0ustar smbsmb/* * ssdt_pm.asl * * Copyright (c) 2008 Kamala Narasimhan * Copyright (c) 2008 Citrix Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * SSDT for extended power management within HVM guest. Power management beyond * S3, S4, S5 is handled by this vACPI layer. * * Battery Management Implementation - * Xen vACPI layer exposes battery information to guest using CMBattery * interface. This virtual firmware CMBattery implementation is very similar to * the actual firmware CMBattery implementation. In fact, a good part of the * below is heavily borrowed from the underlying firmware to support * pass-through and non-pass-through battery management approaches using the * same CMBattery interface implementation. When pass-through approach is used, * the battery ports are directly mapped using xc_domain_ioport_mapping thus * not relying on qemu battery port handling to intercept port reads/writes to * feed relevant battery information to the guest. * * Following are the battery ports read/written to in order to implement * battery support: * Battery command port - 0xb2 * Batter data port - 0x86 * Battery commands (written to port 0xb2) - * 0x7b - Battery operation init * 0x7c - Type of battery operation * 0x79 - Get battery data length * 0x7d - Get battery data * * Also the following ports are used for debugging/logging: * 0xB040, 0xB044, 0xB046, 0xB048 */ DefinitionBlock ("SSDT_PM.aml", "SSDT", 2, "Xen", "HVM", 0) { Scope (\_SB) { OperationRegion (DBGA, SystemIO, 0xB040, 0x01) Field (DBGA, ByteAcc, NoLock, Preserve) { DBG1, 8, } OperationRegion (DBGB, SystemIO, 0xB044, 0x01) Field (DBGB, ByteAcc, NoLock, Preserve) { DBG2, 8, } OperationRegion (DBGC, SystemIO, 0xB046, 0x01) Field (DBGC, ByteAcc, NoLock, Preserve) { DBG3, 8, } OperationRegion (DBGD, SystemIO, 0xB048, 0x01) Field (DBGD, ByteAcc, NoLock, Preserve) { DBG4, 8, } OperationRegion (PRT1, SystemIO, 0xB2, 0x02) Field (PRT1, ByteAcc, NoLock, Preserve) { PB2, 8, PB2A, 8 } OperationRegion (PRT2, SystemIO, 0x86, 0x01) Field (PRT2, ByteAcc, NoLock, Preserve) { P86, 8 } OperationRegion (PRT3, SystemIO, 0x88, 0x01) Field (PRT3, ByteAcc, NoLock, Preserve) { P88, 8 } Mutex (SYNC, 0x01) Name (BUF0, Buffer (0x0100) {}) Name (BUF1, Buffer (0x08) {}) CreateWordField (BUF1, 0x00, BUFA) CreateWordField (BUF1, 0x04, BUFB) Method (ACQR, 0, NotSerialized) { Acquire (SYNC, 0xFFFF) Store (0x00, BUFA) } /* * Initialize relevant buffer to indicate what type of * information is being queried and by what object (e.g. * by battery device 0 or 1). */ Method (INIT, 1, NotSerialized) { Store (BUFA, Local0) Increment (Local0) If (LLessEqual (Local0, SizeOf (BUF0))) { CreateByteField (BUF0, BUFA, TMP1) Store (Arg0, TMP1) Store (Local0, BUFA) } } /* * Write to battery port 0xb2 indicating the type of information * to request, initialize battery data port 0x86 and then return * value provided through data port 0x86. */ Method (WPRT, 2, NotSerialized) { Store (Arg1, \_SB.P86) Store (Arg0, \_SB.PB2) Store (Arg0, \_SB.DBG2) Store (Arg1, \_SB.DBG4) Store (\_SB.PB2, Local0) While (LNotEqual (Local0, 0x00)) { Store (\_SB.PB2, Local0) } Store (\_SB.P86, Local1) Store (Local1, \_SB.DBG3) Return (\_SB.P86) } /* * Helper method 1 to write to battery command and data port. * 0x7c written to port 0xb2 indicating battery info type command. * Value 1 or 2 written to port 0x86. 1 for BIF (batterry info) and 2 * for BST (battery status). */ Method (HLP1, 2, NotSerialized) { If (LLess (Arg1, SizeOf (Arg0))) { CreateByteField (Arg0, Arg1, TMP1) WPRT (0x7C, TMP1) } } /* * Helper method 2. Value 0x7b written to battery command port 0xb2 * indicating battery info initialization request. First thing written * to battery port before querying for further information pertaining * to the battery. */ Method (HLP2, 0, NotSerialized) { WPRT (0x7B, 0x00) Store (0x00, Local0) While (LLess (Local0, BUFA)) { HLP1 (BUF0, Local0) Increment (Local0) } } /* * Helper method 3. 0x7d written to battery command port 0xb2 * indicating request of battery data returned through battery data * port 0x86. */ Method (HLP3, 2, NotSerialized) { If (LLess (Arg1, SizeOf (Arg0))) { CreateByteField (Arg0, Arg1, TMP1) Store (WPRT (0x7D, 0x00), TMP1) } } /* * Helper method 4 to indirectly get battery data and store it in a * local buffer. */ Method (HLP4, 0, NotSerialized) { Store (0x00, Local0) While (LLess (Local0, BUFB)) { Add (BUFA, Local0, Local1) HLP3 (BUF0, Local1) Increment (Local0) } } /* * Helper method 5 to indirectly initialize battery port and get * battery data. Also get battery data length by writing 0x79 to * battery command port and receiving battery data length in port 0x86. */ Method (HLP5, 0, NotSerialized) { HLP2 () Store (WPRT (0x79, 0x00), BUFB) Add (BUFA, BUFB, Local0) If (LLess (SizeOf (BUF0), Local0)) { Store (SizeOf (BUF0), Local0) Subtract (Local0, BUFA, Local0) Store (Local0, BUFB) } HLP4 () } /* Helper method for local buffer housekeeping... */ Method (HLP6, 0, NotSerialized) { Store (BUFA, Local0) Increment (Local0) If (LLessEqual (Local0, SizeOf (BUF0))) { CreateByteField (BUF0, BUFA, TMP1) Store (Local0, BUFA) Return (TMP1) } Return (0x00) } /* Helper methods to help store battery data retrieved through * battery data port 0x86. */ Method (HLP7, 0, NotSerialized) { Store (BUFA, Local0) Add (Local0, 0x04, Local0) If (LLessEqual (Local0, SizeOf (BUF0))) { CreateDWordField (BUF0, BUFA, SX22) Store (Local0, BUFA) Return (SX22) } Return (0x00) } Method (HLP8, 2, NotSerialized) { If (LLess (Arg1, SizeOf (Arg0))) { CreateByteField (Arg0, Arg1, TMP1) Store (HLP6 (), TMP1) } } Method (HLP9, 2, NotSerialized) { Store (0x00, Local0) While (LLess (Local0, Arg1)) { HLP8 (Arg0, Local0) Increment (Local0) } Return (Arg0) } Method (HLPA, 0, NotSerialized) { Store (HLP6 (), Local0) Return (HLP9 (Buffer (Local0) {}, Local0)) } Method (REL, 0, NotSerialized) { Release (SYNC) } /* Future patches will extend AC object to better account for * AC to DC transition and more. */ Device (AC) { Name (_HID, "ACPI0003") Name (_PCL, Package (0x03) { \_SB, BAT0, BAT1 }) Method (_PSR, 0, NotSerialized) { Return (0x0) } Method (_STA, 0, NotSerialized) { Return (0x0F) } } /* Main battery information helper method. */ Name (BIFP, Package (0x0D) {}) Method (BIF, 1, NotSerialized) { ACQR () INIT (0x01) INIT (Arg0) HLP5 () Store (HLP7 (), Index (BIFP, 0x00)) Store (HLP7 (), Index (BIFP, 0x01)) Store (HLP7 (), Index (BIFP, 0x02)) Store (HLP7 (), Index (BIFP, 0x03)) Store (HLP7 (), Index (BIFP, 0x04)) Store (HLP7 (), Index (BIFP, 0x05)) Store (HLP7 (), Index (BIFP, 0x06)) Store (HLP7 (), Index (BIFP, 0x07)) Store (HLP7 (), Index (BIFP, 0x08)) Store (HLPA (), Index (BIFP, 0x09)) Store (HLPA (), Index (BIFP, 0x0A)) Store (HLPA (), Index (BIFP, 0x0B)) Store (HLPA (), Index (BIFP, 0x0C)) REL () Return (BIFP) } /* Battery object 0 - Always exposed as present. */ Device (BAT0) { Name (_HID, EisaId ("PNP0C0A")) Name (_UID, 0x01) Name (_PCL, Package (0x01) { \_SB }) /* Always returns 0x1f indicating battery present. */ Method (_STA, 0, NotSerialized) { Store (\_SB.P88, Local0) Return ( Local0 ) } /* Battery generic info: design capacity, voltage, model # etc. */ Method (_BIF, 0, NotSerialized) { //Store (1, \_SB.DBG1) Store(BIF ( 0x01 ), Local0) //Store (2, \_SB.DBG1) Return( Local0 ) } /* Battery status including battery charging/discharging rate. */ Method (_BST, 0, NotSerialized) { Store (1, \_SB.DBG1) ACQR () INIT (0x02) INIT (0x01) HLP5 () Store (Package (0x04) {}, Local0) Store (HLP7 (), Index (Local0, 0x00)) Store (HLP7 (), Index (Local0, 0x01)) Store (HLP7 (), Index (Local0, 0x02)) Store (HLP7 (), Index (Local0, 0x03)) REL () Store (2, \_SB.DBG1) Return (Local0) } } /* Battery object 1 - Always exposed as not present. */ Device (BAT1) { Name (_HID, EisaId ("PNP0C0A")) Name (_UID, 0x02) Name (_PCL, Package (0x01) { \_SB }) Method (_STA, 0, NotSerialized) { Return (0x0F) } Method (_BIF, 0, NotSerialized) { Store (\_SB.PB2, Local0) Return (BIF (0x02)) } Method (_BST, 0, NotSerialized) { ACQR () INIT (0x02) INIT (0x02) HLP5 () Store (Package (0x04) {}, Local0) Store (HLP7 (), Index (Local0, 0x00)) Store (HLP7 (), Index (Local0, 0x01)) Store (HLP7 (), Index (Local0, 0x02)) Store (HLP7 (), Index (Local0, 0x03)) REL () Return (Local0) } } } } xen-4.9.2/tools/libacpi/mk_dsdt.c0000664000175000017500000004063013256712137015014 0ustar smbsmb/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #if defined(CONFIG_X86) #include #include #elif defined(CONFIG_ARM_64) #include #endif static unsigned int indent_level; static bool debug = false; typedef enum dm_version { QEMU_NONE, QEMU_XEN_TRADITIONAL, QEMU_XEN, } dm_version; static void indent(void) { unsigned int i; for ( i = 0; i < indent_level; i++ ) printf(" "); } static __attribute__((format(printf, 2, 3))) void _stmt(const char *name, const char *fmt, ...) { va_list args; indent(); printf("%s", name); if ( !fmt ) return; printf(" ( "); va_start(args, fmt); vprintf(fmt, args); va_end(args); printf(" )"); } #define stmt(n, f, a...) \ do { \ _stmt(n, f , ## a ); \ printf("\n"); \ } while (0) #define push_block(n, f, a...) \ do { \ _stmt(n, f , ## a ); \ printf(" {\n"); \ indent_level++; \ } while (0) static void pop_block(void) { indent_level--; indent(); printf("}\n"); } #ifdef CONFIG_X86 static void pci_hotplug_notify(unsigned int slt) { stmt("Notify", "\\_SB.PCI0.S%02X, EVT", slt); } static void decision_tree( unsigned int s, unsigned int e, char *var, void (*leaf)(unsigned int)) { if ( s == (e-1) ) { (*leaf)(s); return; } push_block("If", "And(%s, 0x%02x)", var, (e-s)/2); decision_tree((s+e)/2, e, var, leaf); pop_block(); push_block("Else", NULL); decision_tree(s, (s+e)/2, var, leaf); pop_block(); } #endif static struct option options[] = { { "maxcpu", 1, 0, 'c' }, #ifdef CONFIG_X86 { "dm-version", 1, 0, 'q' }, #endif { "debug", 1, 0, 'd' }, { 0, 0, 0, 0 } }; int main(int argc, char **argv) { unsigned int cpu, max_cpus; #if defined(CONFIG_X86) dm_version dm_version = QEMU_XEN_TRADITIONAL; unsigned int slot, dev, intx, link; max_cpus = HVM_MAX_VCPUS; #elif defined(CONFIG_ARM_64) max_cpus = GUEST_MAX_VCPUS; #endif for ( ; ; ) { int opt = getopt_long(argc, argv, "", options, NULL); if ( opt == -1 ) break; switch ( opt ) { case 'c': { long i = 0; char *endptr; i = strtol(optarg, &endptr, 10); if ( (*optarg != '\0') && (*endptr == '\0') && (i >= 0) ) { max_cpus = i; } else if ( !(strcmp(optarg, "any") == 0) ) { fprintf(stderr, "`%s' is not a number or is < 0.\n", optarg); return -1; } break; } #ifdef CONFIG_X86 case 'q': if (strcmp(optarg, "qemu-xen") == 0) { dm_version = QEMU_XEN; } else if (strcmp(optarg, "qemu-xen-traditional") == 0) { dm_version = QEMU_XEN_TRADITIONAL; } else if (strcmp(optarg, "none") == 0) { dm_version = QEMU_NONE; } else { fprintf(stderr, "Unknown device model version `%s'.\n", optarg); return -1; } break; #endif case 'd': if (*optarg == 'y') debug = true; break; default: return -1; } } /**** DSDT DefinitionBlock start ****/ /* (we append to existing DSDT definition block) */ indent_level++; /**** Processor start ****/ push_block("Scope", "\\_SB"); #ifdef CONFIG_X86 /* MADT checksum */ stmt("OperationRegion", "MSUM, SystemMemory, \\_SB.MSUA, 1"); push_block("Field", "MSUM, ByteAcc, NoLock, Preserve"); indent(); printf("MSU, 8\n"); pop_block(); /* Processor object helpers. */ push_block("Method", "PMAT, 2"); push_block("If", "LLess(Arg0, NCPU)"); stmt("Return", "ToBuffer(Arg1)"); pop_block(); stmt("Return", "Buffer() {0, 8, 0xff, 0xff, 0, 0, 0, 0}"); pop_block(); #endif /* Define processor objects and control methods. */ for ( cpu = 0; cpu < max_cpus; cpu++) { push_block("Processor", "PR%02X, %d, 0x0000b010, 0x06", cpu, cpu); stmt("Name", "_HID, \"ACPI0007\""); stmt("Name", "_UID, %d", cpu); #ifdef CONFIG_ARM_64 pop_block(); continue; #endif /* Name this processor's MADT LAPIC descriptor. */ stmt("OperationRegion", "MATR, SystemMemory, Add(\\_SB.MAPA, %d), 8", cpu*8); push_block("Field", "MATR, ByteAcc, NoLock, Preserve"); indent(); printf("MAT, 64\n"); pop_block(); push_block("Field", "MATR, ByteAcc, NoLock, Preserve"); indent(); printf("Offset(4),\n"); indent(); printf("FLG, 1\n"); pop_block(); push_block("Method", "_MAT, 0"); if ( cpu ) stmt("Return", "PMAT (%d, MAT)", cpu); else stmt("Return", "ToBuffer(MAT)"); pop_block(); push_block("Method", "_STA"); if ( cpu ) push_block("If", "LLess(%d, \\_SB.NCPU)", cpu); push_block("If", "FLG"); stmt("Return", "0xF"); pop_block(); if ( cpu ) pop_block(); stmt("Return", "0x0"); pop_block(); push_block("Method", "_EJ0, 1, NotSerialized"); stmt("Sleep", "0xC8"); pop_block(); pop_block(); } #ifdef CONFIG_ARM_64 pop_block(); /**** Processor end ****/ #else /* Operation Region 'PRST': bitmask of online CPUs. */ stmt("OperationRegion", "PRST, SystemIO, %#x, %d", XEN_ACPI_CPU_MAP, XEN_ACPI_CPU_MAP_LEN); push_block("Field", "PRST, ByteAcc, NoLock, Preserve"); indent(); printf("PRS, %u\n", max_cpus); pop_block(); /* Control method 'PRSC': CPU hotplug GPE handler. */ push_block("Method", "PRSC, 0"); stmt("Store", "ToBuffer(PRS), Local0"); for ( cpu = 0; cpu < max_cpus; cpu++ ) { /* Read a byte at a time from the PRST online-CPU bitmask. */ if ( (cpu & 7) == 0 ) stmt("Store", "DerefOf(Index(Local0, %u)), Local1", cpu/8); else stmt("ShiftRight", "Local1, 1, Local1"); /* Extract current CPU's status: 0=offline; 1=online. */ stmt("And", "Local1, 1, Local2"); /* Check if status is up-to-date in the relevant MADT LAPIC entry... */ push_block("If", "LNotEqual(Local2, \\_SB.PR%02X.FLG)", cpu); /* ...If not, update it and the MADT checksum, and notify OSPM. */ stmt("Store", "Local2, \\_SB.PR%02X.FLG", cpu); push_block("If", "LEqual(Local2, 1)"); stmt("Notify", "PR%02X, 1", cpu); /* Notify: Device Check */ stmt("Subtract", "\\_SB.MSU, 1, \\_SB.MSU"); /* Adjust MADT csum */ pop_block(); push_block("Else", NULL); stmt("Notify", "PR%02X, 3", cpu); /* Notify: Eject Request */ stmt("Add", "\\_SB.MSU, 1, \\_SB.MSU"); /* Adjust MADT csum */ pop_block(); pop_block(); } stmt("Return", "One"); pop_block(); pop_block(); /* Define GPE control method. */ push_block("Scope", "\\_GPE"); push_block("Method", dm_version == QEMU_XEN_TRADITIONAL ? "_L%02d" : "_E%02d", XEN_ACPI_GPE0_CPUHP_BIT); stmt("\\_SB.PRSC ()", NULL); pop_block(); pop_block(); if (dm_version == QEMU_NONE) { pop_block(); return 0; } /**** Processor end ****/ /**** PCI0 start ****/ push_block("Scope", "\\_SB.PCI0"); /* * Reserve the IO port ranges [0x10c0, 0x1101] and [0xb044, 0xb047]. * Or else, for a hotplugged-in device, the port IO BAR assigned * by guest OS may conflict with the ranges here. */ push_block("Device", "HP0"); { stmt("Name", "_HID, EISAID(\"PNP0C02\")"); if (dm_version == QEMU_XEN_TRADITIONAL) { stmt("Name", "_CRS, ResourceTemplate() {" " IO (Decode16, 0x10c0, 0x10c0, 0x00, 0x82)" " IO (Decode16, 0xb044, 0xb044, 0x00, 0x04)" "}"); } else { stmt("Name", "_CRS, ResourceTemplate() {" " IO (Decode16, 0xae00, 0xae00, 0x00, 0x10)" " IO (Decode16, 0xb044, 0xb044, 0x00, 0x04)" "}"); } } pop_block(); /*** PCI-ISA link definitions ***/ /* BUFA: List of ISA IRQs available for linking to PCI INTx. */ stmt("Name", "BUFA, ResourceTemplate() { " "IRQ(Level, ActiveLow, Shared) { 5, 10, 11 } }"); /* BUFB: IRQ descriptor for returning from link-device _CRS methods. */ stmt("Name", "BUFB, Buffer() { " "0x23, 0x00, 0x00, 0x18, " /* IRQ descriptor */ "0x79, 0 }"); /* End tag, null checksum */ stmt("CreateWordField", "BUFB, 0x01, IRQV"); /* Create four PCI-ISA link devices: LNKA, LNKB, LNKC, LNKD. */ for ( link = 0; link < 4; link++ ) { push_block("Device", "LNK%c", 'A'+link); stmt("Name", "_HID, EISAID(\"PNP0C0F\")"); /* PCI interrupt link */ stmt("Name", "_UID, %u", link+1); push_block("Method", "_STA, 0"); push_block("If", "And(PIR%c, 0x80)", 'A'+link); stmt("Return", "0x09"); pop_block(); push_block("Else", NULL); stmt("Return", "0x0B"); pop_block(); pop_block(); push_block("Method", "_PRS"); stmt("Return", "BUFA"); pop_block(); push_block("Method", "_DIS"); stmt("Or", "PIR%c, 0x80, PIR%c", 'A'+link, 'A'+link); pop_block(); push_block("Method", "_CRS"); stmt("And", "PIR%c, 0x0f, Local0", 'A'+link); stmt("ShiftLeft", "0x1, Local0, IRQV"); stmt("Return", "BUFB"); pop_block(); push_block("Method", "_SRS, 1"); stmt("CreateWordField", "ARG0, 0x01, IRQ1"); stmt("FindSetRightBit", "IRQ1, Local0"); stmt("Decrement", "Local0"); stmt("Store", "Local0, PIR%c", 'A'+link); pop_block(); pop_block(); } /*** PCI interrupt routing definitions***/ /* _PRT: Method to return routing table. */ push_block("Method", "_PRT, 0"); push_block("If", "PICD"); stmt("Return", "PRTA"); pop_block(); stmt("Return", "PRTP"); pop_block(); /* PRTP: PIC routing table (via ISA links). */ printf("Name(PRTP, Package() {\n"); for ( dev = 1; dev < 32; dev++ ) for ( intx = 0; intx < 4; intx++ ) /* INTA-D */ printf("Package(){0x%04xffff, %u, \\_SB.PCI0.LNK%c, 0},\n", dev, intx, 'A'+((dev+intx)&3)); printf("})\n"); /* PRTA: APIC routing table (via non-legacy IOAPIC GSIs). */ printf("Name(PRTA, Package() {\n"); for ( dev = 1; dev < 32; dev++ ) for ( intx = 0; intx < 4; intx++ ) /* INTA-D */ printf("Package(){0x%04xffff, %u, 0, %u},\n", dev, intx, ((dev*4+dev/8+intx)&31)+16); printf("})\n"); /* * Each PCI hotplug slot needs at least two methods to handle * the ACPI event: * _EJ0: eject a device * _STA: return a device's status, e.g. enabled or removed * * Eject button would generate a general-purpose event, then the * control method for this event uses Notify() to inform OSPM which * action happened and on which device. * * Pls. refer "6.3 Device Insertion, Removal, and Status Objects" * in ACPI spec 3.0b for details. * * QEMU provides a simple hotplug controller with some I/O to handle * the hotplug action and status, which is beyond the ACPI scope. */ if (dm_version == QEMU_XEN_TRADITIONAL) { for ( slot = 0; slot < 0x100; slot++ ) { push_block("Device", "S%02X", slot); /* _ADR == dev:fn (16:16) */ stmt("Name", "_ADR, 0x%08x", ((slot & ~7) << 13) | (slot & 7)); /* _SUN == dev */ stmt("Name", "_SUN, 0x%08x", slot >> 3); push_block("Method", "_EJ0, 1"); if (debug) { stmt("Store", "0x%02x, \\_GPE.DPT1", slot); stmt("Store", "0x88, \\_GPE.DPT2"); } stmt("Store", "0x%02x, \\_GPE.PH%02X", /* eject */ (slot & 1) ? 0x10 : 0x01, slot & ~1); pop_block(); push_block("Method", "_STA, 0"); if (debug) { stmt("Store", "0x%02x, \\_GPE.DPT1", slot); stmt("Store", "0x89, \\_GPE.DPT2"); } if ( slot & 1 ) stmt("ShiftRight", "0x4, \\_GPE.PH%02X, Local1", slot & ~1); else stmt("And", "\\_GPE.PH%02X, 0x0f, Local1", slot & ~1); stmt("Return", "Local1"); /* IN status as the _STA */ pop_block(); pop_block(); } } else { stmt("OperationRegion", "SEJ, SystemIO, 0xae08, 0x04"); push_block("Field", "SEJ, DWordAcc, NoLock, WriteAsZeros"); indent(); printf("B0EJ, 32,\n"); pop_block(); /* hotplug_slot */ for (slot = 1; slot <= 31; slot++) { push_block("Device", "S%i", slot); { stmt("Name", "_ADR, %#06x0000", slot); push_block("Method", "_EJ0,1"); { stmt("Store", "%#010x, B0EJ", 1 << slot); } pop_block(); stmt("Name", "_SUN, %i", slot); } pop_block(); } } pop_block(); /**** PCI0 end ****/ /**** GPE start ****/ push_block("Scope", "\\_GPE"); if (dm_version == QEMU_XEN_TRADITIONAL) { stmt("OperationRegion", "PHP, SystemIO, 0x10c0, 0x82"); push_block("Field", "PHP, ByteAcc, NoLock, Preserve"); indent(); printf("PSTA, 8,\n"); /* hotplug controller event reg */ indent(); printf("PSTB, 8,\n"); /* hotplug controller slot reg */ for ( slot = 0; slot < 0x100; slot += 2 ) { indent(); /* Each hotplug control register manages a pair of pci functions. */ printf("PH%02X, 8,\n", slot); } pop_block(); } else { stmt("OperationRegion", "PCST, SystemIO, 0xae00, 0x08"); push_block("Field", "PCST, DWordAcc, NoLock, WriteAsZeros"); indent(); printf("PCIU, 32,\n"); indent(); printf("PCID, 32,\n"); pop_block(); } stmt("OperationRegion", "DG1, SystemIO, 0xb044, 0x04"); push_block("Field", "DG1, ByteAcc, NoLock, Preserve"); indent(); printf("DPT1, 8, DPT2, 8\n"); pop_block(); if (dm_version == QEMU_XEN_TRADITIONAL) { push_block("Method", "_L03, 0, Serialized"); /* Detect slot and event (remove/add). */ stmt("Name", "SLT, 0x0"); stmt("Name", "EVT, 0x0"); stmt("Store", "PSTA, Local1"); stmt("And", "Local1, 0xf, EVT"); stmt("Store", "PSTB, Local1"); /* XXX: Store (PSTB, SLT) ? */ stmt("And", "Local1, 0xff, SLT"); if (debug) { stmt("Store", "SLT, DPT1"); stmt("Store", "EVT, DPT2"); } /* Decision tree */ decision_tree(0x00, 0x100, "SLT", pci_hotplug_notify); pop_block(); } else { push_block("Method", "_E01"); for (slot = 1; slot <= 31; slot++) { push_block("If", "And(PCIU, ShiftLeft(1, %i))", slot); stmt("Notify", "\\_SB.PCI0.S%i, 1", slot); pop_block(); push_block("If", "And(PCID, ShiftLeft(1, %i))", slot); stmt("Notify", "\\_SB.PCI0.S%i, 3", slot); pop_block(); } pop_block(); } pop_block(); /**** GPE end ****/ #endif pop_block(); /**** DSDT DefinitionBlock end ****/ return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libacpi/README0000664000175000017500000000162713256712137014106 0ustar smbsmbACPI builder for domain firmware BUILDING ACPI ----------------- Users of ACPI builder are expected to provide an include file that makes available the following: * strncpy * printf * NULL * test_bit * offsetof When compiling build.c, the name of this include file should be given to compiler as -DLIBACPI_STDUTILS=\"\". See tools/firmware/hvmloader/Makefile for an example. Note on DSDT Table ------------------ DSDT table source code is acpi_dsdt.asl It is already compiled and the output is acpi_dsdt.c Usually, user is not expected to change the acpi_dsdt.asl. In case that the acpi_dsdt.asl need to be updated, please Follow the instruction: # make acpi_dsdt.c Note: DSDT compiler "iasl" is needed. By default, it will be downloaded using wget in Makefile. if it failed, please download manually from http://developer.intel.com/technology/iapc/acpi/downloads.htm. then compile and install iasl xen-4.9.2/tools/libacpi/ssdt_s4.asl0000664000175000017500000000156013256712137015306 0ustar smbsmb/* * ssdt_s4.asl * * Copyright (c) 2011 Citrix Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ DefinitionBlock ("SSDT_S4.aml", "SSDT", 2, "Xen", "HVM", 0) { /* Must match piix emulation */ Name (\_S4, Package (0x04) { 0x00, /* PM1a_CNT.SLP_TYP */ 0x00, /* PM1b_CNT.SLP_TYP */ 0x00, /* reserved */ 0x00 /* reserved */ }) } xen-4.9.2/tools/libacpi/acpi2_0.h0000664000175000017500000002610013256712137014605 0ustar smbsmb/* * Copyright (c) 2004, Intel Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef _ACPI_2_0_H_ #define _ACPI_2_0_H_ #include #include #include #define ASCII32(a,b,c,d) \ (((a) << 0) | ((b) << 8) | ((c) << 16) | ((d) << 24)) #define ASCII64(a,b,c,d,e,f,g,h) \ (((uint64_t)ASCII32(a,b,c,d)) | (((uint64_t)ASCII32(e,f,g,h)) << 32)) #pragma pack (1) /* * Common ACPI header. */ struct acpi_header { uint32_t signature; uint32_t length; uint8_t revision; uint8_t checksum; char oem_id[6]; char oem_table_id[8]; uint32_t oem_revision; uint32_t creator_id; uint32_t creator_revision; }; #define ACPI_OEM_ID "Xen" #define ACPI_OEM_TABLE_ID "HVM" #define ACPI_OEM_REVISION 0 #define ACPI_CREATOR_ID ASCII32('H','V','M','L') /* HVMLoader */ #define ACPI_CREATOR_REVISION 0 /* * ACPI 2.0 Generic Address Space definition. */ struct acpi_20_generic_address { uint8_t address_space_id; uint8_t register_bit_width; uint8_t register_bit_offset; uint8_t reserved; uint64_t address; }; /* * Generic Address Space Address IDs. */ #define ACPI_SYSTEM_MEMORY 0 #define ACPI_SYSTEM_IO 1 #define ACPI_PCI_CONFIGURATION_SPACE 2 #define ACPI_EMBEDDED_CONTROLLER 3 #define ACPI_SMBUS 4 #define ACPI_FUNCTIONAL_FIXED_HARDWARE 0x7F /* * Root System Description Pointer Structure in ACPI 1.0. */ struct acpi_10_rsdp { uint64_t signature; uint8_t checksum; char oem_id[6]; uint8_t reserved; uint32_t rsdt_address; }; /* * Root System Description Pointer Structure. */ struct acpi_20_rsdp { uint64_t signature; uint8_t checksum; char oem_id[6]; uint8_t revision; uint32_t rsdt_address; uint32_t length; uint64_t xsdt_address; uint8_t extended_checksum; uint8_t reserved[3]; }; /* * Root System Description Table (RSDT). */ struct acpi_20_rsdt { struct acpi_header header; uint32_t entry[1]; }; /* * Extended System Description Table (XSDT). */ struct acpi_20_xsdt { struct acpi_header header; uint64_t entry[1]; }; /* * TCG Hardware Interface Table (TCPA) */ struct acpi_20_tcpa { struct acpi_header header; uint16_t platform_class; uint32_t laml; uint64_t lasa; }; #define ACPI_2_0_TCPA_LAML_SIZE (64*1024) /* * Fixed ACPI Description Table Structure (FADT) in ACPI 1.0. */ struct acpi_10_fadt { struct acpi_header header; uint32_t firmware_ctrl; uint32_t dsdt; uint8_t reserved0; uint8_t preferred_pm_profile; uint16_t sci_int; uint32_t smi_cmd; uint8_t acpi_enable; uint8_t acpi_disable; uint8_t s4bios_req; uint8_t pstate_cnt; uint32_t pm1a_evt_blk; uint32_t pm1b_evt_blk; uint32_t pm1a_cnt_blk; uint32_t pm1b_cnt_blk; uint32_t pm2_cnt_blk; uint32_t pm_tmr_blk; uint32_t gpe0_blk; uint32_t gpe1_blk; uint8_t pm1_evt_len; uint8_t pm1_cnt_len; uint8_t pm2_cnt_len; uint8_t pm_tmr_len; uint8_t gpe0_blk_len; uint8_t gpe1_blk_len; uint8_t gpe1_base; uint8_t cst_cnt; uint16_t p_lvl2_lat; uint16_t p_lvl3_lat; uint16_t flush_size; uint16_t flush_stride; uint8_t duty_offset; uint8_t duty_width; uint8_t day_alrm; uint8_t mon_alrm; uint8_t century; uint16_t iapc_boot_arch; uint8_t reserved1; uint32_t flags; }; /* * Fixed ACPI Description Table Structure (FADT). */ struct acpi_fadt { struct acpi_header header; uint32_t firmware_ctrl; uint32_t dsdt; uint8_t reserved0; uint8_t preferred_pm_profile; uint16_t sci_int; uint32_t smi_cmd; uint8_t acpi_enable; uint8_t acpi_disable; uint8_t s4bios_req; uint8_t pstate_cnt; uint32_t pm1a_evt_blk; uint32_t pm1b_evt_blk; uint32_t pm1a_cnt_blk; uint32_t pm1b_cnt_blk; uint32_t pm2_cnt_blk; uint32_t pm_tmr_blk; uint32_t gpe0_blk; uint32_t gpe1_blk; uint8_t pm1_evt_len; uint8_t pm1_cnt_len; uint8_t pm2_cnt_len; uint8_t pm_tmr_len; uint8_t gpe0_blk_len; uint8_t gpe1_blk_len; uint8_t gpe1_base; uint8_t cst_cnt; uint16_t p_lvl2_lat; uint16_t p_lvl3_lat; uint16_t flush_size; uint16_t flush_stride; uint8_t duty_offset; uint8_t duty_width; uint8_t day_alrm; uint8_t mon_alrm; uint8_t century; uint16_t iapc_boot_arch; uint8_t reserved1; uint32_t flags; struct acpi_20_generic_address reset_reg; uint8_t reset_value; uint8_t reserved2[3]; uint64_t x_firmware_ctrl; uint64_t x_dsdt; struct acpi_20_generic_address x_pm1a_evt_blk; struct acpi_20_generic_address x_pm1b_evt_blk; struct acpi_20_generic_address x_pm1a_cnt_blk; struct acpi_20_generic_address x_pm1b_cnt_blk; struct acpi_20_generic_address x_pm2_cnt_blk; struct acpi_20_generic_address x_pm_tmr_blk; struct acpi_20_generic_address x_gpe0_blk; struct acpi_20_generic_address x_gpe1_blk; /* Only available starting from FADT revision 5. */ struct acpi_20_generic_address sleep_control; struct acpi_20_generic_address sleep_status; }; /* * FADT Boot Architecture Flags. */ #define ACPI_FADT_LEGACY_DEVICES (1 << 0) #define ACPI_FADT_8042 (1 << 1) #define ACPI_FADT_NO_VGA (1 << 2) #define ACPI_FADT_NO_CMOS_RTC (1 << 5) /* * FADT Fixed Feature Flags. */ #define ACPI_WBINVD (1 << 0) #define ACPI_WBINVD_FLUSH (1 << 1) #define ACPI_PROC_C1 (1 << 2) #define ACPI_P_LVL2_UP (1 << 3) #define ACPI_PWR_BUTTON (1 << 4) #define ACPI_SLP_BUTTON (1 << 5) #define ACPI_FIX_RTC (1 << 6) #define ACPI_RTC_S4 (1 << 7) #define ACPI_TMR_VAL_EXT (1 << 8) #define ACPI_DCK_CAP (1 << 9) #define ACPI_RESET_REG_SUP (1 << 10) #define ACPI_SEALED_CASE (1 << 11) #define ACPI_HEADLESS (1 << 12) #define ACPI_CPU_SW_SLP (1 << 13) #define ACPI_USE_PLATFORM_CLOCK (1 << 15) /* PM1 Control Register Bits */ #define ACPI_PM1C_SCI_EN (1 << 0) /* * Firmware ACPI Control Structure (FACS). */ struct acpi_20_facs { uint32_t signature; uint32_t length; uint32_t hardware_signature; uint32_t firmware_waking_vector; uint32_t global_lock; uint32_t flags; uint64_t x_firmware_waking_vector; uint8_t version; uint8_t reserved[31]; }; #define ACPI_2_0_FACS_VERSION 0x01 /* * Multiple APIC Description Table header definition (MADT). */ struct acpi_20_madt { struct acpi_header header; uint32_t lapic_addr; uint32_t flags; }; /* * HPET Description Table */ struct acpi_20_hpet { struct acpi_header header; uint32_t timer_block_id; struct acpi_20_generic_address addr; uint8_t hpet_number; uint16_t min_tick; uint8_t page_protect; }; #define ACPI_HPET_ADDRESS 0xFED00000UL /* * WAET Description Table */ struct acpi_20_waet { struct acpi_header header; uint32_t flags; }; /* * Multiple APIC Flags. */ #define ACPI_PCAT_COMPAT (1 << 0) /* * Multiple APIC Description Table APIC structure types. */ #define ACPI_PROCESSOR_LOCAL_APIC 0x00 #define ACPI_IO_APIC 0x01 #define ACPI_INTERRUPT_SOURCE_OVERRIDE 0x02 #define ACPI_NON_MASKABLE_INTERRUPT_SOURCE 0x03 #define ACPI_LOCAL_APIC_NMI 0x04 #define ACPI_LOCAL_APIC_ADDRESS_OVERRIDE 0x05 #define ACPI_IO_SAPIC 0x06 #define ACPI_PROCESSOR_LOCAL_SAPIC 0x07 #define ACPI_PLATFORM_INTERRUPT_SOURCES 0x08 /* * APIC Structure Definitions. */ /* * Processor Local APIC Structure Definition. */ struct acpi_20_madt_lapic { uint8_t type; uint8_t length; uint8_t acpi_processor_id; uint8_t apic_id; uint32_t flags; }; /* * Local APIC Flags. All other bits are reserved and must be 0. */ #define ACPI_LOCAL_APIC_ENABLED (1 << 0) /* * IO APIC Structure. */ struct acpi_20_madt_ioapic { uint8_t type; uint8_t length; uint8_t ioapic_id; uint8_t reserved; uint32_t ioapic_addr; uint32_t gsi_base; }; struct acpi_20_madt_intsrcovr { uint8_t type; uint8_t length; uint8_t bus; uint8_t source; uint32_t gsi; uint16_t flags; }; /* * System Resource Affinity Table header definition (SRAT) */ struct acpi_20_srat { struct acpi_header header; uint32_t table_revision; uint32_t reserved2[2]; }; #define ACPI_SRAT_TABLE_REVISION 1 /* * System Resource Affinity Table structure types. */ #define ACPI_PROCESSOR_AFFINITY 0x0 #define ACPI_MEMORY_AFFINITY 0x1 struct acpi_20_srat_processor { uint8_t type; uint8_t length; uint8_t domain; uint8_t apic_id; uint32_t flags; uint8_t sapic_id; uint8_t domain_hi[3]; uint32_t reserved; }; /* * Local APIC Affinity Flags. All other bits are reserved and must be 0. */ #define ACPI_LOCAL_APIC_AFFIN_ENABLED (1 << 0) struct acpi_20_srat_memory { uint8_t type; uint8_t length; uint32_t domain; uint16_t reserved; uint64_t base_address; uint64_t mem_length; uint32_t reserved2; uint32_t flags; uint64_t reserved3; }; /* * Memory Affinity Flags. All other bits are reserved and must be 0. */ #define ACPI_MEM_AFFIN_ENABLED (1 << 0) #define ACPI_MEM_AFFIN_HOTPLUGGABLE (1 << 1) #define ACPI_MEM_AFFIN_NONVOLATILE (1 << 2) struct acpi_20_slit { struct acpi_header header; uint64_t localities; uint8_t entry[0]; }; /* * Table Signatures. */ #define ACPI_2_0_RSDP_SIGNATURE ASCII64('R','S','D',' ','P','T','R',' ') #define ACPI_2_0_FACS_SIGNATURE ASCII32('F','A','C','S') #define ACPI_FADT_SIGNATURE ASCII32('F','A','C','P') #define ACPI_2_0_MADT_SIGNATURE ASCII32('A','P','I','C') #define ACPI_2_0_RSDT_SIGNATURE ASCII32('R','S','D','T') #define ACPI_2_0_XSDT_SIGNATURE ASCII32('X','S','D','T') #define ACPI_2_0_TCPA_SIGNATURE ASCII32('T','C','P','A') #define ACPI_2_0_HPET_SIGNATURE ASCII32('H','P','E','T') #define ACPI_2_0_WAET_SIGNATURE ASCII32('W','A','E','T') #define ACPI_2_0_SRAT_SIGNATURE ASCII32('S','R','A','T') #define ACPI_2_0_SLIT_SIGNATURE ASCII32('S','L','I','T') /* * Table revision numbers. */ #define ACPI_2_0_RSDP_REVISION 0x02 #define ACPI_2_0_MADT_REVISION 0x02 #define ACPI_2_0_RSDT_REVISION 0x01 #define ACPI_2_0_XSDT_REVISION 0x01 #define ACPI_2_0_TCPA_REVISION 0x02 #define ACPI_2_0_HPET_REVISION 0x01 #define ACPI_2_0_WAET_REVISION 0x01 #define ACPI_1_0_FADT_REVISION 0x01 #define ACPI_2_0_SRAT_REVISION 0x01 #define ACPI_2_0_SLIT_REVISION 0x01 #pragma pack () #endif /* _ACPI_2_0_H_ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libacpi/ssdt_tpm.asl0000664000175000017500000000162013256712137015555 0ustar smbsmb/* * ssdt_tpm.asl * * Copyright (c) 2006, IBM Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* SSDT for TPM TIS Interface for Xen with Qemu device model. */ DefinitionBlock ("SSDT_TPM.aml", "SSDT", 2, "Xen", "HVM", 0) { Device (TPM) { Name (_HID, EisaId ("PNP0C31")) Name (_CRS, ResourceTemplate () { Memory32Fixed (ReadWrite, 0xFED40000, 0x5000,) }) } } xen-4.9.2/tools/libacpi/build.c0000664000175000017500000005750113256712137014473 0ustar smbsmb/* * Copyright (c) 2004, Intel Corporation. * Copyright (c) 2006, Keir Fraser, XenSource Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include LIBACPI_STDUTILS #include "acpi2_0.h" #include "libacpi.h" #include "ssdt_s3.h" #include "ssdt_s4.h" #include "ssdt_tpm.h" #include "ssdt_pm.h" #include "ssdt_laptop_slate.h" #include #include #include #include #define ACPI_MAX_SECONDARY_TABLES 16 #define align16(sz) (((sz) + 15) & ~15) #define fixed_strcpy(d, s) strncpy((d), (s), sizeof(d)) extern struct acpi_20_rsdp Rsdp; extern struct acpi_20_rsdt Rsdt; extern struct acpi_20_xsdt Xsdt; extern struct acpi_fadt Fadt; extern struct acpi_20_facs Facs; extern struct acpi_20_waet Waet; /* * Located at ACPI_INFO_PHYSICAL_ADDRESS. * * This must match the Field("BIOS"....) definition in the DSDT. */ struct acpi_info { uint8_t com1_present:1; /* 0[0] - System has COM1? */ uint8_t com2_present:1; /* 0[1] - System has COM2? */ uint8_t lpt1_present:1; /* 0[2] - System has LPT1? */ uint8_t hpet_present:1; /* 0[3] - System has HPET? */ uint16_t nr_cpus; /* 2 - Number of CPUs */ uint32_t pci_min, pci_len; /* 4, 8 - PCI I/O hole boundaries */ uint32_t madt_csum_addr; /* 12 - Address of MADT checksum */ uint32_t madt_lapic0_addr; /* 16 - Address of first MADT LAPIC struct */ uint32_t vm_gid_addr; /* 20 - Address of VM generation id buffer */ uint64_t pci_hi_min, pci_hi_len; /* 24, 32 - PCI I/O hole boundaries */ }; static void set_checksum( void *table, uint32_t checksum_offset, uint32_t length) { uint8_t *p, sum = 0; p = table; p[checksum_offset] = 0; while ( length-- ) sum = sum + *p++; p = table; p[checksum_offset] = -sum; } static struct acpi_20_madt *construct_madt(struct acpi_ctxt *ctxt, const struct acpi_config *config, struct acpi_info *info) { struct acpi_20_madt *madt; struct acpi_20_madt_intsrcovr *intsrcovr; struct acpi_20_madt_ioapic *io_apic; struct acpi_20_madt_lapic *lapic; const struct hvm_info_table *hvminfo = config->hvminfo; int i, sz; if ( config->lapic_id == NULL ) return NULL; sz = sizeof(struct acpi_20_madt); sz += sizeof(struct acpi_20_madt_intsrcovr) * 16; sz += sizeof(struct acpi_20_madt_ioapic); sz += sizeof(struct acpi_20_madt_lapic) * hvminfo->nr_vcpus; madt = ctxt->mem_ops.alloc(ctxt, sz, 16); if (!madt) return NULL; memset(madt, 0, sizeof(*madt)); madt->header.signature = ACPI_2_0_MADT_SIGNATURE; madt->header.revision = ACPI_2_0_MADT_REVISION; fixed_strcpy(madt->header.oem_id, ACPI_OEM_ID); fixed_strcpy(madt->header.oem_table_id, ACPI_OEM_TABLE_ID); madt->header.oem_revision = ACPI_OEM_REVISION; madt->header.creator_id = ACPI_CREATOR_ID; madt->header.creator_revision = ACPI_CREATOR_REVISION; madt->lapic_addr = config->lapic_base_address; madt->flags = ACPI_PCAT_COMPAT; if ( config->table_flags & ACPI_HAS_IOAPIC ) { intsrcovr = (struct acpi_20_madt_intsrcovr *)(madt + 1); for ( i = 0; i < 16; i++ ) { memset(intsrcovr, 0, sizeof(*intsrcovr)); intsrcovr->type = ACPI_INTERRUPT_SOURCE_OVERRIDE; intsrcovr->length = sizeof(*intsrcovr); intsrcovr->source = i; if ( i == 0 ) { /* ISA IRQ0 routed to IOAPIC GSI 2. */ intsrcovr->gsi = 2; intsrcovr->flags = 0x0; } else if ( config->pci_isa_irq_mask & (1U << i) ) { /* PCI: active-low level-triggered. */ intsrcovr->gsi = i; intsrcovr->flags = 0xf; } else { /* No need for a INT source override structure. */ continue; } intsrcovr++; } io_apic = (struct acpi_20_madt_ioapic *)intsrcovr; memset(io_apic, 0, sizeof(*io_apic)); io_apic->type = ACPI_IO_APIC; io_apic->length = sizeof(*io_apic); io_apic->ioapic_id = config->ioapic_id; io_apic->ioapic_addr = config->ioapic_base_address; lapic = (struct acpi_20_madt_lapic *)(io_apic + 1); } else lapic = (struct acpi_20_madt_lapic *)(madt + 1); info->nr_cpus = hvminfo->nr_vcpus; info->madt_lapic0_addr = ctxt->mem_ops.v2p(ctxt, lapic); for ( i = 0; i < hvminfo->nr_vcpus; i++ ) { memset(lapic, 0, sizeof(*lapic)); lapic->type = ACPI_PROCESSOR_LOCAL_APIC; lapic->length = sizeof(*lapic); /* Processor ID must match processor-object IDs in the DSDT. */ lapic->acpi_processor_id = i; lapic->apic_id = config->lapic_id(i); lapic->flags = (test_bit(i, hvminfo->vcpu_online) ? ACPI_LOCAL_APIC_ENABLED : 0); lapic++; } madt->header.length = (unsigned char *)lapic - (unsigned char *)madt; set_checksum(madt, offsetof(struct acpi_header, checksum), madt->header.length); info->madt_csum_addr = ctxt->mem_ops.v2p(ctxt, &madt->header.checksum); return madt; } static struct acpi_20_hpet *construct_hpet(struct acpi_ctxt *ctxt, const struct acpi_config *config) { struct acpi_20_hpet *hpet; hpet = ctxt->mem_ops.alloc(ctxt, sizeof(*hpet), 16); if (!hpet) return NULL; memset(hpet, 0, sizeof(*hpet)); hpet->header.signature = ACPI_2_0_HPET_SIGNATURE; hpet->header.revision = ACPI_2_0_HPET_REVISION; fixed_strcpy(hpet->header.oem_id, ACPI_OEM_ID); fixed_strcpy(hpet->header.oem_table_id, ACPI_OEM_TABLE_ID); hpet->header.oem_revision = ACPI_OEM_REVISION; hpet->header.creator_id = ACPI_CREATOR_ID; hpet->header.creator_revision = ACPI_CREATOR_REVISION; hpet->timer_block_id = 0x8086a201; hpet->addr.address = ACPI_HPET_ADDRESS; hpet->header.length = sizeof(*hpet); set_checksum(hpet, offsetof(struct acpi_header, checksum), sizeof(*hpet)); return hpet; } static struct acpi_20_waet *construct_waet(struct acpi_ctxt *ctxt, const struct acpi_config *config) { struct acpi_20_waet *waet; waet = ctxt->mem_ops.alloc(ctxt, sizeof(*waet), 16); if (!waet) return NULL; memcpy(waet, &Waet, sizeof(*waet)); waet->header.length = sizeof(*waet); set_checksum(waet, offsetof(struct acpi_header, checksum), sizeof(*waet)); return waet; } static struct acpi_20_srat *construct_srat(struct acpi_ctxt *ctxt, const struct acpi_config *config) { struct acpi_20_srat *srat; struct acpi_20_srat_processor *processor; struct acpi_20_srat_memory *memory; unsigned int size; void *p; unsigned int i; size = sizeof(*srat) + sizeof(*processor) * config->hvminfo->nr_vcpus + sizeof(*memory) * config->numa.nr_vmemranges; p = ctxt->mem_ops.alloc(ctxt, size, 16); if ( !p ) return NULL; srat = memset(p, 0, size); srat->header.signature = ACPI_2_0_SRAT_SIGNATURE; srat->header.revision = ACPI_2_0_SRAT_REVISION; fixed_strcpy(srat->header.oem_id, ACPI_OEM_ID); fixed_strcpy(srat->header.oem_table_id, ACPI_OEM_TABLE_ID); srat->header.oem_revision = ACPI_OEM_REVISION; srat->header.creator_id = ACPI_CREATOR_ID; srat->header.creator_revision = ACPI_CREATOR_REVISION; srat->table_revision = ACPI_SRAT_TABLE_REVISION; processor = (struct acpi_20_srat_processor *)(srat + 1); for ( i = 0; i < config->hvminfo->nr_vcpus; i++ ) { processor->type = ACPI_PROCESSOR_AFFINITY; processor->length = sizeof(*processor); processor->domain = config->numa.vcpu_to_vnode[i]; processor->apic_id = config->lapic_id(i); processor->flags = ACPI_LOCAL_APIC_AFFIN_ENABLED; processor++; } memory = (struct acpi_20_srat_memory *)processor; for ( i = 0; i < config->numa.nr_vmemranges; i++ ) { memory->type = ACPI_MEMORY_AFFINITY; memory->length = sizeof(*memory); memory->domain = config->numa.vmemrange[i].nid; memory->flags = ACPI_MEM_AFFIN_ENABLED; memory->base_address = config->numa.vmemrange[i].start; memory->mem_length = config->numa.vmemrange[i].end - config->numa.vmemrange[i].start; memory++; } ASSERT(((unsigned long)memory) - ((unsigned long)p) == size); srat->header.length = size; set_checksum(srat, offsetof(struct acpi_header, checksum), size); return srat; } static struct acpi_20_slit *construct_slit(struct acpi_ctxt *ctxt, const struct acpi_config *config) { struct acpi_20_slit *slit; unsigned int i, num, size; num = config->numa.nr_vnodes * config->numa.nr_vnodes; size = sizeof(*slit) + num * sizeof(uint8_t); slit = ctxt->mem_ops.alloc(ctxt, size, 16); if ( !slit ) return NULL; memset(slit, 0, size); slit->header.signature = ACPI_2_0_SLIT_SIGNATURE; slit->header.revision = ACPI_2_0_SLIT_REVISION; fixed_strcpy(slit->header.oem_id, ACPI_OEM_ID); fixed_strcpy(slit->header.oem_table_id, ACPI_OEM_TABLE_ID); slit->header.oem_revision = ACPI_OEM_REVISION; slit->header.creator_id = ACPI_CREATOR_ID; slit->header.creator_revision = ACPI_CREATOR_REVISION; for ( i = 0; i < num; i++ ) slit->entry[i] = config->numa.vdistance[i]; slit->localities = config->numa.nr_vnodes; slit->header.length = size; set_checksum(slit, offsetof(struct acpi_header, checksum), size); return slit; } static int construct_passthrough_tables(struct acpi_ctxt *ctxt, unsigned long *table_ptrs, int nr_tables, struct acpi_config *config) { unsigned long pt_addr; struct acpi_header *header; int nr_added; int nr_max = (ACPI_MAX_SECONDARY_TABLES - nr_tables - 1); uint32_t total = 0; uint8_t *buffer; if ( config->pt.addr == 0 ) return 0; pt_addr = config->pt.addr; for ( nr_added = 0; nr_added < nr_max; nr_added++ ) { if ( (config->pt.length - total) < sizeof(struct acpi_header) ) break; header = (struct acpi_header*)pt_addr; buffer = ctxt->mem_ops.alloc(ctxt, header->length, 16); if ( buffer == NULL ) break; memcpy(buffer, header, header->length); table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, buffer); total += header->length; pt_addr += header->length; } return nr_added; } static int construct_secondary_tables(struct acpi_ctxt *ctxt, unsigned long *table_ptrs, struct acpi_config *config, struct acpi_info *info) { int nr_tables = 0; struct acpi_20_madt *madt; struct acpi_20_hpet *hpet; struct acpi_20_waet *waet; struct acpi_20_tcpa *tcpa; unsigned char *ssdt; static const uint16_t tis_signature[] = {0x0001, 0x0001, 0x0001}; void *lasa; /* MADT. */ if ( (config->hvminfo->nr_vcpus > 1) || config->hvminfo->apic_mode ) { madt = construct_madt(ctxt, config, info); if (!madt) return -1; table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, madt); } /* HPET. */ if ( info->hpet_present ) { hpet = construct_hpet(ctxt, config); if (!hpet) return -1; table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, hpet); } /* WAET. */ if ( config->table_flags & ACPI_HAS_WAET ) { waet = construct_waet(ctxt, config); if ( !waet ) return -1; table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, waet); } if ( config->table_flags & ACPI_HAS_SSDT_PM ) { ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_pm), 16); if (!ssdt) return -1; memcpy(ssdt, ssdt_pm, sizeof(ssdt_pm)); table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt); } if ( config->table_flags & ACPI_HAS_SSDT_S3 ) { ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_s3), 16); if (!ssdt) return -1; memcpy(ssdt, ssdt_s3, sizeof(ssdt_s3)); table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt); } else { printf("S3 disabled\n"); } if ( config->table_flags & ACPI_HAS_SSDT_S4 ) { ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_s4), 16); if (!ssdt) return -1; memcpy(ssdt, ssdt_s4, sizeof(ssdt_s4)); table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt); } else { printf("S4 disabled\n"); } if ( config->table_flags & ACPI_HAS_SSDT_LAPTOP_SLATE ) { ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_laptop_slate), 16); if (!ssdt) return -1; memcpy(ssdt, ssdt_laptop_slate, sizeof(ssdt_laptop_slate)); table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt); } else { printf("CONV disabled\n"); } /* TPM TCPA and SSDT. */ if ( (config->table_flags & ACPI_HAS_TCPA) && (config->tis_hdr[0] == tis_signature[0]) && (config->tis_hdr[1] == tis_signature[1]) && (config->tis_hdr[2] == tis_signature[2]) ) { ssdt = ctxt->mem_ops.alloc(ctxt, sizeof(ssdt_tpm), 16); if (!ssdt) return -1; memcpy(ssdt, ssdt_tpm, sizeof(ssdt_tpm)); table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, ssdt); tcpa = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_20_tcpa), 16); if (!tcpa) return -1; memset(tcpa, 0, sizeof(*tcpa)); table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, tcpa); tcpa->header.signature = ACPI_2_0_TCPA_SIGNATURE; tcpa->header.length = sizeof(*tcpa); tcpa->header.revision = ACPI_2_0_TCPA_REVISION; fixed_strcpy(tcpa->header.oem_id, ACPI_OEM_ID); fixed_strcpy(tcpa->header.oem_table_id, ACPI_OEM_TABLE_ID); tcpa->header.oem_revision = ACPI_OEM_REVISION; tcpa->header.creator_id = ACPI_CREATOR_ID; tcpa->header.creator_revision = ACPI_CREATOR_REVISION; if ( (lasa = ctxt->mem_ops.alloc(ctxt, ACPI_2_0_TCPA_LAML_SIZE, 16)) != NULL ) { tcpa->lasa = ctxt->mem_ops.v2p(ctxt, lasa); tcpa->laml = ACPI_2_0_TCPA_LAML_SIZE; memset(lasa, 0, tcpa->laml); set_checksum(tcpa, offsetof(struct acpi_header, checksum), tcpa->header.length); } } /* SRAT and SLIT */ if ( config->numa.nr_vnodes > 0 ) { struct acpi_20_srat *srat = construct_srat(ctxt, config); struct acpi_20_slit *slit = construct_slit(ctxt, config); if ( srat ) table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, srat); else printf("Failed to build SRAT, skipping...\n"); if ( slit ) table_ptrs[nr_tables++] = ctxt->mem_ops.v2p(ctxt, slit); else printf("Failed to build SLIT, skipping...\n"); } /* Load any additional tables passed through. */ nr_tables += construct_passthrough_tables(ctxt, table_ptrs, nr_tables, config); table_ptrs[nr_tables] = 0; return nr_tables; } /** * Allocate and initialize Windows Generation ID * If value is not present in the XenStore or if all zeroes * the device will be not active * * Return 0 if memory failure, != 0 if success */ static int new_vm_gid(struct acpi_ctxt *ctxt, struct acpi_config *config, struct acpi_info *info) { uint64_t *buf; info->vm_gid_addr = 0; /* check for 0 ID*/ if ( !config->vm_gid[0] && !config->vm_gid[1] ) return 1; /* copy to allocate BIOS memory */ buf = ctxt->mem_ops.alloc(ctxt, sizeof(config->vm_gid), 8); if ( !buf ) return 0; memcpy(buf, config->vm_gid, sizeof(config->vm_gid)); /* set the address into ACPI table and also pass it back to the caller */ info->vm_gid_addr = ctxt->mem_ops.v2p(ctxt, buf); config->vm_gid_addr = info->vm_gid_addr; return 1; } int acpi_build_tables(struct acpi_ctxt *ctxt, struct acpi_config *config) { struct acpi_info *acpi_info; struct acpi_20_rsdp *rsdp; struct acpi_20_rsdt *rsdt; struct acpi_20_xsdt *xsdt; struct acpi_fadt *fadt; struct acpi_10_fadt *fadt_10; struct acpi_20_facs *facs; unsigned char *dsdt; unsigned long secondary_tables[ACPI_MAX_SECONDARY_TABLES]; int nr_secondaries, i; unsigned int fadt_size; acpi_info = (struct acpi_info *)config->infop; memset(acpi_info, 0, sizeof(*acpi_info)); acpi_info->com1_present = !!(config->table_flags & ACPI_HAS_COM1); acpi_info->com2_present = !!(config->table_flags & ACPI_HAS_COM2); acpi_info->lpt1_present = !!(config->table_flags & ACPI_HAS_LPT1); acpi_info->hpet_present = !!(config->table_flags & ACPI_HAS_HPET); acpi_info->pci_min = config->pci_start; acpi_info->pci_len = config->pci_len; if ( config->pci_hi_len ) { acpi_info->pci_hi_min = config->pci_hi_start; acpi_info->pci_hi_len = config->pci_hi_len; } /* * Fill in high-memory data structures, starting at @buf. */ facs = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_20_facs), 16); if (!facs) goto oom; memcpy(facs, &Facs, sizeof(struct acpi_20_facs)); /* * Alternative DSDTs we get linked against. A cover-all DSDT for up to the * implementation-defined maximum number of VCPUs, and an alternative for use * when a guest can only have up to 15 VCPUs. * * The latter is required for Windows 2000, which experiences a BSOD of * KMODE_EXCEPTION_NOT_HANDLED if it sees more than 15 processor objects. */ if ( config->hvminfo->nr_vcpus <= 15 && config->dsdt_15cpu) { dsdt = ctxt->mem_ops.alloc(ctxt, config->dsdt_15cpu_len, 16); if (!dsdt) goto oom; memcpy(dsdt, config->dsdt_15cpu, config->dsdt_15cpu_len); } else { dsdt = ctxt->mem_ops.alloc(ctxt, config->dsdt_anycpu_len, 16); if (!dsdt) goto oom; memcpy(dsdt, config->dsdt_anycpu, config->dsdt_anycpu_len); } /* * N.B. ACPI 1.0 operating systems may not handle FADT with revision 2 * or above properly, notably Windows 2000, which tries to copy FADT * into a 116 bytes buffer thus causing an overflow. The solution is to * link the higher revision FADT with the XSDT only and introduce a * compatible revision 1 FADT that is linked with the RSDT. Refer to: * http://www.acpi.info/presentations/S01USMOBS169_OS%20new.ppt */ fadt_10 = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_10_fadt), 16); if (!fadt_10) goto oom; memcpy(fadt_10, &Fadt, sizeof(struct acpi_10_fadt)); fadt_10->header.length = sizeof(struct acpi_10_fadt); fadt_10->header.revision = ACPI_1_0_FADT_REVISION; fadt_10->dsdt = ctxt->mem_ops.v2p(ctxt, dsdt); fadt_10->firmware_ctrl = ctxt->mem_ops.v2p(ctxt, facs); set_checksum(fadt_10, offsetof(struct acpi_header, checksum), sizeof(struct acpi_10_fadt)); switch ( config->acpi_revision ) { case 4: /* * NB: we can use offsetof because there's no padding between * x_gpe1_blk and sleep_control. */ fadt_size = offsetof(struct acpi_fadt, sleep_control); break; case 5: fadt_size = sizeof(*fadt); break; default: printf("ACPI revision %u not supported\n", config->acpi_revision); return -1; } fadt = ctxt->mem_ops.alloc(ctxt, fadt_size, 16); if (!fadt) goto oom; if ( !(config->table_flags & ACPI_HAS_PMTIMER) ) { Fadt.pm_tmr_blk = Fadt.pm_tmr_len = 0; memset(&Fadt.x_pm_tmr_blk, 0, sizeof(Fadt.x_pm_tmr_blk)); } if ( !(config->table_flags & ACPI_HAS_BUTTONS) ) Fadt.flags |= (ACPI_PWR_BUTTON | ACPI_SLP_BUTTON); memcpy(fadt, &Fadt, fadt_size); /* * For both ACPI 4 and 5 the revision of the FADT matches the ACPI * revision. */ fadt->header.revision = config->acpi_revision; fadt->header.length = fadt_size; fadt->dsdt = ctxt->mem_ops.v2p(ctxt, dsdt); fadt->x_dsdt = ctxt->mem_ops.v2p(ctxt, dsdt); fadt->firmware_ctrl = ctxt->mem_ops.v2p(ctxt, facs); fadt->x_firmware_ctrl = ctxt->mem_ops.v2p(ctxt, facs); if ( !(config->table_flags & ACPI_HAS_VGA) ) fadt->iapc_boot_arch |= ACPI_FADT_NO_VGA; if ( config->table_flags & ACPI_HAS_8042 ) fadt->iapc_boot_arch |= ACPI_FADT_8042; if ( !(config->table_flags & ACPI_HAS_CMOS_RTC) ) { if ( fadt->header.revision < 5 ) { printf("ACPI_FADT_NO_CMOS_RTC requires FADT revision 5\n"); return -1; } fadt->iapc_boot_arch |= ACPI_FADT_NO_CMOS_RTC; } set_checksum(fadt, offsetof(struct acpi_header, checksum), fadt_size); nr_secondaries = construct_secondary_tables(ctxt, secondary_tables, config, acpi_info); if ( nr_secondaries < 0 ) goto oom; xsdt = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_20_xsdt) + sizeof(uint64_t) * nr_secondaries, 16); if (!xsdt) goto oom; memcpy(xsdt, &Xsdt, sizeof(struct acpi_header)); xsdt->entry[0] = ctxt->mem_ops.v2p(ctxt, fadt); for ( i = 0; secondary_tables[i]; i++ ) xsdt->entry[i+1] = secondary_tables[i]; xsdt->header.length = sizeof(struct acpi_header) + (i+1)*sizeof(uint64_t); set_checksum(xsdt, offsetof(struct acpi_header, checksum), xsdt->header.length); rsdt = ctxt->mem_ops.alloc(ctxt, sizeof(struct acpi_20_rsdt) + sizeof(uint32_t) * nr_secondaries, 16); if (!rsdt) goto oom; memcpy(rsdt, &Rsdt, sizeof(struct acpi_header)); rsdt->entry[0] = ctxt->mem_ops.v2p(ctxt, fadt_10); for ( i = 0; secondary_tables[i]; i++ ) rsdt->entry[i+1] = secondary_tables[i]; rsdt->header.length = sizeof(struct acpi_header) + (i+1)*sizeof(uint32_t); set_checksum(rsdt, offsetof(struct acpi_header, checksum), rsdt->header.length); /* * Fill in low-memory data structures: acpi_info and RSDP. */ rsdp = (struct acpi_20_rsdp *)config->rsdp; memcpy(rsdp, &Rsdp, sizeof(struct acpi_20_rsdp)); rsdp->rsdt_address = ctxt->mem_ops.v2p(ctxt, rsdt); rsdp->xsdt_address = ctxt->mem_ops.v2p(ctxt, xsdt); set_checksum(rsdp, offsetof(struct acpi_10_rsdp, checksum), sizeof(struct acpi_10_rsdp)); set_checksum(rsdp, offsetof(struct acpi_20_rsdp, extended_checksum), sizeof(struct acpi_20_rsdp)); if ( !new_vm_gid(ctxt, config, acpi_info) ) goto oom; return 0; oom: printf("unable to build ACPI tables: out of memory\n"); return -1; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/configure0000775000175000017500000114364213256712137013537 0ustar smbsmb#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for Xen Hypervisor Tools 4.9. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: xen-devel@lists.xen.org about your system, including $0: any error possibly output before this message. Then $0: install a modern shell, or manually run the script $0: under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Xen Hypervisor Tools' PACKAGE_TARNAME='xen' PACKAGE_VERSION='4.9' PACKAGE_STRING='Xen Hypervisor Tools 4.9' PACKAGE_BUGREPORT='xen-devel@lists.xen.org' PACKAGE_URL='http://www.xen.org/' ac_unique_file="libxl/libxl.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS SYSTEMD_LIBS SYSTEMD_CFLAGS SYSTEMD_MODULES_LOAD SYSTEMD_DIR systemd libnl LIBNL3_LIBS LIBNL3_CFLAGS argp_ldflags libiconv PTYFUNCS_LIBS PTHREAD_LIBS PTHREAD_LDFLAGS PTHREAD_CFLAGS libgcrypt EXTFS_LIBS system_aio zlib FETCHER FTP WGET pixman_LIBS pixman_CFLAGS glib_LIBS glib_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG TINFO_LIBS CURSES_LIBS PY_NOOPT_CFLAGS EGREP GREP CPP pyconfig PYTHONPATH CHECKPOLICY XENSTORED OCAMLFIND OCAMLBUILD OCAMLDOC OCAMLMKLIB OCAMLMKTOP OCAMLDEP OCAML OCAMLOPTDOTOPT OCAMLCDOTOPT OCAMLBEST OCAMLOPT OCAMLLIB OCAMLVERSION OCAMLC INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM SET_MAKE AWK IASL XGETTEXT BASH XML CURL FLEX BISON PERL PYTHON APPEND_LIB APPEND_INCLUDES PREPEND_LIB PREPEND_INCLUDES EXTRA_QEMUU_CONFIGURE_ARGS qemu_xen_systemd qemu_xen_path qemu_xen rombios BCC LD86 AS86 qemu_traditional blktap2 LINUX_BACKEND_MODULES seabios ovmf xsmpolicy ocamltools monitors githttp rpath XEN_DUMP_DIR XEN_PAGING_DIR XEN_LOCK_DIR XEN_SCRIPT_DIR XEN_CONFIG_DIR INITD_DIR CONFIG_DIR SHAREDIR XEN_LIB_DIR XEN_RUN_STORED XEN_LIB_STORED XEN_LOG_DIR XEN_RUN_DIR XENFIRMWAREDIR LIBEXEC_INC LIBEXEC_LIB LIBEXEC_BIN LIBEXEC CONFIG_LEAF_DIR XENSTORED_PORT XENSTORED_KVA FILE_OFFSET_BITS OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC CONFIG_RUMP host_os host_vendor host_cpu host build_os build_vendor build_cpu build target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_largefile with_initddir with_sysconfig_leaf_dir with_libexec_leaf_dir with_xen_dumpdir with_rundir enable_rpath enable_githttp enable_monitors enable_ocamltools enable_xsmpolicy enable_ovmf enable_seabios with_linux_backend_modules enable_blktap2 enable_qemu_traditional enable_rombios with_system_qemu with_system_seabios with_system_ovmf with_extra_qemuu_configure_args with_xenstored enable_systemd with_systemd with_systemd_modules_load ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS PREPEND_INCLUDES PREPEND_LIB APPEND_INCLUDES APPEND_LIB PYTHON PERL BISON FLEX CURL XML BASH XGETTEXT AS86 LD86 BCC IASL AWK CPP PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR glib_CFLAGS glib_LIBS pixman_CFLAGS pixman_LIBS LIBNL3_CFLAGS LIBNL3_LIBS SYSTEMD_CFLAGS SYSTEMD_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures Xen Hypervisor Tools 4.9 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/xen] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of Xen Hypervisor Tools 4.9:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-largefile omit support for large files --enable-rpath Build tools with -Wl,-rpath,LIBDIR (default is DISABLED) --enable-githttp Download GIT repositories via HTTP (default is DISABLED) --disable-monitors Disable xenstat and xentop monitoring tools (default is ENABLED) --disable-ocamltools Disable Ocaml tools (default is ENABLED) --disable-xsmpolicy Disable XSM policy compilation (default is ENABLED) --enable-ovmf Enable OVMF (default is DISABLED) --disable-seabios Disable SeaBIOS (default is ENABLED) --enable-blktap2 Enable blktap2, (DEFAULT is on for Linux, otherwise off) --enable-qemu-traditional Enable qemu traditional device model, (DEFAULT is on for Linux or NetBSD x86, otherwise off) --enable-rombios Enable ROMBIOS, (DEFAULT is on if qemu-traditional is enabled, otherwise off) --enable-systemd Enable systemd support (default is DISABLED) Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-initddir=DIR Path to directory with sysv runlevel scripts. [SYSCONFDIR/init.d] --with-sysconfig-leaf-dir=SUBDIR Name of subdirectory in /etc to store runtime options for runlevel scripts and daemons such as xenstored. This should be either "sysconfig" or "default". [sysconfig] --with-libexec-leaf-dir=SUBDIR Name of subdirectory in libexecdir to use. --with-xen-dumpdir=DIR Path to directory for domU crash dumps. [LOCALSTATEDIR/lib/xen/dump] --with-rundir=DIR Path to directory for runtime data. [LOCALSTATEDIR/run] --with-linux-backend-modules="mod1 mod2" List of Linux backend module or modalias names to be autoloaded on startup. --with-system-qemu[=PATH] Use system supplied qemu PATH or qemu (taken from $PATH) as qemu-xen device model instead of building and installing our own version --with-system-seabios[=PATH] Use system supplied seabios PATH instead of building and installing our own version --with-system-ovmf[=PATH] Use system supplied OVMF PATH instead of building and installing our own version --with-extra-qemuu-configure-args[="--ARG1 ..."] List of additional configure options for upstream qemu --with-xenstored[=oxenstored|xenstored] This lets you choose which xenstore daemon you want, you have two options: the original xenstored written in C (xenstored) or the newer and robust one written in Ocaml (oxenstored). The oxenstored daemon is the default but can only be used if you have ocaml library / build dependencies solved, if you have not specified a preference and do not have ocaml dependencies resolved we'll enable the C xenstored for you. If you ask for oxenstored we'll complain until you resolve those dependencies --with-systemd=DIR set directory for systemd service files [PREFIX/lib/systemd/system] --with-systemd-modules-load=DIR set directory for systemd modules load files [PREFIX/lib/modules-load.d/] Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory PREPEND_INCLUDES List of include folders to prepend to CFLAGS (without -I) PREPEND_LIB List of library folders to prepend to LDFLAGS (without -L) APPEND_INCLUDES List of include folders to append to CFLAGS (without -I) APPEND_LIB List of library folders to append to LDFLAGS (without -L) PYTHON Path to the Python parser PERL Path to Perl parser BISON Path to Bison parser generator FLEX Path to Flex lexical analyser generator CURL Path to curl-config tool XML Path to xml2-config tool BASH Path to bash shell XGETTEXT Path to xgetttext tool AS86 Path to as86 tool LD86 Path to ld86 tool BCC Path to bcc tool IASL Path to iasl tool AWK Path to awk tool CPP C preprocessor PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path glib_CFLAGS C compiler flags for glib, overriding pkg-config glib_LIBS linker flags for glib, overriding pkg-config pixman_CFLAGS C compiler flags for pixman, overriding pkg-config pixman_LIBS linker flags for pixman, overriding pkg-config LIBNL3_CFLAGS C compiler flags for LIBNL3, overriding pkg-config LIBNL3_LIBS linker flags for LIBNL3, overriding pkg-config SYSTEMD_CFLAGS C compiler flags for SYSTEMD, overriding pkg-config SYSTEMD_LIBS linker flags for SYSTEMD, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . Xen Hypervisor Tools home page: . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF Xen Hypervisor Tools configure 4.9 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## -------------------------------------- ## ## Report this to xen-devel@lists.xen.org ## ## -------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by Xen Hypervisor Tools $as_me 4.9, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_files="$ac_config_files ../config/Tools.mk hotplug/FreeBSD/rc.d/xencommons hotplug/FreeBSD/rc.d/xendriverdomain hotplug/Linux/init.d/sysconfig.xencommons hotplug/Linux/init.d/sysconfig.xendomains hotplug/Linux/init.d/xen-watchdog hotplug/Linux/init.d/xencommons hotplug/Linux/init.d/xendomains hotplug/Linux/init.d/xendriverdomain hotplug/Linux/launch-xenstore hotplug/Linux/vif-setup hotplug/Linux/xen-hotplug-common.sh hotplug/Linux/xendomains hotplug/NetBSD/rc.d/xencommons hotplug/NetBSD/rc.d/xendriverdomain ocaml/xenstored/oxenstored.conf" ac_config_headers="$ac_config_headers config.h" ac_aux_dir= for ac_dir in ../ "$srcdir"/../; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in ../ \"$srcdir\"/../" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Check if CFLAGS, LDFLAGS, LIBS, CPPFLAGS or CPP is set and print a warning if test -n "$CC$CFLAGS$LDFLAGS$LIBS$CPPFLAGS$CPP"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Setting CC, CFLAGS, LDFLAGS, LIBS, CPPFLAGS or CPP is not \ recommended, use PREPEND_INCLUDES, PREPEND_LIB, \ APPEND_INCLUDES and APPEND_LIB instead when possible." >&5 $as_echo "$as_me: WARNING: Setting CC, CFLAGS, LDFLAGS, LIBS, CPPFLAGS or CPP is not \ recommended, use PREPEND_INCLUDES, PREPEND_LIB, \ APPEND_INCLUDES and APPEND_LIB instead when possible." >&2;} fi # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac case $host_vendor in rumpxen|rumprun) CONFIG_RUMP=y; rump=true ;; *) CONFIG_RUMP=n; rump=false ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if ${ac_cv_sys_largefile_CC+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if ${ac_cv_sys_file_offset_bits+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if ${ac_cv_sys_large_files+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits ;; esac # M4 Macro includes # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 1 (pkg-config-0.24) # # Copyright © 2004 Scott James Remnant . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; If not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- # PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) # only at the first occurence in configure.ac, so if the first place # it's called might be skipped (such as if it is within an "if", you # have to call PKG_CHECK_EXISTS manually # -------------------------------------------------------------- # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- # _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- # _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- # PKG_CHECK_MODULES # PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- # Checks for existence of MODULES and gathers its build flags with # static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags # and VARIABLE-PREFIX_LIBS from --libs. # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES_STATIC might not happen, you should be sure to include # an explicit call to PKG_PROG_PKG_CONFIG in your configure.ac. # PKG_INSTALLDIR(DIRECTORY) # ------------------------- # Substitutes the variable pkgconfigdir as the location where a module # should install pkg-config .pc files. By default the directory is # $libdir/pkgconfig, but the default can be changed by passing # DIRECTORY. The user can override through the --with-pkgconfigdir # parameter. # PKG_NOARCH_INSTALLDIR(DIRECTORY) # ------------------------- # Substitutes the variable noarch_pkgconfigdir as the location where a # module should install arch-independent pkg-config .pc files. By # default the directory is $datadir/pkgconfig, but the default can be # changed by passing DIRECTORY. The user can override through the # --with-noarch-pkgconfigdir parameter. # PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------- # Retrieves the value of the pkg-config variable for the given module. # PKG_CHECK_VAR # We define, separately, PTHREAD_CFLAGS, _LDFLAGS and _LIBS # even though currently we don't set them very separately. # This means that the makefiles will not need to change in # the future if we make the test more sophisticated. # We invoke AX_PTHREAD_VARS with the name of another macro # which is then expanded once for each variable. # Fetched from http://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=blob_plain;f=m4/ax_compare_version.m4 # Commit ID: 27948f49ca30e4222bb7cdd55182bd7341ac50c5 # =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_compare_version.html # =========================================================================== # # SYNOPSIS # # AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # # DESCRIPTION # # This macro compares two version strings. Due to the various number of # minor-version numbers that can exist, and the fact that string # comparisons are not compatible with numeric comparisons, this is not # necessarily trivial to do in a autoconf script. This macro makes doing # these comparisons easy. # # The six basic comparisons are available, as well as checking equality # limited to a certain number of minor-version levels. # # The operator OP determines what type of comparison to do, and can be one # of: # # eq - equal (test A == B) # ne - not equal (test A != B) # le - less than or equal (test A <= B) # ge - greater than or equal (test A >= B) # lt - less than (test A < B) # gt - greater than (test A > B) # # Additionally, the eq and ne operator can have a number after it to limit # the test to that number of minor versions. # # eq0 - equal up to the length of the shorter version # ne0 - not equal up to the length of the shorter version # eqN - equal up to N sub-version levels # neN - not equal up to N sub-version levels # # When the condition is true, shell commands ACTION-IF-TRUE are run, # otherwise shell commands ACTION-IF-FALSE are run. The environment # variable 'ax_compare_version' is always set to either 'true' or 'false' # as well. # # Examples: # # AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) # AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) # # would both be true. # # AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) # AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) # # would both be false. # # AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) # # would be true because it is only comparing two minor versions. # # AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) # # would be true because it is only comparing the lesser number of minor # versions of the two values. # # Note: The characters that separate the version numbers do not matter. An # empty string is the same as version 0. OP is evaluated by autoconf, not # configure, so must be a string, not a variable. # # The author would like to acknowledge Guido Draheim whose advice about # the m4_case and m4_ifvaln functions make this macro only include the # portions necessary to perform the specific comparison specified by the # OP argument in the final configure script. # # LICENSE # # Copyright (c) 2008 Tim Toolan # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 11 case "$host_os" in *freebsd*) XENSTORED_KVA=/dev/xen/xenstored ;; *) XENSTORED_KVA=/proc/xen/xsd_kva ;; esac case "$host_os" in *freebsd*) XENSTORED_PORT=/dev/xen/xenstored ;; *) XENSTORED_PORT=/proc/xen/xsd_port ;; esac # systemd.m4 - Macros to check for and enable systemd -*- Autoconf -*- # # Copyright (C) 2014 Luis R. Rodriguez # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; If not, see . test "x$prefix" = "xNONE" && prefix=$ac_default_prefix test "x$exec_prefix" = "xNONE" && exec_prefix=${prefix} if test "$localstatedir" = '${prefix}/var' ; then localstatedir=/var fi bindir=`eval echo $bindir` sbindir=`eval echo $sbindir` libdir=`eval echo $libdir` if test "x$sysconfdir" = 'x${prefix}/etc' ; then case "$host_os" in *freebsd*) sysconfdir=$prefix/etc ;; *solaris*) if test "$prefix" = "/usr" ; then sysconfdir=/etc else sysconfdir=$prefix/etc fi ;; *) sysconfdir=/etc ;; esac fi # Check whether --with-initddir was given. if test "${with_initddir+set}" = set; then : withval=$with_initddir; initddir_path=$withval else case "$host_os" in *linux*) if test -d $sysconfdir/rc.d/init.d ; then initddir_path=$sysconfdir/rc.d/init.d else initddir_path=$sysconfdir/init.d fi ;; *) initddir_path=$sysconfdir/rc.d ;; esac fi # Check whether --with-sysconfig-leaf-dir was given. if test "${with_sysconfig_leaf_dir+set}" = set; then : withval=$with_sysconfig_leaf_dir; config_leaf_dir=$withval else config_leaf_dir=sysconfig if test ! -d /etc/sysconfig ; then config_leaf_dir=default ; fi fi CONFIG_LEAF_DIR=$config_leaf_dir # Check whether --with-libexec-leaf-dir was given. if test "${with_libexec_leaf_dir+set}" = set; then : withval=$with_libexec_leaf_dir; libexec_subdir=$withval else libexec_subdir=$PACKAGE_TARNAME fi # Check whether --with-xen-dumpdir was given. if test "${with_xen_dumpdir+set}" = set; then : withval=$with_xen_dumpdir; xen_dumpdir_path=$withval else xen_dumpdir_path=$localstatedir/lib/xen/dump fi # Check whether --with-rundir was given. if test "${with_rundir+set}" = set; then : withval=$with_rundir; rundir_path=$withval else rundir_path=$localstatedir/run fi if test "$libexecdir" = '${exec_prefix}/libexec' ; then case "$host_os" in *netbsd*) ;; *) libexecdir='${exec_prefix}/lib' ;; esac fi LIBEXEC=`eval echo $libexecdir/$libexec_subdir` LIBEXEC_BIN=${LIBEXEC}/bin LIBEXEC_LIB=${LIBEXEC}/lib LIBEXEC_INC=${LIBEXEC}/include XENFIRMWAREDIR=${LIBEXEC}/boot XEN_RUN_DIR=$rundir_path/xen XEN_LOG_DIR=$localstatedir/log/xen XEN_LIB_STORED=$localstatedir/lib/xenstored XEN_RUN_STORED=$rundir_path/xenstored XEN_LIB_DIR=$localstatedir/lib/xen SHAREDIR=$prefix/share CONFIG_DIR=$sysconfdir INITD_DIR=$initddir_path XEN_CONFIG_DIR=$CONFIG_DIR/xen XEN_SCRIPT_DIR=$XEN_CONFIG_DIR/scripts case "$host_os" in *freebsd*) XEN_LOCK_DIR=$localstatedir/lib ;; *netbsd*) XEN_LOCK_DIR=$localstatedir/lib ;; *) XEN_LOCK_DIR=$localstatedir/lock ;; esac XEN_PAGING_DIR=$localstatedir/lib/xen/xenpaging XEN_DUMP_DIR=$xen_dumpdir_path # Enable/disable options # Check whether --enable-rpath was given. if test "${enable_rpath+set}" = set; then : enableval=$enable_rpath; fi if test "x$enable_rpath" = "xno"; then : ax_cv_rpath="n" elif test "x$enable_rpath" = "xyes"; then : ax_cv_rpath="y" elif test -z $ax_cv_rpath; then : ax_cv_rpath="n" fi rpath=$ax_cv_rpath # Check whether --enable-githttp was given. if test "${enable_githttp+set}" = set; then : enableval=$enable_githttp; fi if test "x$enable_githttp" = "xno"; then : ax_cv_githttp="n" elif test "x$enable_githttp" = "xyes"; then : ax_cv_githttp="y" elif test -z $ax_cv_githttp; then : ax_cv_githttp="n" fi githttp=$ax_cv_githttp # Check whether --enable-monitors was given. if test "${enable_monitors+set}" = set; then : enableval=$enable_monitors; fi if test "x$enable_monitors" = "xno"; then : ax_cv_monitors="n" elif test "x$enable_monitors" = "xyes"; then : ax_cv_monitors="y" elif test -z $ax_cv_monitors; then : ax_cv_monitors="y" fi monitors=$ax_cv_monitors # Check whether --enable-ocamltools was given. if test "${enable_ocamltools+set}" = set; then : enableval=$enable_ocamltools; fi if test "x$enable_ocamltools" = "xno"; then : ax_cv_ocamltools="n" elif test "x$enable_ocamltools" = "xyes"; then : ax_cv_ocamltools="y" elif test -z $ax_cv_ocamltools; then : ax_cv_ocamltools="y" fi ocamltools=$ax_cv_ocamltools # Check whether --enable-xsmpolicy was given. if test "${enable_xsmpolicy+set}" = set; then : enableval=$enable_xsmpolicy; fi if test "x$enable_xsmpolicy" = "xno"; then : ax_cv_xsmpolicy="n" elif test "x$enable_xsmpolicy" = "xyes"; then : ax_cv_xsmpolicy="y" elif test -z $ax_cv_xsmpolicy; then : ax_cv_xsmpolicy="y" fi xsmpolicy=$ax_cv_xsmpolicy # Check whether --enable-ovmf was given. if test "${enable_ovmf+set}" = set; then : enableval=$enable_ovmf; fi if test "x$enable_ovmf" = "xno"; then : ax_cv_ovmf="n" elif test "x$enable_ovmf" = "xyes"; then : ax_cv_ovmf="y" elif test -z $ax_cv_ovmf; then : ax_cv_ovmf="n" fi ovmf=$ax_cv_ovmf # Check whether --enable-seabios was given. if test "${enable_seabios+set}" = set; then : enableval=$enable_seabios; fi if test "x$enable_seabios" = "xno"; then : ax_cv_seabios="n" elif test "x$enable_seabios" = "xyes"; then : ax_cv_seabios="y" elif test -z $ax_cv_seabios; then : ax_cv_seabios="y" fi seabios=$ax_cv_seabios # Check whether --with-linux-backend-modules was given. if test "${with_linux_backend_modules+set}" = set; then : withval=$with_linux_backend_modules; LINUX_BACKEND_MODULES="$withval" else case "$host_os" in *linux*) LINUX_BACKEND_MODULES=" xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback evtchn gntdev netbk blkbk xen-scsibk usbbk pciback xen-acpi-processor blktap2 " ;; *) LINUX_BACKEND_MODULES= ;; esac fi LINUX_BACKEND_MODULES="`eval echo $LINUX_BACKEND_MODULES`" # Check whether --enable-blktap2 was given. if test "${enable_blktap2+set}" = set; then : enableval=$enable_blktap2; else case "$host_os" in linux*) enable_blktap2="yes";; *) enable_blktap2="no";; esac fi if test "x$enable_blktap2" = "xyes"; then : $as_echo "#define HAVE_BLKTAP2 1" >>confdefs.h blktap2=y else blktap2=n fi # Check whether --enable-qemu-traditional was given. if test "${enable_qemu_traditional+set}" = set; then : enableval=$enable_qemu_traditional; else case "$host_cpu" in i[3456]86|x86_64) enable_qemu_traditional="yes";; *) enable_qemu_traditional="no";; esac case "$host_os" in freebsd*) enable_qemu_traditional="no";; esac fi if test "x$enable_qemu_traditional" = "xyes"; then : $as_echo "#define HAVE_QEMU_TRADITIONAL 1" >>confdefs.h qemu_traditional=y else qemu_traditional=n fi # Check whether --enable-rombios was given. if test "${enable_rombios+set}" = set; then : enableval=$enable_rombios; else if test "x$enable_qemu_traditional" = "xyes"; then : enable_rombios="yes" else enable_rombios="no" fi fi if test "x$enable_rombios" = "xyes"; then : # Extract the first word of "as86", so it can be a program name with args. set dummy as86; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_AS86+:} false; then : $as_echo_n "(cached) " >&6 else case $AS86 in [\\/]* | ?:[\\/]*) ac_cv_path_AS86="$AS86" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_AS86="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_AS86" && ac_cv_path_AS86="no" ;; esac fi AS86=$ac_cv_path_AS86 if test -n "$AS86"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AS86" >&5 $as_echo "$AS86" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"${AS86}" = x"no" then as_fn_error $? "Unable to find as86, please install as86" "$LINENO" 5 fi # Extract the first word of "ld86", so it can be a program name with args. set dummy ld86; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_LD86+:} false; then : $as_echo_n "(cached) " >&6 else case $LD86 in [\\/]* | ?:[\\/]*) ac_cv_path_LD86="$LD86" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_LD86="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_LD86" && ac_cv_path_LD86="no" ;; esac fi LD86=$ac_cv_path_LD86 if test -n "$LD86"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD86" >&5 $as_echo "$LD86" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"${LD86}" = x"no" then as_fn_error $? "Unable to find ld86, please install ld86" "$LINENO" 5 fi # Extract the first word of "bcc", so it can be a program name with args. set dummy bcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_BCC+:} false; then : $as_echo_n "(cached) " >&6 else case $BCC in [\\/]* | ?:[\\/]*) ac_cv_path_BCC="$BCC" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_BCC="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_BCC" && ac_cv_path_BCC="no" ;; esac fi BCC=$ac_cv_path_BCC if test -n "$BCC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BCC" >&5 $as_echo "$BCC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"${BCC}" = x"no" then as_fn_error $? "Unable to find bcc, please install bcc" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lzma_version_number in -llzma" >&5 $as_echo_n "checking for lzma_version_number in -llzma... " >&6; } if ${ac_cv_lib_lzma_lzma_version_number+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llzma $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lzma_version_number (); int main () { return lzma_version_number (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lzma_lzma_version_number=yes else ac_cv_lib_lzma_lzma_version_number=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lzma_lzma_version_number" >&5 $as_echo "$ac_cv_lib_lzma_lzma_version_number" >&6; } if test "x$ac_cv_lib_lzma_lzma_version_number" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBLZMA 1 _ACEOF LIBS="-llzma $LIBS" else as_fn_error $? "Could not find lzma, needed to build rombios" "$LINENO" 5 fi $as_echo "#define HAVE_ROMBIOS 1" >>confdefs.h rombios=y else rombios=n fi # Check whether --with-system-qemu was given. if test "${with_system_qemu+set}" = set; then : withval=$with_system_qemu; case $withval in yes) qemu_xen=n ; qemu_xen_path="qemu-system-i386" qemu_xen_systemd="/usr/bin/env $qemu_xen_path" ;; no) qemu_xen=y ;; *) qemu_xen=n ; qemu_xen_path="$withval" ; qemu_xen_systemd="$qemu_xen_path" ;; esac else case "$host_cpu" in i[3456]86|x86_64) qemu_xen=y;; arm*|aarch64) qemu_xen=y;; *) qemu_xen=n;; esac fi if test "x$qemu_xen" = "xy"; then : qemu_xen_path="$LIBEXEC_BIN/qemu-system-i386" qemu_xen_systemd="$qemu_xen_path" fi cat >>confdefs.h <<_ACEOF #define QEMU_XEN_PATH "$qemu_xen_path" _ACEOF # Check whether --with-system-seabios was given. if test "${with_system_seabios+set}" = set; then : withval=$with_system_seabios; # Disable compilation of SeaBIOS. seabios=n case $withval in no) seabios_path= ;; *) seabios_path=$withval ;; esac fi if test "x$seabios" = "xy" -o -n "$seabios_path" ; then : cat >>confdefs.h <<_ACEOF #define SEABIOS_PATH "${seabios_path:-$XENFIRMWAREDIR/seabios.bin}" _ACEOF fi # Check whether --with-system-ovmf was given. if test "${with_system_ovmf+set}" = set; then : withval=$with_system_ovmf; # Disable compilation of OVMF. ovmf=n case $withval in no) ovmf_path= ;; *) ovmf_path=$withval ;; esac fi if test "x$ovmf" = "xy" -o -n "$ovmf_path" ; then : cat >>confdefs.h <<_ACEOF #define OVMF_PATH "${ovmf_path:-$XENFIRMWAREDIR/ovmf.bin}" _ACEOF fi # Check whether --with-extra-qemuu-configure-args was given. if test "${with_extra_qemuu_configure_args+set}" = set; then : withval=$with_extra_qemuu_configure_args; case $withval in no) EXTRA_QEMUU_CONFIGURE_ARGS= ;; *) EXTRA_QEMUU_CONFIGURE_ARGS=$withval ;; esac fi for cppflag in $PREPEND_INCLUDES do PREPEND_CPPFLAGS="$PREPEND_CPPFLAGS -I$cppflag" done for ldflag in $PREPEND_LIB do PREPEND_LDFLAGS="$PREPEND_LDFLAGS -L$ldflag" done for cppflag in $APPEND_INCLUDES do APPEND_CPPFLAGS="$APPEND_CPPFLAGS -I$cppflag" done for ldflag in $APPEND_LIB do APPEND_LDFLAGS="$APPEND_LDFLAGS -L$ldflag" done CPPFLAGS="$PREPEND_CPPFLAGS $CPPFLAGS $APPEND_CPPFLAGS" LDFLAGS="$PREPEND_LDFLAGS $LDFLAGS $APPEND_LDFLAGS" # Checks for programs. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # Extract the first word of "bison", so it can be a program name with args. set dummy bison; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_BISON+:} false; then : $as_echo_n "(cached) " >&6 else case $BISON in [\\/]* | ?:[\\/]*) ac_cv_path_BISON="$BISON" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_BISON="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi BISON=$ac_cv_path_BISON if test -n "$BISON"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BISON" >&5 $as_echo "$BISON" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "flex", so it can be a program name with args. set dummy flex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_FLEX+:} false; then : $as_echo_n "(cached) " >&6 else case $FLEX in [\\/]* | ?:[\\/]*) ac_cv_path_FLEX="$FLEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_FLEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi FLEX=$ac_cv_path_FLEX if test -n "$FLEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FLEX" >&5 $as_echo "$FLEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "perl", so it can be a program name with args. set dummy perl; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PERL+:} false; then : $as_echo_n "(cached) " >&6 else case $PERL in [\\/]* | ?:[\\/]*) ac_cv_path_PERL="$PERL" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_PERL" && ac_cv_path_PERL="no" ;; esac fi PERL=$ac_cv_path_PERL if test -n "$PERL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5 $as_echo "$PERL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"${PERL}" = x"no" then as_fn_error $? "Unable to find perl, please install perl" "$LINENO" 5 fi # Extract the first word of "awk", so it can be a program name with args. set dummy awk; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_AWK+:} false; then : $as_echo_n "(cached) " >&6 else case $AWK in [\\/]* | ?:[\\/]*) ac_cv_path_AWK="$AWK" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_AWK="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_AWK" && ac_cv_path_AWK="no" ;; esac fi AWK=$ac_cv_path_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"${AWK}" = x"no" then as_fn_error $? "Unable to find awk, please install awk" "$LINENO" 5 fi # checking for ocamlc if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlc", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamlc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLC"; then ac_cv_prog_OCAMLC="$OCAMLC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLC="${ac_tool_prefix}ocamlc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLC=$ac_cv_prog_OCAMLC if test -n "$OCAMLC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLC" >&5 $as_echo "$OCAMLC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLC"; then ac_ct_OCAMLC=$OCAMLC # Extract the first word of "ocamlc", so it can be a program name with args. set dummy ocamlc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLC"; then ac_cv_prog_ac_ct_OCAMLC="$ac_ct_OCAMLC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLC="ocamlc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLC=$ac_cv_prog_ac_ct_OCAMLC if test -n "$ac_ct_OCAMLC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLC" >&5 $as_echo "$ac_ct_OCAMLC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLC" = x; then OCAMLC="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLC=$ac_ct_OCAMLC fi else OCAMLC="$ac_cv_prog_OCAMLC" fi if test "$OCAMLC" != "no"; then OCAMLVERSION=`$OCAMLC -v | sed -n -e 's|.*version* *\(.*\)$|\1|p'` { $as_echo "$as_me:${as_lineno-$LINENO}: result: OCaml version is $OCAMLVERSION" >&5 $as_echo "OCaml version is $OCAMLVERSION" >&6; } # If OCAMLLIB is set, use it if test "$OCAMLLIB" = ""; then OCAMLLIB=`$OCAMLC -where 2>/dev/null || $OCAMLC -v|tail -1|cut -d ' ' -f 4` else { $as_echo "$as_me:${as_lineno-$LINENO}: result: OCAMLLIB previously set; preserving it." >&5 $as_echo "OCAMLLIB previously set; preserving it." >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: OCaml library path is $OCAMLLIB" >&5 $as_echo "OCaml library path is $OCAMLLIB" >&6; } # checking for ocamlopt if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlopt", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamlopt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLOPT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLOPT"; then ac_cv_prog_OCAMLOPT="$OCAMLOPT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLOPT="${ac_tool_prefix}ocamlopt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLOPT=$ac_cv_prog_OCAMLOPT if test -n "$OCAMLOPT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLOPT" >&5 $as_echo "$OCAMLOPT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLOPT"; then ac_ct_OCAMLOPT=$OCAMLOPT # Extract the first word of "ocamlopt", so it can be a program name with args. set dummy ocamlopt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLOPT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLOPT"; then ac_cv_prog_ac_ct_OCAMLOPT="$ac_ct_OCAMLOPT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLOPT="ocamlopt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLOPT=$ac_cv_prog_ac_ct_OCAMLOPT if test -n "$ac_ct_OCAMLOPT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLOPT" >&5 $as_echo "$ac_ct_OCAMLOPT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLOPT" = x; then OCAMLOPT="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLOPT=$ac_ct_OCAMLOPT fi else OCAMLOPT="$ac_cv_prog_OCAMLOPT" fi OCAMLBEST=byte if test "$OCAMLOPT" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find ocamlopt; bytecode compilation only." >&5 $as_echo "$as_me: WARNING: Cannot find ocamlopt; bytecode compilation only." >&2;} else TMPVERSION=`$OCAMLOPT -v | sed -n -e 's|.*version* *\(.*\)$|\1|p' ` if test "$TMPVERSION" != "$OCAMLVERSION" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: versions differs from ocamlc; ocamlopt discarded." >&5 $as_echo "versions differs from ocamlc; ocamlopt discarded." >&6; } OCAMLOPT=no else OCAMLBEST=opt fi fi # checking for ocamlc.opt if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlc.opt", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamlc.opt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLCDOTOPT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLCDOTOPT"; then ac_cv_prog_OCAMLCDOTOPT="$OCAMLCDOTOPT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLCDOTOPT="${ac_tool_prefix}ocamlc.opt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLCDOTOPT=$ac_cv_prog_OCAMLCDOTOPT if test -n "$OCAMLCDOTOPT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLCDOTOPT" >&5 $as_echo "$OCAMLCDOTOPT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLCDOTOPT"; then ac_ct_OCAMLCDOTOPT=$OCAMLCDOTOPT # Extract the first word of "ocamlc.opt", so it can be a program name with args. set dummy ocamlc.opt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLCDOTOPT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLCDOTOPT"; then ac_cv_prog_ac_ct_OCAMLCDOTOPT="$ac_ct_OCAMLCDOTOPT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLCDOTOPT="ocamlc.opt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLCDOTOPT=$ac_cv_prog_ac_ct_OCAMLCDOTOPT if test -n "$ac_ct_OCAMLCDOTOPT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLCDOTOPT" >&5 $as_echo "$ac_ct_OCAMLCDOTOPT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLCDOTOPT" = x; then OCAMLCDOTOPT="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLCDOTOPT=$ac_ct_OCAMLCDOTOPT fi else OCAMLCDOTOPT="$ac_cv_prog_OCAMLCDOTOPT" fi if test "$OCAMLCDOTOPT" != "no"; then TMPVERSION=`$OCAMLCDOTOPT -v | sed -n -e 's|.*version* *\(.*\)$|\1|p' ` if test "$TMPVERSION" != "$OCAMLVERSION" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: versions differs from ocamlc; ocamlc.opt discarded." >&5 $as_echo "versions differs from ocamlc; ocamlc.opt discarded." >&6; } else OCAMLC=$OCAMLCDOTOPT fi fi # checking for ocamlopt.opt if test "$OCAMLOPT" != "no" ; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlopt.opt", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamlopt.opt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLOPTDOTOPT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLOPTDOTOPT"; then ac_cv_prog_OCAMLOPTDOTOPT="$OCAMLOPTDOTOPT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLOPTDOTOPT="${ac_tool_prefix}ocamlopt.opt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLOPTDOTOPT=$ac_cv_prog_OCAMLOPTDOTOPT if test -n "$OCAMLOPTDOTOPT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLOPTDOTOPT" >&5 $as_echo "$OCAMLOPTDOTOPT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLOPTDOTOPT"; then ac_ct_OCAMLOPTDOTOPT=$OCAMLOPTDOTOPT # Extract the first word of "ocamlopt.opt", so it can be a program name with args. set dummy ocamlopt.opt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLOPTDOTOPT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLOPTDOTOPT"; then ac_cv_prog_ac_ct_OCAMLOPTDOTOPT="$ac_ct_OCAMLOPTDOTOPT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLOPTDOTOPT="ocamlopt.opt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLOPTDOTOPT=$ac_cv_prog_ac_ct_OCAMLOPTDOTOPT if test -n "$ac_ct_OCAMLOPTDOTOPT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLOPTDOTOPT" >&5 $as_echo "$ac_ct_OCAMLOPTDOTOPT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLOPTDOTOPT" = x; then OCAMLOPTDOTOPT="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLOPTDOTOPT=$ac_ct_OCAMLOPTDOTOPT fi else OCAMLOPTDOTOPT="$ac_cv_prog_OCAMLOPTDOTOPT" fi if test "$OCAMLOPTDOTOPT" != "no"; then TMPVERSION=`$OCAMLOPTDOTOPT -v | sed -n -e 's|.*version* *\(.*\)$|\1|p' ` if test "$TMPVERSION" != "$OCAMLVERSION" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: version differs from ocamlc; ocamlopt.opt discarded." >&5 $as_echo "version differs from ocamlc; ocamlopt.opt discarded." >&6; } else OCAMLOPT=$OCAMLOPTDOTOPT fi fi fi fi # checking for ocaml toplevel if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocaml", so it can be a program name with args. set dummy ${ac_tool_prefix}ocaml; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAML+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAML"; then ac_cv_prog_OCAML="$OCAML" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAML="${ac_tool_prefix}ocaml" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAML=$ac_cv_prog_OCAML if test -n "$OCAML"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAML" >&5 $as_echo "$OCAML" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAML"; then ac_ct_OCAML=$OCAML # Extract the first word of "ocaml", so it can be a program name with args. set dummy ocaml; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAML+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAML"; then ac_cv_prog_ac_ct_OCAML="$ac_ct_OCAML" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAML="ocaml" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAML=$ac_cv_prog_ac_ct_OCAML if test -n "$ac_ct_OCAML"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAML" >&5 $as_echo "$ac_ct_OCAML" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAML" = x; then OCAML="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAML=$ac_ct_OCAML fi else OCAML="$ac_cv_prog_OCAML" fi # checking for ocamldep if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamldep", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamldep; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLDEP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLDEP"; then ac_cv_prog_OCAMLDEP="$OCAMLDEP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLDEP="${ac_tool_prefix}ocamldep" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLDEP=$ac_cv_prog_OCAMLDEP if test -n "$OCAMLDEP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLDEP" >&5 $as_echo "$OCAMLDEP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLDEP"; then ac_ct_OCAMLDEP=$OCAMLDEP # Extract the first word of "ocamldep", so it can be a program name with args. set dummy ocamldep; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLDEP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLDEP"; then ac_cv_prog_ac_ct_OCAMLDEP="$ac_ct_OCAMLDEP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLDEP="ocamldep" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLDEP=$ac_cv_prog_ac_ct_OCAMLDEP if test -n "$ac_ct_OCAMLDEP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLDEP" >&5 $as_echo "$ac_ct_OCAMLDEP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLDEP" = x; then OCAMLDEP="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLDEP=$ac_ct_OCAMLDEP fi else OCAMLDEP="$ac_cv_prog_OCAMLDEP" fi # checking for ocamlmktop if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlmktop", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamlmktop; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLMKTOP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLMKTOP"; then ac_cv_prog_OCAMLMKTOP="$OCAMLMKTOP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLMKTOP="${ac_tool_prefix}ocamlmktop" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLMKTOP=$ac_cv_prog_OCAMLMKTOP if test -n "$OCAMLMKTOP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLMKTOP" >&5 $as_echo "$OCAMLMKTOP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLMKTOP"; then ac_ct_OCAMLMKTOP=$OCAMLMKTOP # Extract the first word of "ocamlmktop", so it can be a program name with args. set dummy ocamlmktop; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLMKTOP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLMKTOP"; then ac_cv_prog_ac_ct_OCAMLMKTOP="$ac_ct_OCAMLMKTOP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLMKTOP="ocamlmktop" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLMKTOP=$ac_cv_prog_ac_ct_OCAMLMKTOP if test -n "$ac_ct_OCAMLMKTOP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLMKTOP" >&5 $as_echo "$ac_ct_OCAMLMKTOP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLMKTOP" = x; then OCAMLMKTOP="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLMKTOP=$ac_ct_OCAMLMKTOP fi else OCAMLMKTOP="$ac_cv_prog_OCAMLMKTOP" fi # checking for ocamlmklib if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlmklib", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamlmklib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLMKLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLMKLIB"; then ac_cv_prog_OCAMLMKLIB="$OCAMLMKLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLMKLIB="${ac_tool_prefix}ocamlmklib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLMKLIB=$ac_cv_prog_OCAMLMKLIB if test -n "$OCAMLMKLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLMKLIB" >&5 $as_echo "$OCAMLMKLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLMKLIB"; then ac_ct_OCAMLMKLIB=$OCAMLMKLIB # Extract the first word of "ocamlmklib", so it can be a program name with args. set dummy ocamlmklib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLMKLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLMKLIB"; then ac_cv_prog_ac_ct_OCAMLMKLIB="$ac_ct_OCAMLMKLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLMKLIB="ocamlmklib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLMKLIB=$ac_cv_prog_ac_ct_OCAMLMKLIB if test -n "$ac_ct_OCAMLMKLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLMKLIB" >&5 $as_echo "$ac_ct_OCAMLMKLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLMKLIB" = x; then OCAMLMKLIB="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLMKLIB=$ac_ct_OCAMLMKLIB fi else OCAMLMKLIB="$ac_cv_prog_OCAMLMKLIB" fi # checking for ocamldoc if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamldoc", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamldoc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLDOC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLDOC"; then ac_cv_prog_OCAMLDOC="$OCAMLDOC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLDOC="${ac_tool_prefix}ocamldoc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLDOC=$ac_cv_prog_OCAMLDOC if test -n "$OCAMLDOC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLDOC" >&5 $as_echo "$OCAMLDOC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLDOC"; then ac_ct_OCAMLDOC=$OCAMLDOC # Extract the first word of "ocamldoc", so it can be a program name with args. set dummy ocamldoc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLDOC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLDOC"; then ac_cv_prog_ac_ct_OCAMLDOC="$ac_ct_OCAMLDOC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLDOC="ocamldoc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLDOC=$ac_cv_prog_ac_ct_OCAMLDOC if test -n "$ac_ct_OCAMLDOC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLDOC" >&5 $as_echo "$ac_ct_OCAMLDOC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLDOC" = x; then OCAMLDOC="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLDOC=$ac_ct_OCAMLDOC fi else OCAMLDOC="$ac_cv_prog_OCAMLDOC" fi # checking for ocamlbuild if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlbuild", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamlbuild; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLBUILD+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLBUILD"; then ac_cv_prog_OCAMLBUILD="$OCAMLBUILD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLBUILD="${ac_tool_prefix}ocamlbuild" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLBUILD=$ac_cv_prog_OCAMLBUILD if test -n "$OCAMLBUILD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLBUILD" >&5 $as_echo "$OCAMLBUILD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLBUILD"; then ac_ct_OCAMLBUILD=$OCAMLBUILD # Extract the first word of "ocamlbuild", so it can be a program name with args. set dummy ocamlbuild; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLBUILD+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLBUILD"; then ac_cv_prog_ac_ct_OCAMLBUILD="$ac_ct_OCAMLBUILD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLBUILD="ocamlbuild" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLBUILD=$ac_cv_prog_ac_ct_OCAMLBUILD if test -n "$ac_ct_OCAMLBUILD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLBUILD" >&5 $as_echo "$ac_ct_OCAMLBUILD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLBUILD" = x; then OCAMLBUILD="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLBUILD=$ac_ct_OCAMLBUILD fi else OCAMLBUILD="$ac_cv_prog_OCAMLBUILD" fi # checking for ocamlfind if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlfind", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamlfind; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OCAMLFIND+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OCAMLFIND"; then ac_cv_prog_OCAMLFIND="$OCAMLFIND" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OCAMLFIND="${ac_tool_prefix}ocamlfind" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OCAMLFIND=$ac_cv_prog_OCAMLFIND if test -n "$OCAMLFIND"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OCAMLFIND" >&5 $as_echo "$OCAMLFIND" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OCAMLFIND"; then ac_ct_OCAMLFIND=$OCAMLFIND # Extract the first word of "ocamlfind", so it can be a program name with args. set dummy ocamlfind; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OCAMLFIND+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OCAMLFIND"; then ac_cv_prog_ac_ct_OCAMLFIND="$ac_ct_OCAMLFIND" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OCAMLFIND="ocamlfind" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OCAMLFIND=$ac_cv_prog_ac_ct_OCAMLFIND if test -n "$ac_ct_OCAMLFIND"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OCAMLFIND" >&5 $as_echo "$ac_ct_OCAMLFIND" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OCAMLFIND" = x; then OCAMLFIND="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OCAMLFIND=$ac_ct_OCAMLFIND fi else OCAMLFIND="$ac_cv_prog_OCAMLFIND" fi for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done if test "x$ocamltools" = "xy"; then : if test "x$OCAMLOPT" = "xno" || test "x$OCAMLFIND" = "xno"; then : if test "x$enable_ocamltools" = "xyes"; then : as_fn_error $? "Ocaml tools enabled, but missing ocamlopt or ocamlfind" "$LINENO" 5 fi ocamltools="n" else # Used to indicate true or false condition ax_compare_version=false # Convert the two version strings to be compared into a format that # allows a simple string comparison. The end result is that a version # string of the form 1.12.5-r617 will be converted to the form # 0001001200050617. In other words, each number is zero padded to four # digits, and non digits are removed. ax_compare_version_A=`echo "$OCAMLVERSION" | sed -e 's/\([0-9]*\)/Z\1Z/g' \ -e 's/Z\([0-9]\)Z/Z0\1Z/g' \ -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \ -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \ -e 's/[^0-9]//g'` ax_compare_version_B=`echo "3.09.3" | sed -e 's/\([0-9]*\)/Z\1Z/g' \ -e 's/Z\([0-9]\)Z/Z0\1Z/g' \ -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \ -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \ -e 's/[^0-9]//g'` ax_compare_version=`echo "x$ax_compare_version_A x$ax_compare_version_B" | sed 's/^ *//' | sort -r | sed "s/x${ax_compare_version_A}/false/;s/x${ax_compare_version_B}/true/;1q"` if test "$ax_compare_version" = "true" ; then if test "x$enable_ocamltools" = "xyes"; then : as_fn_error $? "Your version of OCaml: $OCAMLVERSION is not supported" "$LINENO" 5 fi ocamltools="n" fi fi fi if test "x$XENSTORE" = "x"; then : # Check whether --with-xenstored was given. if test "${with_xenstored+set}" = set; then : withval=$with_xenstored; if test "x$withval" = "xxenstored"; then : xenstore=$withval xenstored=$sbindir/xenstored fi if test "x$withval" = "xoxenstored"; then : xenstore=$withval xenstored=$sbindir/oxenstored if test "$ocamltools" = "n"; then : as_fn_error $? "Missing ocaml dependencies for oxenstored, try installing ocaml ocaml-compiler-libs ocaml-runtime ocaml-findlib" "$LINENO" 5 fi fi if test "x$withval" != "xoxenstored" && test "x$withval" != "xxenstored"; then : as_fn_error $? "Unsupported xenstored specified, supported types: oxenstored xenstored" "$LINENO" 5 fi else xenstore="oxenstored" xenstored=$sbindir/oxenstored if test "$ocamltools" = "n"; then : xenstore="xenstored" xenstored=$sbindir/xenstored fi fi fi XENSTORE=$xenstore if test "x$XENSTORED" = "x"; then : XENSTORED=$xenstored fi if test "x$xsmpolicy" = "xy"; then : # check for a checkpolicy binary with support for -t xen if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}checkpolicy", so it can be a program name with args. set dummy ${ac_tool_prefix}checkpolicy; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CHECKPOLICY+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CHECKPOLICY"; then ac_cv_prog_CHECKPOLICY="$CHECKPOLICY" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CHECKPOLICY="${ac_tool_prefix}checkpolicy" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CHECKPOLICY=$ac_cv_prog_CHECKPOLICY if test -n "$CHECKPOLICY"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CHECKPOLICY" >&5 $as_echo "$CHECKPOLICY" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CHECKPOLICY"; then ac_ct_CHECKPOLICY=$CHECKPOLICY # Extract the first word of "checkpolicy", so it can be a program name with args. set dummy checkpolicy; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CHECKPOLICY+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CHECKPOLICY"; then ac_cv_prog_ac_ct_CHECKPOLICY="$ac_ct_CHECKPOLICY" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CHECKPOLICY="checkpolicy" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CHECKPOLICY=$ac_cv_prog_ac_ct_CHECKPOLICY if test -n "$ac_ct_CHECKPOLICY"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CHECKPOLICY" >&5 $as_echo "$ac_ct_CHECKPOLICY" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CHECKPOLICY" = x; then CHECKPOLICY="no" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CHECKPOLICY=$ac_ct_CHECKPOLICY fi else CHECKPOLICY="$ac_cv_prog_CHECKPOLICY" fi if test "$CHECKPOLICY" != "no"; then CHECKPOLICYHELP=`$CHECKPOLICY -h | grep xen` if test "$CHECKPOLICYHELP" = ""; then CHECKPOLICY=no fi fi if test "x$CHECKPOLICY" = "xno"; then : if test "x$enable_xsmpolicy" = "xyes"; then : as_fn_error $? "XSM policy compilation enabled, but unable to find checkpolicy" "$LINENO" 5 fi xsmpolicy="n" fi fi case "$host_os" in freebsd*) ;; *) # Extract the first word of "bash", so it can be a program name with args. set dummy bash; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_BASH+:} false; then : $as_echo_n "(cached) " >&6 else case $BASH in [\\/]* | ?:[\\/]*) ac_cv_path_BASH="$BASH" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_BASH="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_BASH" && ac_cv_path_BASH="no" ;; esac fi BASH=$ac_cv_path_BASH if test -n "$BASH"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BASH" >&5 $as_echo "$BASH" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"${BASH}" = x"no" then as_fn_error $? "Unable to find bash, please install bash" "$LINENO" 5 fi;; esac if echo "$PYTHON" | grep -q "^/"; then : PYTHONPATH=$PYTHON PYTHON=`basename $PYTHONPATH` elif test -z "$PYTHON"; then : PYTHON="python" else as_fn_error $? "PYTHON specified, but is not an absolute path" "$LINENO" 5 fi # Extract the first word of "$PYTHON", so it can be a program name with args. set dummy $PYTHON; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PYTHONPATH+:} false; then : $as_echo_n "(cached) " >&6 else case $PYTHONPATH in [\\/]* | ?:[\\/]*) ac_cv_path_PYTHONPATH="$PYTHONPATH" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PYTHONPATH="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_PYTHONPATH" && ac_cv_path_PYTHONPATH="no" ;; esac fi PYTHONPATH=$ac_cv_path_PYTHONPATH if test -n "$PYTHONPATH"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHONPATH" >&5 $as_echo "$PYTHONPATH" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"${PYTHONPATH}" = x"no" then as_fn_error $? "Unable to find $PYTHON, please install $PYTHON" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for python version >= 2.3 " >&5 $as_echo_n "checking for python version >= 2.3 ... " >&6; } `$PYTHON -c 'import sys; sys.exit(eval("sys.version_info < (2, 3)"))'` if test "$?" != "0" then python_version=`$PYTHON -V 2>&1` { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "$python_version is too old, minimum required version is 2.3" "$LINENO" 5 else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done if test "$cross_compiling" != yes; then : ac_previous_cppflags=$CPPFLAGS ac_previous_ldflags=$LDFLAGS ac_python_version=`$PYTHON -c 'import distutils.sysconfig; \ print distutils.sysconfig.get_config_var("VERSION")'` # Extract the first word of "$PYTHON-config", so it can be a program name with args. set dummy $PYTHON-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_pyconfig+:} false; then : $as_echo_n "(cached) " >&6 else case $pyconfig in [\\/]* | ?:[\\/]*) ac_cv_path_pyconfig="$pyconfig" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_pyconfig="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_pyconfig" && ac_cv_path_pyconfig="no" ;; esac fi pyconfig=$ac_cv_path_pyconfig if test -n "$pyconfig"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pyconfig" >&5 $as_echo "$pyconfig" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"$pyconfig" = x"no"; then : CPPFLAGS="$CFLAGS `$PYTHON -c 'import distutils.sysconfig; \ print "-I" + distutils.sysconfig.get_config_var("INCLUDEPY")'`" CPPFLAGS="$CPPFLAGS `$PYTHON -c 'import distutils.sysconfig; \ print distutils.sysconfig.get_config_var("CFLAGS")'`" PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ print distutils.sysconfig.get_config_var("LIBS")'`" PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ print distutils.sysconfig.get_config_var("SYSLIBS")'`" LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ print "-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\ standard_lib=1) + "/config"'`" LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ print distutils.sysconfig.get_config_var("LINKFORSHARED")'`" LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ print distutils.sysconfig.get_config_var("LDFLAGS")'`" else CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" PYTHON_LIBS="$LIBS `$PYTHON-config --libs`" fi ac_fn_c_check_header_mongrel "$LINENO" "Python.h" "ac_cv_header_Python_h" "$ac_includes_default" if test "x$ac_cv_header_Python_h" = xyes; then : else as_fn_error $? "Unable to find Python development headers" "$LINENO" 5 fi as_ac_Lib=`$as_echo "ac_cv_lib_python$ac_python_version''_PyArg_ParseTuple" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PyArg_ParseTuple in -lpython$ac_python_version" >&5 $as_echo_n "checking for PyArg_ParseTuple in -lpython$ac_python_version... " >&6; } if eval \${$as_ac_Lib+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpython$ac_python_version $PYTHON_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char PyArg_ParseTuple (); int main () { return PyArg_ParseTuple (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$as_ac_Lib=yes" else eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_LIBpython$ac_python_version" | $as_tr_cpp` 1 _ACEOF LIBS="-lpython$ac_python_version $LIBS" else as_fn_error $? "Unable to find a suitable python development library" "$LINENO" 5 fi CPPFLAGS=$ac_previous_cppflags LDFLAGS=$ac_previous_ldflags { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Python setup.py brokenly enables -D_FORTIFY_SOURCE" >&5 $as_echo_n "checking whether Python setup.py brokenly enables -D_FORTIFY_SOURCE... " >&6; } if ${ax_cv_python_fortify+:} false; then : $as_echo_n "(cached) " >&6 else ax_cv_python_fortify=no for arg in $($PYTHON-config --cflags); do case "$arg" in -D_FORTIFY_SOURCE=0) ax_cv_python_fortify=no ;; -D_FORTIFY_SOURCE=*) ax_cv_python_fortify=yes ;; -Wp,-D_FORTIFY_SOURCE=0) ax_cv_python_fortify=no ;; -Wp,-D_FORTIFY_SOURCE=*) ax_cv_python_fortify=yes ;; *) ;; esac done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_python_fortify" >&5 $as_echo "$ax_cv_python_fortify" >&6; } if test x$ax_cv_python_fortify = xyes; then : PY_NOOPT_CFLAGS=-O1 else PY_NOOPT_CFLAGS='' fi fi if ! $rump; then # Extract the first word of "xgettext", so it can be a program name with args. set dummy xgettext; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_XGETTEXT+:} false; then : $as_echo_n "(cached) " >&6 else case $XGETTEXT in [\\/]* | ?:[\\/]*) ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_XGETTEXT="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT="no" ;; esac fi XGETTEXT=$ac_cv_path_XGETTEXT if test -n "$XGETTEXT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XGETTEXT" >&5 $as_echo "$XGETTEXT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"${XGETTEXT}" = x"no" then as_fn_error $? "Unable to find xgettext, please install xgettext" "$LINENO" 5 fi case "$host_cpu" in i[3456]86|x86_64|aarch64) # Extract the first word of "iasl", so it can be a program name with args. set dummy iasl; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_IASL+:} false; then : $as_echo_n "(cached) " >&6 else case $IASL in [\\/]* | ?:[\\/]*) ac_cv_path_IASL="$IASL" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_IASL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_IASL" && ac_cv_path_IASL="no" ;; esac fi IASL=$ac_cv_path_IASL if test -n "$IASL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $IASL" >&5 $as_echo "$IASL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"${IASL}" = x"no" then as_fn_error $? "Unable to find iasl, please install iasl" "$LINENO" 5 fi ;; esac ac_fn_c_check_header_mongrel "$LINENO" "uuid/uuid.h" "ac_cv_header_uuid_uuid_h" "$ac_includes_default" if test "x$ac_cv_header_uuid_uuid_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid_clear in -luuid" >&5 $as_echo_n "checking for uuid_clear in -luuid... " >&6; } if ${ac_cv_lib_uuid_uuid_clear+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-luuid $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char uuid_clear (); int main () { return uuid_clear (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_uuid_uuid_clear=yes else ac_cv_lib_uuid_uuid_clear=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_clear" >&5 $as_echo "$ac_cv_lib_uuid_uuid_clear" >&6; } if test "x$ac_cv_lib_uuid_uuid_clear" = xyes; then : libuuid="y" fi fi ac_fn_c_check_header_mongrel "$LINENO" "uuid.h" "ac_cv_header_uuid_h" "$ac_includes_default" if test "x$ac_cv_header_uuid_h" = xyes; then : libuuid="y" fi if test "$libuuid" != "y"; then : as_fn_error $? "cannot find a valid uuid library" "$LINENO" 5 fi ac_fn_c_check_header_mongrel "$LINENO" "curses.h" "ac_cv_header_curses_h" "$ac_includes_default" if test "x$ac_cv_header_curses_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clear in -lcurses" >&5 $as_echo_n "checking for clear in -lcurses... " >&6; } if ${ac_cv_lib_curses_clear+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcurses $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clear (); int main () { return clear (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_curses_clear=yes else ac_cv_lib_curses_clear=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_clear" >&5 $as_echo "$ac_cv_lib_curses_clear" >&6; } if test "x$ac_cv_lib_curses_clear" = xyes; then : curses="y" else curses="n" fi else curses="n" fi ac_fn_c_check_header_mongrel "$LINENO" "ncurses.h" "ac_cv_header_ncurses_h" "$ac_includes_default" if test "x$ac_cv_header_ncurses_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clear in -lncurses" >&5 $as_echo_n "checking for clear in -lncurses... " >&6; } if ${ac_cv_lib_ncurses_clear+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lncurses $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clear (); int main () { return clear (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ncurses_clear=yes else ac_cv_lib_ncurses_clear=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_clear" >&5 $as_echo "$ac_cv_lib_ncurses_clear" >&6; } if test "x$ac_cv_lib_ncurses_clear" = xyes; then : ncurses="y" else ncurses="n" fi else ncurses="n" fi if test "$curses" = "n" && test "$ncurses" = "n"; then : as_fn_error $? "Unable to find a suitable curses library" "$LINENO" 5 fi # Prefer ncurses over curses if both are present if test "$ncurses" = "y"; then : CURSES_LIBS="-lncurses" $as_echo "#define INCLUDE_CURSES_H " >>confdefs.h else CURSES_LIBS="-lcurses" $as_echo "#define INCLUDE_CURSES_H " >>confdefs.h fi if test "$ncurses" = "y"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for define_key in -ltinfo" >&5 $as_echo_n "checking for define_key in -ltinfo... " >&6; } if ${ac_cv_lib_tinfo_define_key+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltinfo $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char define_key (); int main () { return define_key (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_tinfo_define_key=yes else ac_cv_lib_tinfo_define_key=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tinfo_define_key" >&5 $as_echo "$ac_cv_lib_tinfo_define_key" >&6; } if test "x$ac_cv_lib_tinfo_define_key" = xyes; then : TINFO_LIBS=-ltinfo fi fi if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi if test "x$qemu_xen" = "xy"; then : pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for glib" >&5 $as_echo_n "checking for glib... " >&6; } if test -n "$glib_CFLAGS"; then pkg_cv_glib_CFLAGS="$glib_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.12\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.12") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_glib_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.12" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$glib_LIBS"; then pkg_cv_glib_LIBS="$glib_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.12\""; } >&5 ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.12") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_glib_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.12" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then glib_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.12" 2>&1` else glib_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.12" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$glib_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (glib-2.0 >= 2.12) were not met: $glib_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables glib_CFLAGS and glib_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables glib_CFLAGS and glib_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else glib_CFLAGS=$pkg_cv_glib_CFLAGS glib_LIBS=$pkg_cv_glib_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pixman" >&5 $as_echo_n "checking for pixman... " >&6; } if test -n "$pixman_CFLAGS"; then pkg_cv_pixman_CFLAGS="$pixman_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"pixman-1 >= 0.21.8\""; } >&5 ($PKG_CONFIG --exists --print-errors "pixman-1 >= 0.21.8") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_pixman_CFLAGS=`$PKG_CONFIG --cflags "pixman-1 >= 0.21.8" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$pixman_LIBS"; then pkg_cv_pixman_LIBS="$pixman_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"pixman-1 >= 0.21.8\""; } >&5 ($PKG_CONFIG --exists --print-errors "pixman-1 >= 0.21.8") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_pixman_LIBS=`$PKG_CONFIG --libs "pixman-1 >= 0.21.8" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then pixman_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "pixman-1 >= 0.21.8" 2>&1` else pixman_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "pixman-1 >= 0.21.8" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$pixman_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (pixman-1 >= 0.21.8) were not met: $pixman_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables pixman_CFLAGS and pixman_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables pixman_CFLAGS and pixman_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else pixman_CFLAGS=$pkg_cv_pixman_CFLAGS pixman_LIBS=$pkg_cv_pixman_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi fi # Extract the first word of "wget", so it can be a program name with args. set dummy wget; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_WGET+:} false; then : $as_echo_n "(cached) " >&6 else case $WGET in [\\/]* | ?:[\\/]*) ac_cv_path_WGET="$WGET" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_WGET="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_WGET" && ac_cv_path_WGET="no" ;; esac fi WGET=$ac_cv_path_WGET if test -n "$WGET"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WGET" >&5 $as_echo "$WGET" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"$WGET" != x"no"; then : FETCHER="$WGET -c -O" else # Extract the first word of "ftp", so it can be a program name with args. set dummy ftp; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_FTP+:} false; then : $as_echo_n "(cached) " >&6 else case $FTP in [\\/]* | ?:[\\/]*) ac_cv_path_FTP="$FTP" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_FTP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_FTP" && ac_cv_path_FTP="no" ;; esac fi FTP=$ac_cv_path_FTP if test -n "$FTP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FTP" >&5 $as_echo "$FTP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"$FTP" != x"no"; then : FETCHER="$FTP -o" else as_fn_error $? "cannot find wget or ftp" "$LINENO" 5 fi fi # Checks for libraries. ac_fn_c_check_header_mongrel "$LINENO" "bzlib.h" "ac_cv_header_bzlib_h" "$ac_includes_default" if test "x$ac_cv_header_bzlib_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BZ2_bzDecompressInit in -lbz2" >&5 $as_echo_n "checking for BZ2_bzDecompressInit in -lbz2... " >&6; } if ${ac_cv_lib_bz2_BZ2_bzDecompressInit+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbz2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char BZ2_bzDecompressInit (); int main () { return BZ2_bzDecompressInit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_bz2_BZ2_bzDecompressInit=yes else ac_cv_lib_bz2_BZ2_bzDecompressInit=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bz2_BZ2_bzDecompressInit" >&5 $as_echo "$ac_cv_lib_bz2_BZ2_bzDecompressInit" >&6; } if test "x$ac_cv_lib_bz2_BZ2_bzDecompressInit" = xyes; then : zlib="$zlib -DHAVE_BZLIB -lbz2" fi fi ac_fn_c_check_header_mongrel "$LINENO" "lzma.h" "ac_cv_header_lzma_h" "$ac_includes_default" if test "x$ac_cv_header_lzma_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lzma_stream_decoder in -llzma" >&5 $as_echo_n "checking for lzma_stream_decoder in -llzma... " >&6; } if ${ac_cv_lib_lzma_lzma_stream_decoder+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llzma $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lzma_stream_decoder (); int main () { return lzma_stream_decoder (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lzma_lzma_stream_decoder=yes else ac_cv_lib_lzma_lzma_stream_decoder=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lzma_lzma_stream_decoder" >&5 $as_echo "$ac_cv_lib_lzma_lzma_stream_decoder" >&6; } if test "x$ac_cv_lib_lzma_lzma_stream_decoder" = xyes; then : zlib="$zlib -DHAVE_LZMA -llzma" fi fi ac_fn_c_check_header_mongrel "$LINENO" "lzo/lzo1x.h" "ac_cv_header_lzo_lzo1x_h" "$ac_includes_default" if test "x$ac_cv_header_lzo_lzo1x_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lzo1x_decompress in -llzo2" >&5 $as_echo_n "checking for lzo1x_decompress in -llzo2... " >&6; } if ${ac_cv_lib_lzo2_lzo1x_decompress+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llzo2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char lzo1x_decompress (); int main () { return lzo1x_decompress (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_lzo2_lzo1x_decompress=yes else ac_cv_lib_lzo2_lzo1x_decompress=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lzo2_lzo1x_decompress" >&5 $as_echo "$ac_cv_lib_lzo2_lzo1x_decompress" >&6; } if test "x$ac_cv_lib_lzo2_lzo1x_decompress" = xyes; then : zlib="$zlib -DHAVE_LZO1X -llzo2" fi fi if test "x$enable_blktap2" = "xyes"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for io_setup in -laio" >&5 $as_echo_n "checking for io_setup in -laio... " >&6; } if ${ac_cv_lib_aio_io_setup+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-laio $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char io_setup (); int main () { return io_setup (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_aio_io_setup=yes else ac_cv_lib_aio_io_setup=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_aio_io_setup" >&5 $as_echo "$ac_cv_lib_aio_io_setup" >&6; } if test "x$ac_cv_lib_aio_io_setup" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBAIO 1 _ACEOF LIBS="-laio $LIBS" else as_fn_error $? "Could not find libaio" "$LINENO" 5 fi fi ac_fn_c_check_header_mongrel "$LINENO" "ext2fs/ext2fs.h" "ac_cv_header_ext2fs_ext2fs_h" "$ac_includes_default" if test "x$ac_cv_header_ext2fs_ext2fs_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ext2fs_open2 in -lext2fs" >&5 $as_echo_n "checking for ext2fs_open2 in -lext2fs... " >&6; } if ${ac_cv_lib_ext2fs_ext2fs_open2+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lext2fs $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ext2fs_open2 (); int main () { return ext2fs_open2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ext2fs_ext2fs_open2=yes else ac_cv_lib_ext2fs_ext2fs_open2=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ext2fs_ext2fs_open2" >&5 $as_echo "$ac_cv_lib_ext2fs_ext2fs_open2" >&6; } if test "x$ac_cv_lib_ext2fs_ext2fs_open2" = xyes; then : $as_echo "#define INCLUDE_EXTFS_H " >>confdefs.h EXTFS_LIBS="-lext2fs" fi fi ac_fn_c_check_header_mongrel "$LINENO" "ext4fs/ext2fs.h" "ac_cv_header_ext4fs_ext2fs_h" "$ac_includes_default" if test "x$ac_cv_header_ext4fs_ext2fs_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ext2fs_open2 in -lext4fs" >&5 $as_echo_n "checking for ext2fs_open2 in -lext4fs... " >&6; } if ${ac_cv_lib_ext4fs_ext2fs_open2+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lext4fs $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ext2fs_open2 (); int main () { return ext2fs_open2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ext4fs_ext2fs_open2=yes else ac_cv_lib_ext4fs_ext2fs_open2=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ext4fs_ext2fs_open2" >&5 $as_echo "$ac_cv_lib_ext4fs_ext2fs_open2" >&6; } if test "x$ac_cv_lib_ext4fs_ext2fs_open2" = xyes; then : $as_echo "#define INCLUDE_EXTFS_H " >>confdefs.h EXTFS_LIBS="-lext4fs" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gcry_md_hash_buffer in -lgcrypt" >&5 $as_echo_n "checking for gcry_md_hash_buffer in -lgcrypt... " >&6; } if ${ac_cv_lib_gcrypt_gcry_md_hash_buffer+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lgcrypt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gcry_md_hash_buffer (); int main () { return gcry_md_hash_buffer (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_gcrypt_gcry_md_hash_buffer=yes else ac_cv_lib_gcrypt_gcry_md_hash_buffer=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gcrypt_gcry_md_hash_buffer" >&5 $as_echo "$ac_cv_lib_gcrypt_gcry_md_hash_buffer" >&6; } if test "x$ac_cv_lib_gcrypt_gcry_md_hash_buffer" = xyes; then : libgcrypt="y" else libgcrypt="n" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread flag" >&5 $as_echo_n "checking for pthread flag... " >&6; } if ${ax_cv_pthread_flags+:} false; then : $as_echo_n "(cached) " >&6 else ax_cv_pthread_flags=-pthread PTHREAD_CFLAGS="$ax_cv_pthread_flags" PTHREAD_LDFLAGS="$ax_cv_pthread_flags" PTHREAD_LIBS="" saved_CFLAGS="$CFLAGS" saved_LDFLAGS="$LDFLAGS" saved_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LDFLAGS="$LDFLAGS $PTHREAD_LDFLAGS" LIBS="$LIBS $PTHREAD_LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) { pthread_atfork(0,0,0); pthread_create(0,0,0,0); } _ACEOF if ac_fn_c_try_link "$LINENO"; then : else ax_cv_pthread_flags=failed fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS="$saved_CFLAGS" LDFLAGS="$saved_LDFLAGS" LIBS="$saved_LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_pthread_flags" >&5 $as_echo "$ax_cv_pthread_flags" >&6; } if test "x$ax_cv_pthread_flags" = xfailed; then as_fn_error $? "-pthread does not work" "$LINENO" 5 fi PTHREAD_CFLAGS="$ax_cv_pthread_flags" PTHREAD_LDFLAGS="$ax_cv_pthread_flags" PTHREAD_LIBS="" saved_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -Werror" ac_fn_c_check_header_mongrel "$LINENO" "libutil.h" "ac_cv_header_libutil_h" "$ac_includes_default" if test "x$ac_cv_header_libutil_h" = xyes; then : $as_echo "#define INCLUDE_LIBUTIL_H " >>confdefs.h fi CPPFLAGS="$saved_CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openpty et al" >&5 $as_echo_n "checking for openpty et al... " >&6; } if ${ax_cv_ptyfuncs_libs+:} false; then : $as_echo_n "(cached) " >&6 else for ax_cv_ptyfuncs_libs in -lutil "" NOT_FOUND; do if test "x$ax_cv_ptyfuncs_libs" = "xNOT_FOUND"; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Unable to find library for openpty and login_tty See \`config.log' for more details" "$LINENO" 5; } fi saved_LIBS="$LIBS" LIBS="$LIBS $ax_cv_ptyfuncs_libs" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef INCLUDE_LIBUTIL_H #include INCLUDE_LIBUTIL_H #endif int main(void) { openpty(0,0,0,0,0); login_tty(0); } _ACEOF if ac_fn_c_try_link "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$saved_LIBS" done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_ptyfuncs_libs" >&5 $as_echo "$ax_cv_ptyfuncs_libs" >&6; } PTYFUNCS_LIBS="$ax_cv_ptyfuncs_libs" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for yajl_alloc in -lyajl" >&5 $as_echo_n "checking for yajl_alloc in -lyajl... " >&6; } if ${ac_cv_lib_yajl_yajl_alloc+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lyajl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char yajl_alloc (); int main () { return yajl_alloc (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_yajl_yajl_alloc=yes else ac_cv_lib_yajl_yajl_alloc=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_yajl_yajl_alloc" >&5 $as_echo "$ac_cv_lib_yajl_yajl_alloc" >&6; } if test "x$ac_cv_lib_yajl_yajl_alloc" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBYAJL 1 _ACEOF LIBS="-lyajl $LIBS" else as_fn_error $? "Could not find yajl" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for deflateCopy in -lz" >&5 $as_echo_n "checking for deflateCopy in -lz... " >&6; } if ${ac_cv_lib_z_deflateCopy+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char deflateCopy (); int main () { return deflateCopy (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_z_deflateCopy=yes else ac_cv_lib_z_deflateCopy=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflateCopy" >&5 $as_echo "$ac_cv_lib_z_deflateCopy" >&6; } if test "x$ac_cv_lib_z_deflateCopy" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBZ 1 _ACEOF LIBS="-lz $LIBS" else as_fn_error $? "Could not find zlib" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libiconv_open in -liconv" >&5 $as_echo_n "checking for libiconv_open in -liconv... " >&6; } if ${ac_cv_lib_iconv_libiconv_open+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-liconv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char libiconv_open (); int main () { return libiconv_open (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_iconv_libiconv_open=yes else ac_cv_lib_iconv_libiconv_open=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iconv_libiconv_open" >&5 $as_echo "$ac_cv_lib_iconv_libiconv_open" >&6; } if test "x$ac_cv_lib_iconv_libiconv_open" = xyes; then : libiconv="y" else libiconv="n" fi ac_fn_c_check_header_mongrel "$LINENO" "argp.h" "ac_cv_header_argp_h" "$ac_includes_default" if test "x$ac_cv_header_argp_h" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for argp_usage in -largp" >&5 $as_echo_n "checking for argp_usage in -largp... " >&6; } if ${ac_cv_lib_argp_argp_usage+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-largp $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char argp_usage (); int main () { return argp_usage (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_argp_argp_usage=yes else ac_cv_lib_argp_argp_usage=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_argp_argp_usage" >&5 $as_echo "$ac_cv_lib_argp_argp_usage" >&6; } if test "x$ac_cv_lib_argp_argp_usage" = xyes; then : argp_ldflags="-largp" fi else as_fn_error $? "Could not find argp" "$LINENO" 5 fi # FDT is needed only on ARM case "$host_cpu" in arm*|aarch64) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fdt_create in -lfdt" >&5 $as_echo_n "checking for fdt_create in -lfdt... " >&6; } if ${ac_cv_lib_fdt_fdt_create+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lfdt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char fdt_create (); int main () { return fdt_create (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_fdt_fdt_create=yes else ac_cv_lib_fdt_fdt_create=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_fdt_fdt_create" >&5 $as_echo "$ac_cv_lib_fdt_fdt_create" >&6; } if test "x$ac_cv_lib_fdt_fdt_create" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBFDT 1 _ACEOF LIBS="-lfdt $LIBS" else as_fn_error $? "Could not find libfdt" "$LINENO" 5 fi # Check for libfdt >= 1.4.0. If present enable passthrough # Note that libfdt doesn't provide versionning. So we need to rely on # function present in new version. # Use fdt_first_property_offset which has been correctly exported since v1.4.0 ac_fn_c_check_func "$LINENO" "fdt_first_property_offset" "ac_cv_func_fdt_first_property_offset" if test "x$ac_cv_func_fdt_first_property_offset" = xyes; then : partial_dt="y" else partial_dt="n" fi if test "x$partial_dt" = "xy" ; then : $as_echo "#define ENABLE_PARTIAL_DEVICE_TREE 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Disabling support for partial device tree in libxl. Please install libfdt library - version 1.4.0 or higher" >&5 $as_echo "$as_me: WARNING: Disabling support for partial device tree in libxl. Please install libfdt library - version 1.4.0 or higher" >&2;} fi # The functions fdt_{first,next}_subnode may not be available because: # * It has been introduced in 2013 => Doesn't work on Wheezy # * The prototype exists but the functions are not exposed. Don't ask why... for ac_func in fdt_first_subnode fdt_next_subnode do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_decl "$LINENO" "fdt_first_subnode" "ac_cv_have_decl_fdt_first_subnode" "#include " if test "x$ac_cv_have_decl_fdt_first_subnode" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_FDT_FIRST_SUBNODE $ac_have_decl _ACEOF ac_fn_c_check_decl "$LINENO" "fdt_next_subnode" "ac_cv_have_decl_fdt_next_subnode" "#include " if test "x$ac_cv_have_decl_fdt_next_subnode" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_FDT_NEXT_SUBNODE $ac_have_decl _ACEOF # The helper fdt_property_u32 is only present in libfdt >= 1.4.0 # It's an inline function, so only check if the declaration is present ac_fn_c_check_decl "$LINENO" "fdt_property_u32" "ac_cv_have_decl_fdt_property_u32" "#include " if test "x$ac_cv_have_decl_fdt_property_u32" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_FDT_PROPERTY_U32 $ac_have_decl _ACEOF esac # Checks for header files. for ac_header in yajl/yajl_version.h sys/eventfd.h valgrind/memcheck.h utmp.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Check for libnl3 >=3.2.8. If present enable remus network buffering. pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBNL3" >&5 $as_echo_n "checking for LIBNL3... " >&6; } if test -n "$LIBNL3_CFLAGS"; then pkg_cv_LIBNL3_CFLAGS="$LIBNL3_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnl-3.0 >= 3.2.8 libnl-route-3.0 >= 3.2.8\""; } >&5 ($PKG_CONFIG --exists --print-errors "libnl-3.0 >= 3.2.8 libnl-route-3.0 >= 3.2.8") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNL3_CFLAGS=`$PKG_CONFIG --cflags "libnl-3.0 >= 3.2.8 libnl-route-3.0 >= 3.2.8" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNL3_LIBS"; then pkg_cv_LIBNL3_LIBS="$LIBNL3_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnl-3.0 >= 3.2.8 libnl-route-3.0 >= 3.2.8\""; } >&5 ($PKG_CONFIG --exists --print-errors "libnl-3.0 >= 3.2.8 libnl-route-3.0 >= 3.2.8") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNL3_LIBS=`$PKG_CONFIG --libs "libnl-3.0 >= 3.2.8 libnl-route-3.0 >= 3.2.8" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNL3_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libnl-3.0 >= 3.2.8 libnl-route-3.0 >= 3.2.8" 2>&1` else LIBNL3_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libnl-3.0 >= 3.2.8 libnl-route-3.0 >= 3.2.8" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNL3_PKG_ERRORS" >&5 libnl3_lib="n" elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } libnl3_lib="n" else LIBNL3_CFLAGS=$pkg_cv_LIBNL3_CFLAGS LIBNL3_LIBS=$pkg_cv_LIBNL3_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } libnl3_lib="y" fi if test "x$libnl3_lib" = "xn" ; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Disabling support for Remus network buffering and COLO. Please install libnl3 libraries (including libnl3-route), command line tools and devel headers - version 3.2.8 or higher" >&5 $as_echo "$as_me: WARNING: Disabling support for Remus network buffering and COLO. Please install libnl3 libraries (including libnl3-route), command line tools and devel headers - version 3.2.8 or higher" >&2;} libnl=n else libnl=y fi fi # ! $rump # Check whether --enable-systemd was given. if test "${enable_systemd+set}" = set; then : enableval=$enable_systemd; fi if test "x$enable_systemd" = "xno"; then : ax_cv_systemd="n" elif test "x$enable_systemd" = "xyes"; then : ax_cv_systemd="y" elif test -z $ax_cv_systemd; then : ax_cv_systemd="n" fi systemd=$ax_cv_systemd # Check whether --with-systemd was given. if test "${with_systemd+set}" = set; then : withval=$with_systemd; SYSTEMD_DIR="$withval" else SYSTEMD_DIR="" fi # Check whether --with-systemd-modules-load was given. if test "${with_systemd_modules_load+set}" = set; then : withval=$with_systemd_modules_load; SYSTEMD_MODULES_LOAD="$withval" else SYSTEMD_MODULES_LOAD="" fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD" >&5 $as_echo_n "checking for SYSTEMD... " >&6; } if test -n "$SYSTEMD_CFLAGS"; then pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd-daemon") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "libsystemd-daemon" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SYSTEMD_LIBS"; then pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd-daemon") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "libsystemd-daemon" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd-daemon" 2>&1` else SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd-daemon" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SYSTEMD_PKG_ERRORS" >&5 pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD" >&5 $as_echo_n "checking for SYSTEMD... " >&6; } if test -n "$SYSTEMD_CFLAGS"; then pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SYSTEMD_LIBS"; then pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd >= 209" 2>&1` else SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd >= 209" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SYSTEMD_PKG_ERRORS" >&5 systemd="n" elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } systemd="n" else SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } systemd="y" fi elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD" >&5 $as_echo_n "checking for SYSTEMD... " >&6; } if test -n "$SYSTEMD_CFLAGS"; then pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SYSTEMD_LIBS"; then pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd >= 209" 2>&1` else SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd >= 209" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SYSTEMD_PKG_ERRORS" >&5 systemd="n" elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } systemd="n" else SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } systemd="y" fi else SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } systemd="y" fi if test "x$enable_systemd" != "xno"; then : if test "x$systemd" = "xy" ; then : $as_echo "#define HAVE_SYSTEMD 1" >>confdefs.h systemd=y pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD" >&5 $as_echo_n "checking for SYSTEMD... " >&6; } if test -n "$SYSTEMD_CFLAGS"; then pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd-daemon") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "libsystemd-daemon" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SYSTEMD_LIBS"; then pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd-daemon") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "libsystemd-daemon" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd-daemon" 2>&1` else SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd-daemon" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SYSTEMD_PKG_ERRORS" >&5 pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD" >&5 $as_echo_n "checking for SYSTEMD... " >&6; } if test -n "$SYSTEMD_CFLAGS"; then pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SYSTEMD_LIBS"; then pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd >= 209" 2>&1` else SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd >= 209" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SYSTEMD_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libsystemd >= 209) were not met: $SYSTEMD_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables SYSTEMD_CFLAGS and SYSTEMD_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables SYSTEMD_CFLAGS and SYSTEMD_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD" >&5 $as_echo_n "checking for SYSTEMD... " >&6; } if test -n "$SYSTEMD_CFLAGS"; then pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$SYSTEMD_LIBS"; then pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd >= 209\""; } >&5 ($PKG_CONFIG --exists --print-errors "libsystemd >= 209") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "libsystemd >= 209" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd >= 209" 2>&1` else SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd >= 209" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$SYSTEMD_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (libsystemd >= 209) were not met: $SYSTEMD_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables SYSTEMD_CFLAGS and SYSTEMD_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables SYSTEMD_CFLAGS and SYSTEMD_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi else SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi if test "x$SYSTEMD_DIR" = x; then : SYSTEMD_DIR="\$(prefix)/lib/systemd/system/" fi if test "x$SYSTEMD_DIR" = x; then : as_fn_error $? "SYSTEMD_DIR is unset" "$LINENO" 5 fi if test "x$SYSTEMD_MODULES_LOAD" = x; then : SYSTEMD_MODULES_LOAD="\$(prefix)/lib/modules-load.d/" fi if test "x$SYSTEMD_MODULES_LOAD" = x; then : as_fn_error $? "SYSTEMD_MODULES_LOAD is unset" "$LINENO" 5 fi else if test "x$enable_systemd" = "xyes"; then : as_fn_error $? "Unable to find systemd development library" "$LINENO" 5 else systemd=n fi fi else systemd=n fi if test "x$systemd" = "xy"; then : ac_config_files="$ac_config_files hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/var-lib-xenstored.mount hotplug/Linux/systemd/xen-init-dom0.service hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" fi cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by Xen Hypervisor Tools $as_me 4.9, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to . Xen Hypervisor Tools home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ Xen Hypervisor Tools config.status 4.9 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "../config/Tools.mk") CONFIG_FILES="$CONFIG_FILES ../config/Tools.mk" ;; "hotplug/FreeBSD/rc.d/xencommons") CONFIG_FILES="$CONFIG_FILES hotplug/FreeBSD/rc.d/xencommons" ;; "hotplug/FreeBSD/rc.d/xendriverdomain") CONFIG_FILES="$CONFIG_FILES hotplug/FreeBSD/rc.d/xendriverdomain" ;; "hotplug/Linux/init.d/sysconfig.xencommons") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/init.d/sysconfig.xencommons" ;; "hotplug/Linux/init.d/sysconfig.xendomains") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/init.d/sysconfig.xendomains" ;; "hotplug/Linux/init.d/xen-watchdog") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/init.d/xen-watchdog" ;; "hotplug/Linux/init.d/xencommons") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/init.d/xencommons" ;; "hotplug/Linux/init.d/xendomains") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/init.d/xendomains" ;; "hotplug/Linux/init.d/xendriverdomain") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/init.d/xendriverdomain" ;; "hotplug/Linux/launch-xenstore") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/launch-xenstore" ;; "hotplug/Linux/vif-setup") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/vif-setup" ;; "hotplug/Linux/xen-hotplug-common.sh") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/xen-hotplug-common.sh" ;; "hotplug/Linux/xendomains") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/xendomains" ;; "hotplug/NetBSD/rc.d/xencommons") CONFIG_FILES="$CONFIG_FILES hotplug/NetBSD/rc.d/xencommons" ;; "hotplug/NetBSD/rc.d/xendriverdomain") CONFIG_FILES="$CONFIG_FILES hotplug/NetBSD/rc.d/xendriverdomain" ;; "ocaml/xenstored/oxenstored.conf") CONFIG_FILES="$CONFIG_FILES ocaml/xenstored/oxenstored.conf" ;; "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "hotplug/Linux/systemd/proc-xen.mount") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/systemd/proc-xen.mount" ;; "hotplug/Linux/systemd/var-lib-xenstored.mount") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/systemd/var-lib-xenstored.mount" ;; "hotplug/Linux/systemd/xen-init-dom0.service") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/systemd/xen-init-dom0.service" ;; "hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service" ;; "hotplug/Linux/systemd/xen-watchdog.service") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/systemd/xen-watchdog.service" ;; "hotplug/Linux/systemd/xenconsoled.service") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/systemd/xenconsoled.service" ;; "hotplug/Linux/systemd/xendomains.service") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/systemd/xendomains.service" ;; "hotplug/Linux/systemd/xendriverdomain.service") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/systemd/xendriverdomain.service" ;; "hotplug/Linux/systemd/xenstored.service") CONFIG_FILES="$CONFIG_FILES hotplug/Linux/systemd/xenstored.service" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi xen-4.9.2/tools/libvchan/0000775000175000017500000000000013256712137013403 5ustar smbsmbxen-4.9.2/tools/libvchan/node-select.c0000664000175000017500000001012713256712137015752 0ustar smbsmb/** * @file * @section AUTHORS * * Copyright (C) 2010 Rafal Wojtczuk * * Authors: * Rafal Wojtczuk * Daniel De Graaf * * @section LICENSE * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; If not, see . * * @section DESCRIPTION * * This is a test program for libxenvchan. Communications are bidirectional, * with either server (grant offeror) or client able to read and write. */ #include #include #include #include #include #include #include static void usage(char** argv) { fprintf(stderr, "usage:\n" "\t%s [client|server] domainid nodepath [rbufsiz wbufsiz]\n", argv[0]); exit(1); } #define BUFSIZE 5000 char inbuf[BUFSIZE]; char outbuf[BUFSIZE]; int insiz = 0; int outsiz = 0; struct libxenvchan *ctrl = 0; static void vchan_wr(void) { int ret; if (!insiz) return; ret = libxenvchan_write(ctrl, inbuf, insiz); if (ret < 0) { fprintf(stderr, "vchan write failed\n"); exit(1); } if (ret > 0) { insiz -= ret; memmove(inbuf, inbuf + ret, insiz); } } static void stdout_wr(void) { int ret; if (!outsiz) return; ret = write(1, outbuf, outsiz); if (ret < 0 && errno != EAGAIN) exit(1); if (ret > 0) { outsiz -= ret; memmove(outbuf, outbuf + ret, outsiz); } } static int set_nonblocking(int fd, int nonblocking) { int flags = fcntl(fd, F_GETFL); if (flags == -1) return -1; if (nonblocking) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) return -1; return 0; } /** Simple libxenvchan application, both client and server. Both sides may write and read, both from the libxenvchan and from stdin/stdout (just like netcat). */ int main(int argc, char **argv) { int ret; int libxenvchan_fd; if (argc < 4 || argv[3][0] != '/') usage(argv); if (!strcmp(argv[1], "server")) { int rsiz = argc > 4 ? atoi(argv[4]) : 0; int wsiz = argc > 5 ? atoi(argv[5]) : 0; ctrl = libxenvchan_server_init(NULL, atoi(argv[2]), argv[3], rsiz, wsiz); } else if (!strcmp(argv[1], "client")) ctrl = libxenvchan_client_init(NULL, atoi(argv[2]), argv[3]); else usage(argv); if (!ctrl) { perror("libxenvchan_*_init"); exit(1); } if (set_nonblocking(0, 1) || set_nonblocking(1, 1)) { perror("set_nonblocking"); exit(1); } libxenvchan_fd = libxenvchan_fd_for_select(ctrl); for (;;) { fd_set rfds; fd_set wfds; FD_ZERO(&rfds); FD_ZERO(&wfds); if (insiz != BUFSIZE) FD_SET(0, &rfds); if (outsiz) FD_SET(1, &wfds); FD_SET(libxenvchan_fd, &rfds); ret = select(libxenvchan_fd + 1, &rfds, &wfds, NULL, NULL); if (ret < 0) { perror("select"); exit(1); } if (FD_ISSET(0, &rfds)) { ret = read(0, inbuf + insiz, BUFSIZE - insiz); if (ret < 0 && errno != EAGAIN) exit(1); if (ret == 0) { while (insiz) { vchan_wr(); libxenvchan_wait(ctrl); } return 0; } if (ret) insiz += ret; vchan_wr(); } if (FD_ISSET(libxenvchan_fd, &rfds)) { libxenvchan_wait(ctrl); vchan_wr(); } if (FD_ISSET(1, &wfds)) stdout_wr(); while (libxenvchan_data_ready(ctrl) && outsiz < BUFSIZE) { ret = libxenvchan_read(ctrl, outbuf + outsiz, BUFSIZE - outsiz); if (ret < 0) exit(1); outsiz += ret; stdout_wr(); } if (!libxenvchan_is_open(ctrl)) { if (set_nonblocking(1, 0)) { perror("set_nonblocking"); exit(1); } while (outsiz) stdout_wr(); return 0; } } } xen-4.9.2/tools/libvchan/init.c0000664000175000017500000002555313256712137014524 0ustar smbsmb/** * @file * @section AUTHORS * * Copyright (C) 2010 Rafal Wojtczuk * * Authors: * Rafal Wojtczuk * Daniel De Graaf * * @section LICENSE * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * @section DESCRIPTION * * This file contains the setup code used to establish the ring buffer. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PAGE_SHIFT #define PAGE_SHIFT 12 #endif #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif #define SMALL_RING_SHIFT 10 #define LARGE_RING_SHIFT 11 #define MAX_SMALL_RING (1 << SMALL_RING_SHIFT) #define SMALL_RING_OFFSET 1024 #define MAX_LARGE_RING (1 << LARGE_RING_SHIFT) #define LARGE_RING_OFFSET 2048 // if you go over this size, you'll have too many grants to fit in the shared page. #define MAX_RING_SHIFT 20 #define MAX_RING_SIZE (1 << MAX_RING_SHIFT) #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #define max(a,b) ((a > b) ? a : b) static int init_gnt_srv(struct libxenvchan *ctrl, int domain) { int pages_left = ctrl->read.order >= PAGE_SHIFT ? 1 << (ctrl->read.order - PAGE_SHIFT) : 0; int pages_right = ctrl->write.order >= PAGE_SHIFT ? 1 << (ctrl->write.order - PAGE_SHIFT) : 0; uint32_t ring_ref = -1; void *ring; ring = xengntshr_share_page_notify(ctrl->gntshr, domain, &ring_ref, 1, offsetof(struct vchan_interface, srv_live), ctrl->event_port); if (!ring) goto out; memset(ring, 0, PAGE_SIZE); ctrl->ring = ring; ctrl->read.shr = &ctrl->ring->left; ctrl->write.shr = &ctrl->ring->right; ctrl->ring->left_order = ctrl->read.order; ctrl->ring->right_order = ctrl->write.order; ctrl->ring->cli_live = 2; ctrl->ring->srv_live = 1; ctrl->ring->cli_notify = VCHAN_NOTIFY_WRITE; switch (ctrl->read.order) { case SMALL_RING_SHIFT: ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET; break; case LARGE_RING_SHIFT: ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET; break; default: ctrl->read.buffer = xengntshr_share_pages(ctrl->gntshr, domain, pages_left, ctrl->ring->grants, 1); if (!ctrl->read.buffer) goto out_ring; } switch (ctrl->write.order) { case SMALL_RING_SHIFT: ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET; break; case LARGE_RING_SHIFT: ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET; break; default: ctrl->write.buffer = xengntshr_share_pages(ctrl->gntshr, domain, pages_right, ctrl->ring->grants + pages_left, 1); if (!ctrl->write.buffer) goto out_unmap_left; } out: return ring_ref; out_unmap_left: if (pages_left) xengntshr_unshare(ctrl->gntshr, ctrl->read.buffer, pages_left); out_ring: xengntshr_unshare(ctrl->gntshr, ring, 1); ring_ref = -1; ctrl->ring = NULL; ctrl->write.order = ctrl->read.order = 0; goto out; } static int init_gnt_cli(struct libxenvchan *ctrl, int domain, uint32_t ring_ref) { int rv = -1; uint32_t *grants; ctrl->ring = xengnttab_map_grant_ref_notify(ctrl->gnttab, domain, ring_ref, PROT_READ|PROT_WRITE, offsetof(struct vchan_interface, cli_live), ctrl->event_port); if (!ctrl->ring) goto out; ctrl->write.order = ctrl->ring->left_order; ctrl->read.order = ctrl->ring->right_order; ctrl->write.shr = &ctrl->ring->left; ctrl->read.shr = &ctrl->ring->right; if (ctrl->write.order < SMALL_RING_SHIFT || ctrl->write.order > MAX_RING_SHIFT) goto out_unmap_ring; if (ctrl->read.order < SMALL_RING_SHIFT || ctrl->read.order > MAX_RING_SHIFT) goto out_unmap_ring; if (ctrl->read.order == ctrl->write.order && ctrl->read.order < PAGE_SHIFT) goto out_unmap_ring; grants = ctrl->ring->grants; switch (ctrl->write.order) { case SMALL_RING_SHIFT: ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET; break; case LARGE_RING_SHIFT: ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET; break; default: { int pages_left = 1 << (ctrl->write.order - PAGE_SHIFT); ctrl->write.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab, pages_left, domain, grants, PROT_READ|PROT_WRITE); if (!ctrl->write.buffer) goto out_unmap_ring; grants += pages_left; } } switch (ctrl->read.order) { case SMALL_RING_SHIFT: ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET; break; case LARGE_RING_SHIFT: ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET; break; default: { int pages_right = 1 << (ctrl->read.order - PAGE_SHIFT); ctrl->read.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab, pages_right, domain, grants, PROT_READ); if (!ctrl->read.buffer) goto out_unmap_left; } } rv = 0; out: return rv; out_unmap_left: if (ctrl->write.order >= PAGE_SHIFT) xengnttab_unmap(ctrl->gnttab, ctrl->write.buffer, 1 << (ctrl->write.order - PAGE_SHIFT)); out_unmap_ring: xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1); ctrl->ring = 0; ctrl->write.order = ctrl->read.order = 0; rv = -1; goto out; } static int init_evt_srv(struct libxenvchan *ctrl, int domain, struct xentoollog_logger *logger) { xenevtchn_port_or_error_t port; ctrl->event = xenevtchn_open(logger, 0); if (!ctrl->event) return -1; port = xenevtchn_bind_unbound_port(ctrl->event, domain); if (port < 0) goto fail; ctrl->event_port = port; if (xenevtchn_unmask(ctrl->event, ctrl->event_port)) goto fail; return 0; fail: if (port >= 0) xenevtchn_unbind(ctrl->event, port); xenevtchn_close(ctrl->event); ctrl->event = NULL; return -1; } static int init_xs_srv(struct libxenvchan *ctrl, int domain, const char* xs_base, int ring_ref) { int ret = -1; struct xs_handle *xs; struct xs_permissions perms[2]; char buf[64]; char ref[16]; char* domid_str = NULL; xs = xs_domain_open(); if (!xs) goto fail; domid_str = xs_read(xs, 0, "domid", NULL); if (!domid_str) goto fail_xs_open; // owner domain is us perms[0].id = atoi(domid_str); // permissions for domains not listed = none perms[0].perms = XS_PERM_NONE; // other domains perms[1].id = domain; perms[1].perms = XS_PERM_READ; snprintf(ref, sizeof ref, "%d", ring_ref); snprintf(buf, sizeof buf, "%s/ring-ref", xs_base); if (!xs_write(xs, 0, buf, ref, strlen(ref))) goto fail_xs_open; if (!xs_set_permissions(xs, 0, buf, perms, 2)) goto fail_xs_open; snprintf(ref, sizeof ref, "%d", ctrl->event_port); snprintf(buf, sizeof buf, "%s/event-channel", xs_base); if (!xs_write(xs, 0, buf, ref, strlen(ref))) goto fail_xs_open; if (!xs_set_permissions(xs, 0, buf, perms, 2)) goto fail_xs_open; ret = 0; fail_xs_open: free(domid_str); xs_daemon_close(xs); fail: return ret; } static int min_order(size_t siz) { int rv = PAGE_SHIFT; while (siz > (1 << rv)) rv++; return rv; } struct libxenvchan *libxenvchan_server_init(struct xentoollog_logger *logger, int domain, const char* xs_path, size_t left_min, size_t right_min) { struct libxenvchan *ctrl; int ring_ref; if (left_min > MAX_RING_SIZE || right_min > MAX_RING_SIZE) return 0; ctrl = malloc(sizeof(*ctrl)); if (!ctrl) return 0; ctrl->ring = NULL; ctrl->event = NULL; ctrl->is_server = 1; ctrl->server_persist = 0; ctrl->read.order = min_order(left_min); ctrl->write.order = min_order(right_min); // if we can avoid allocating extra pages by using in-page rings, do so if (left_min <= MAX_SMALL_RING && right_min <= MAX_LARGE_RING) { ctrl->read.order = SMALL_RING_SHIFT; ctrl->write.order = LARGE_RING_SHIFT; } else if (left_min <= MAX_LARGE_RING && right_min <= MAX_SMALL_RING) { ctrl->read.order = LARGE_RING_SHIFT; ctrl->write.order = SMALL_RING_SHIFT; } else if (left_min <= MAX_LARGE_RING) { ctrl->read.order = LARGE_RING_SHIFT; } else if (right_min <= MAX_LARGE_RING) { ctrl->write.order = LARGE_RING_SHIFT; } ctrl->gntshr = xengntshr_open(logger, 0); if (!ctrl->gntshr) goto out; if (init_evt_srv(ctrl, domain, logger)) goto out; ring_ref = init_gnt_srv(ctrl, domain); if (ring_ref < 0) goto out; if (init_xs_srv(ctrl, domain, xs_path, ring_ref)) goto out; return ctrl; out: libxenvchan_close(ctrl); return 0; } static int init_evt_cli(struct libxenvchan *ctrl, int domain, struct xentoollog_logger *logger) { xenevtchn_port_or_error_t port; ctrl->event = xenevtchn_open(logger, 0); if (!ctrl->event) return -1; port = xenevtchn_bind_interdomain(ctrl->event, domain, ctrl->event_port); if (port < 0) goto fail; ctrl->event_port = port; if (xenevtchn_unmask(ctrl->event, ctrl->event_port)) goto fail; return 0; fail: if (port >= 0) xenevtchn_unbind(ctrl->event, port); xenevtchn_close(ctrl->event); ctrl->event = NULL; return -1; } struct libxenvchan *libxenvchan_client_init(struct xentoollog_logger *logger, int domain, const char* xs_path) { struct libxenvchan *ctrl = malloc(sizeof(struct libxenvchan)); struct xs_handle *xs = NULL; char buf[64]; char *ref; int ring_ref; unsigned int len; if (!ctrl) return 0; ctrl->ring = NULL; ctrl->event = NULL; ctrl->gnttab = NULL; ctrl->write.order = ctrl->read.order = 0; ctrl->is_server = 0; xs = xs_daemon_open(); if (!xs) xs = xs_domain_open(); if (!xs) goto fail; // find xenstore entry snprintf(buf, sizeof buf, "%s/ring-ref", xs_path); ref = xs_read(xs, 0, buf, &len); if (!ref) goto fail; ring_ref = atoi(ref); free(ref); if (!ring_ref) goto fail; snprintf(buf, sizeof buf, "%s/event-channel", xs_path); ref = xs_read(xs, 0, buf, &len); if (!ref) goto fail; ctrl->event_port = atoi(ref); free(ref); if (!ctrl->event_port) goto fail; ctrl->gnttab = xengnttab_open(logger, 0); if (!ctrl->gnttab) goto fail; // set up event channel if (init_evt_cli(ctrl, domain, logger)) goto fail; // set up shared page(s) if (init_gnt_cli(ctrl, domain, ring_ref)) goto fail; ctrl->ring->cli_live = 1; ctrl->ring->srv_notify = VCHAN_NOTIFY_WRITE; out: if (xs) xs_daemon_close(xs); return ctrl; fail: libxenvchan_close(ctrl); ctrl = NULL; goto out; } xen-4.9.2/tools/libvchan/io.c0000664000175000017500000002351213256712137014161 0ustar smbsmb/** * @file * @section AUTHORS * * Copyright (C) 2010 Rafal Wojtczuk * * Authors: * Rafal Wojtczuk * Daniel De Graaf * * @section LICENSE * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * @section DESCRIPTION * * This file contains the communications interface built on the ring buffer. */ #include #include #include #include #include #include #include #include #include #include #ifndef PAGE_SHIFT #define PAGE_SHIFT 12 #endif #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif static inline uint32_t rd_prod(struct libxenvchan *ctrl) { return ctrl->read.shr->prod; } static inline uint32_t* _rd_cons(struct libxenvchan *ctrl) { return &ctrl->read.shr->cons; } #define rd_cons(x) (*_rd_cons(x)) static inline uint32_t* _wr_prod(struct libxenvchan *ctrl) { return &ctrl->write.shr->prod; } #define wr_prod(x) (*_wr_prod(x)) static inline uint32_t wr_cons(struct libxenvchan *ctrl) { return ctrl->write.shr->cons; } static inline const void* rd_ring(struct libxenvchan *ctrl) { return ctrl->read.buffer; } static inline void* wr_ring(struct libxenvchan *ctrl) { return ctrl->write.buffer; } static inline uint32_t wr_ring_size(struct libxenvchan *ctrl) { return (1 << ctrl->write.order); } static inline uint32_t rd_ring_size(struct libxenvchan *ctrl) { return (1 << ctrl->read.order); } static inline void request_notify(struct libxenvchan *ctrl, uint8_t bit) { uint8_t *notify = ctrl->is_server ? &ctrl->ring->cli_notify : &ctrl->ring->srv_notify; __sync_or_and_fetch(notify, bit); xen_mb(); /* post the request /before/ caller re-reads any indexes */ } static inline int send_notify(struct libxenvchan *ctrl, uint8_t bit) { uint8_t *notify, prev; xen_mb(); /* caller updates indexes /before/ we decode to notify */ notify = ctrl->is_server ? &ctrl->ring->srv_notify : &ctrl->ring->cli_notify; prev = __sync_fetch_and_and(notify, ~bit); if (prev & bit) return xenevtchn_notify(ctrl->event, ctrl->event_port); else return 0; } /* * Get the amount of buffer space available, and do nothing about * notifications. */ static inline int raw_get_data_ready(struct libxenvchan *ctrl) { uint32_t ready = rd_prod(ctrl) - rd_cons(ctrl); xen_mb(); /* Ensure 'ready' is read only once. */ if (ready > rd_ring_size(ctrl)) /* We have no way to return errors. Locking up the ring is * better than the alternatives. */ return 0; return ready; } /** * Get the amount of buffer space available and enable notifications if needed. */ static inline int fast_get_data_ready(struct libxenvchan *ctrl, size_t request) { int ready = raw_get_data_ready(ctrl); if (ready >= request) return ready; /* We plan to consume all data; please tell us if you send more */ request_notify(ctrl, VCHAN_NOTIFY_WRITE); /* * If the writer moved rd_prod after our read but before request, we * will not get notified even though the actual amount of data ready is * above request. Reread rd_prod to cover this case. */ return raw_get_data_ready(ctrl); } int libxenvchan_data_ready(struct libxenvchan *ctrl) { /* Since this value is being used outside libxenvchan, request notification * when it changes */ request_notify(ctrl, VCHAN_NOTIFY_WRITE); return raw_get_data_ready(ctrl); } /** * Get the amount of buffer space available, and do nothing * about notifications */ static inline int raw_get_buffer_space(struct libxenvchan *ctrl) { uint32_t ready = wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl)); xen_mb(); /* Ensure 'ready' is read only once. */ if (ready > wr_ring_size(ctrl)) /* We have no way to return errors. Locking up the ring is * better than the alternatives. */ return 0; return ready; } /** * Get the amount of buffer space available and enable notifications if needed. */ static inline int fast_get_buffer_space(struct libxenvchan *ctrl, size_t request) { int ready = raw_get_buffer_space(ctrl); if (ready >= request) return ready; /* We plan to fill the buffer; please tell us when you've read it */ request_notify(ctrl, VCHAN_NOTIFY_READ); /* * If the reader moved wr_cons after our read but before request, we * will not get notified even though the actual amount of buffer space * is above request. Reread wr_cons to cover this case. */ return raw_get_buffer_space(ctrl); } int libxenvchan_buffer_space(struct libxenvchan *ctrl) { /* Since this value is being used outside libxenvchan, request notification * when it changes */ request_notify(ctrl, VCHAN_NOTIFY_READ); return raw_get_buffer_space(ctrl); } int libxenvchan_wait(struct libxenvchan *ctrl) { int ret = xenevtchn_pending(ctrl->event); if (ret < 0) return -1; xenevtchn_unmask(ctrl->event, ret); return 0; } /** * returns -1 on error, or size on success * * caller must have checked that enough space is available */ static int do_send(struct libxenvchan *ctrl, const void *data, size_t size) { int real_idx = wr_prod(ctrl) & (wr_ring_size(ctrl) - 1); int avail_contig = wr_ring_size(ctrl) - real_idx; if (avail_contig > size) avail_contig = size; xen_mb(); /* read indexes /then/ write data */ memcpy(wr_ring(ctrl) + real_idx, data, avail_contig); if (avail_contig < size) { // we rolled across the end of the ring memcpy(wr_ring(ctrl), data + avail_contig, size - avail_contig); } xen_wmb(); /* write data /then/ notify */ wr_prod(ctrl) += size; if (send_notify(ctrl, VCHAN_NOTIFY_WRITE)) return -1; return size; } /** * returns 0 if no buffer space is available, -1 on error, or size on success */ int libxenvchan_send(struct libxenvchan *ctrl, const void *data, size_t size) { int avail; while (1) { if (!libxenvchan_is_open(ctrl)) return -1; avail = fast_get_buffer_space(ctrl, size); if (size <= avail) return do_send(ctrl, data, size); if (!ctrl->blocking) return 0; if (size > wr_ring_size(ctrl)) return -1; if (libxenvchan_wait(ctrl)) return -1; } } int libxenvchan_write(struct libxenvchan *ctrl, const void *data, size_t size) { int avail; if (!libxenvchan_is_open(ctrl)) return -1; if (ctrl->blocking) { size_t pos = 0; while (1) { avail = fast_get_buffer_space(ctrl, size - pos); if (pos + avail > size) avail = size - pos; if (avail) pos += do_send(ctrl, data + pos, avail); if (pos == size) return pos; if (libxenvchan_wait(ctrl)) return -1; if (!libxenvchan_is_open(ctrl)) return -1; } } else { avail = fast_get_buffer_space(ctrl, size); if (size > avail) size = avail; if (size == 0) return 0; return do_send(ctrl, data, size); } } /** * returns -1 on error, or size on success * * caller must have checked that enough data is available */ static int do_recv(struct libxenvchan *ctrl, void *data, size_t size) { int real_idx = rd_cons(ctrl) & (rd_ring_size(ctrl) - 1); int avail_contig = rd_ring_size(ctrl) - real_idx; if (avail_contig > size) avail_contig = size; xen_rmb(); /* data read must happen /after/ rd_cons read */ memcpy(data, rd_ring(ctrl) + real_idx, avail_contig); if (avail_contig < size) { // we rolled across the end of the ring memcpy(data + avail_contig, rd_ring(ctrl), size - avail_contig); } xen_mb(); /* consume /then/ notify */ rd_cons(ctrl) += size; if (send_notify(ctrl, VCHAN_NOTIFY_READ)) return -1; return size; } /** * reads exactly size bytes from the vchan. * returns 0 if insufficient data is available, -1 on error, or size on success */ int libxenvchan_recv(struct libxenvchan *ctrl, void *data, size_t size) { while (1) { int avail = fast_get_data_ready(ctrl, size); if (size <= avail) return do_recv(ctrl, data, size); if (!libxenvchan_is_open(ctrl)) return -1; if (!ctrl->blocking) return 0; if (size > rd_ring_size(ctrl)) return -1; if (libxenvchan_wait(ctrl)) return -1; } } int libxenvchan_read(struct libxenvchan *ctrl, void *data, size_t size) { while (1) { int avail = fast_get_data_ready(ctrl, size); if (avail && size > avail) size = avail; if (avail) return do_recv(ctrl, data, size); if (!libxenvchan_is_open(ctrl)) return -1; if (!ctrl->blocking) return 0; if (libxenvchan_wait(ctrl)) return -1; } } int libxenvchan_is_open(struct libxenvchan* ctrl) { if (ctrl->is_server) return ctrl->server_persist ? 1 : ctrl->ring->cli_live; else return ctrl->ring->srv_live; } int libxenvchan_fd_for_select(struct libxenvchan *ctrl) { return xenevtchn_fd(ctrl->event); } void libxenvchan_close(struct libxenvchan *ctrl) { if (!ctrl) return; if (ctrl->read.order >= PAGE_SHIFT) munmap(ctrl->read.buffer, 1 << ctrl->read.order); if (ctrl->write.order >= PAGE_SHIFT) munmap(ctrl->write.buffer, 1 << ctrl->write.order); if (ctrl->ring) { if (ctrl->is_server) { ctrl->ring->srv_live = 0; xengntshr_unshare(ctrl->gntshr, ctrl->ring, 1); } else { ctrl->ring->cli_live = 0; xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1); } } if (ctrl->event) { if (ctrl->ring) xenevtchn_notify(ctrl->event, ctrl->event_port); xenevtchn_close(ctrl->event); } if (ctrl->is_server) { if (ctrl->gntshr) xengntshr_close(ctrl->gntshr); } else { if (ctrl->gnttab) xengnttab_close(ctrl->gnttab); } free(ctrl); } xen-4.9.2/tools/libvchan/node.c0000664000175000017500000000706113256712137014500 0ustar smbsmb/** * @file * @section AUTHORS * * Copyright (C) 2010 Rafal Wojtczuk * * Authors: * Rafal Wojtczuk * Daniel De Graaf * * @section LICENSE * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; If not, see . * * @section DESCRIPTION * * This is a test program for libxenvchan. Communications are in one direction, * either server (grant offeror) to client or vice versa. */ #include #include #include #include #include #include int libxenvchan_write_all(struct libxenvchan *ctrl, char *buf, int size) { int written = 0; int ret; while (written < size) { ret = libxenvchan_write(ctrl, buf + written, size - written); if (ret <= 0) { perror("write"); exit(1); } written += ret; } return size; } int write_all(int fd, char *buf, int size) { int written = 0; int ret; while (written < size) { ret = write(fd, buf + written, size - written); if (ret <= 0) { perror("write"); exit(1); } written += ret; } return size; } void usage(char** argv) { fprintf(stderr, "usage:\n" "%s [client|server] [read|write] domid nodepath\n", argv[0]); exit(1); } #define BUFSIZE 5000 char buf[BUFSIZE]; void reader(struct libxenvchan *ctrl) { int size; for (;;) { size = rand() % (BUFSIZE - 1) + 1; size = libxenvchan_read(ctrl, buf, size); fprintf(stderr, "#"); if (size < 0) { perror("read vchan"); libxenvchan_close(ctrl); exit(1); } size = write_all(1, buf, size); if (size < 0) { perror("stdout write"); exit(1); } if (size == 0) { perror("write size=0?\n"); exit(1); } } } void writer(struct libxenvchan *ctrl) { int size; for (;;) { size = rand() % (BUFSIZE - 1) + 1; size = read(0, buf, size); if (size < 0) { perror("read stdin"); libxenvchan_close(ctrl); exit(1); } if (size == 0) break; size = libxenvchan_write_all(ctrl, buf, size); fprintf(stderr, "#"); if (size < 0) { perror("vchan write"); exit(1); } if (size == 0) { perror("write size=0?\n"); exit(1); } } } /** Simple libxenvchan application, both client and server. One side does writing, the other side does reading; both from standard input/output fds. */ int main(int argc, char **argv) { int seed = time(0); struct libxenvchan *ctrl = 0; int wr = 0; if (argc < 4) usage(argv); if (!strcmp(argv[2], "read")) wr = 0; else if (!strcmp(argv[2], "write")) wr = 1; else usage(argv); if (!strcmp(argv[1], "server")) ctrl = libxenvchan_server_init(NULL, atoi(argv[3]), argv[4], 0, 0); else if (!strcmp(argv[1], "client")) ctrl = libxenvchan_client_init(NULL, atoi(argv[3]), argv[4]); else usage(argv); if (!ctrl) { perror("libxenvchan_*_init"); exit(1); } ctrl->blocking = 1; srand(seed); fprintf(stderr, "seed=%d\n", seed); if (wr) writer(ctrl); else reader(ctrl); libxenvchan_close(ctrl); return 0; } xen-4.9.2/tools/libvchan/Makefile0000664000175000017500000000503013256712137015041 0ustar smbsmb# # tools/libvchan/Makefile # XEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk LIBVCHAN_OBJS = init.o io.o NODE_OBJS = node.o NODE2_OBJS = node-select.o LIBVCHAN_PIC_OBJS = $(patsubst %.o,%.opic,$(LIBVCHAN_OBJS)) LIBVCHAN_LIBS = $(LDLIBS_libxenstore) $(LDLIBS_libxengnttab) $(LDLIBS_libxenevtchn) $(LIBVCHAN_OBJS) $(LIBVCHAN_PIC_OBJS): CFLAGS += $(CFLAGS_libxenstore) $(CFLAGS_libxengnttab) $(CFLAGS_libxenevtchn) $(NODE_OBJS) $(NODE2_OBJS): CFLAGS += $(CFLAGS_libxengnttab) $(CFLAGS_libxenevtchn) MAJOR = 4.9 MINOR = 0 CFLAGS += -I../include -I. io.o io.opic: CFLAGS += $(CFLAGS_libxenctrl) # for xen_mb et al PKG_CONFIG := xenvchan.pc PKG_CONFIG_VERSION := $(MAJOR).$(MINOR) ifneq ($(CONFIG_LIBXC_MINIOS),y) PKG_CONFIG_INST := $(PKG_CONFIG) $(PKG_CONFIG_INST): PKG_CONFIG_PREFIX = $(prefix) $(PKG_CONFIG_INST): PKG_CONFIG_INCDIR = $(includedir) $(PKG_CONFIG_INST): PKG_CONFIG_LIBDIR = $(libdir) endif PKG_CONFIG_LOCAL := $(foreach pc,$(PKG_CONFIG),$(PKG_CONFIG_DIR)/$(pc)) $(PKG_CONFIG_LOCAL): PKG_CONFIG_PREFIX = $(XEN_ROOT) $(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(XEN_LIBVCHAN) $(PKG_CONFIG_LOCAL): PKG_CONFIG_LIBDIR = $(CURDIR) $(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude) .PHONY: all all: libxenvchan.so vchan-node1 vchan-node2 libxenvchan.a $(PKG_CONFIG_INST) $(PKG_CONFIG_LOCAL) libxenvchan.so: libxenvchan.so.$(MAJOR) ln -sf $< $@ libxenvchan.so.$(MAJOR): libxenvchan.so.$(MAJOR).$(MINOR) ln -sf $< $@ libxenvchan.so.$(MAJOR).$(MINOR): $(LIBVCHAN_PIC_OBJS) $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenvchan.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LIBVCHAN_LIBS) $(APPEND_LDFLAGS) libxenvchan.a: $(LIBVCHAN_OBJS) $(AR) rcs libxenvchan.a $^ vchan-node1: $(NODE_OBJS) libxenvchan.so $(CC) $(LDFLAGS) -o $@ $(NODE_OBJS) $(LDLIBS_libxenvchan) $(APPEND_LDFLAGS) vchan-node2: $(NODE2_OBJS) libxenvchan.so $(CC) $(LDFLAGS) -o $@ $(NODE2_OBJS) $(LDLIBS_libxenvchan) $(APPEND_LDFLAGS) .PHONY: install install: all $(INSTALL_DIR) $(DESTDIR)$(libdir) $(INSTALL_DIR) $(DESTDIR)$(includedir) $(INSTALL_PROG) libxenvchan.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir) ln -sf libxenvchan.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libxenvchan.so.$(MAJOR) ln -sf libxenvchan.so.$(MAJOR) $(DESTDIR)$(libdir)/libxenvchan.so $(INSTALL_DATA) libxenvchan.h $(DESTDIR)$(includedir) $(INSTALL_DATA) libxenvchan.a $(DESTDIR)$(libdir) $(INSTALL_DATA) xenvchan.pc $(DESTDIR)$(PKG_INSTALLDIR) .PHONY: clean clean: $(RM) -f *.o *.opic *.so* *.a vchan-node1 vchan-node2 $(DEPS) $(RM) -f xenvchan.pc distclean: clean -include $(DEPS) xen-4.9.2/tools/libvchan/libxenvchan.h0000664000175000017500000001570213256712137016062 0ustar smbsmb/** * @file * @section AUTHORS * * Copyright (C) 2010 Rafal Wojtczuk * * Authors: * Rafal Wojtczuk * Daniel De Graaf * * @section LICENSE * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * @section DESCRIPTION * * Originally borrowed from the Qubes OS Project, http://www.qubes-os.org, * this code has been substantially rewritten to use the gntdev and gntalloc * devices instead of raw MFNs and map_foreign_range. * * This is a library for inter-domain communication. A standard Xen ring * buffer is used, with a datagram-based interface built on top. The grant * reference and event channels are shared in XenStore under the path * /local/domain//data/vchan///{ring-ref,event-channel} * * The ring.h macros define an asymmetric interface to a shared data structure * that assumes all rings reside in a single contiguous memory space. This is * not suitable for vchan because the interface to the ring is symmetric except * for the setup. Unlike the producer-consumer rings defined in ring.h, the * size of the rings used in vchan are determined at execution time instead of * compile time, so the macros in ring.h cannot be used to access the rings. */ #include #include #include #include /* Callers who don't care don't need to #include */ struct xentoollog_logger; struct libxenvchan_ring { /* Pointer into the shared page. Offsets into buffer. */ struct ring_shared* shr; /* ring data; may be its own shared page(s) depending on order */ void* buffer; /** * The size of the ring is (1 << order); offsets wrap around when they * exceed this. This copy is required because we can't trust the order * in the shared page to remain constant. */ int order; }; /** * struct libxenvchan: control structure passed to all library calls */ struct libxenvchan { /* Mapping handle for shared ring page */ union { xengntshr_handle *gntshr; /* for server */ xengnttab_handle *gnttab; /* for client */ }; /* Pointer to shared ring page */ struct vchan_interface *ring; /* event channel interface */ xenevtchn_handle *event; uint32_t event_port; /* informative flags: are we acting as server? */ int is_server:1; /* true if server remains active when client closes (allows reconnection) */ int server_persist:1; /* true if operations should block instead of returning 0 */ int blocking:1; /* communication rings */ struct libxenvchan_ring read, write; }; /** * Set up a vchan, including granting pages * @param logger Logger for libxc errors * @param domain The peer domain that will be connecting * @param xs_path Base xenstore path for storing ring/event data * @param send_min The minimum size (in bytes) of the send ring (left) * @param recv_min The minimum size (in bytes) of the receive ring (right) * @return The structure, or NULL in case of an error */ struct libxenvchan *libxenvchan_server_init(struct xentoollog_logger *logger, int domain, const char* xs_path, size_t read_min, size_t write_min); /** * Connect to an existing vchan. Note: you can reconnect to an existing vchan * safely, however no locking is performed, so you must prevent multiple clients * from connecting to a single server. * * @param logger Logger for libxc errors * @param domain The peer domain to connect to * @param xs_path Base xenstore path for storing ring/event data * @return The structure, or NULL in case of an error */ struct libxenvchan *libxenvchan_client_init(struct xentoollog_logger *logger, int domain, const char* xs_path); /** * Close a vchan. This deallocates the vchan and attempts to free its * resources. The other side is notified of the close, but can still read any * data pending prior to the close. */ void libxenvchan_close(struct libxenvchan *ctrl); /** * Packet-based receive: always reads exactly $size bytes. * @param ctrl The vchan control structure * @param data Buffer for data that was read * @param size Size of the buffer and amount of data to read * @return -1 on error, 0 if nonblocking and insufficient data is available, or $size */ int libxenvchan_recv(struct libxenvchan *ctrl, void *data, size_t size); /** * Stream-based receive: reads as much data as possible. * @param ctrl The vchan control structure * @param data Buffer for data that was read * @param size Size of the buffer * @return -1 on error, otherwise the amount of data read (which may be zero if * the vchan is nonblocking) */ int libxenvchan_read(struct libxenvchan *ctrl, void *data, size_t size); /** * Packet-based send: send entire buffer if possible * @param ctrl The vchan control structure * @param data Buffer for data to send * @param size Size of the buffer and amount of data to send * @return -1 on error, 0 if nonblocking and insufficient space is available, or $size */ int libxenvchan_send(struct libxenvchan *ctrl, const void *data, size_t size); /** * Stream-based send: send as much data as possible. * @param ctrl The vchan control structure * @param data Buffer for data to send * @param size Size of the buffer * @return -1 on error, otherwise the amount of data sent (which may be zero if * the vchan is nonblocking) */ int libxenvchan_write(struct libxenvchan *ctrl, const void *data, size_t size); /** * Waits for reads or writes to unblock, or for a close */ int libxenvchan_wait(struct libxenvchan *ctrl); /** * Returns the event file descriptor for this vchan. When this FD is readable, * libxenvchan_wait() will not block, and the state of the vchan has changed since * the last invocation of libxenvchan_wait(). */ int libxenvchan_fd_for_select(struct libxenvchan *ctrl); /** * Query the state of the vchan shared page: * return 0 when one side has called libxenvchan_close() or crashed * return 1 when both sides are open * return 2 [server only] when no client has yet connected */ int libxenvchan_is_open(struct libxenvchan* ctrl); /** Amount of data ready to read, in bytes */ int libxenvchan_data_ready(struct libxenvchan *ctrl); /** Amount of data it is possible to send without blocking */ int libxenvchan_buffer_space(struct libxenvchan *ctrl); xen-4.9.2/tools/libvchan/xenvchan.pc.in0000664000175000017500000000043513256712137016150 0ustar smbsmbprefix=@@prefix@@ includedir=@@incdir@@ libdir=@@libdir@@ Name: Xenvchan Description: The Xenvchan library for Xen hypervisor Version: @@version@@ Cflags: -I${includedir} @@cflagslocal@@ Libs: @@libsflag@@${libdir} -lxenvchan Requires.private: xentoollog,xenstore,xenevtchn,xengnttab xen-4.9.2/tools/examples/0000775000175000017500000000000013256712137013433 5ustar smbsmbxen-4.9.2/tools/examples/xl.conf0000664000175000017500000000242713256712137014732 0ustar smbsmb## Global XL config file ## # Control whether dom0 is ballooned down when xen doesn't have enough # free memory to create a domain. "auto" means only balloon if dom0 # starts with all the host's memory. #autoballoon="auto" # full path of the lockfile used by xl during domain creation #lockfile="/var/lock/xl" # default output format used by "xl list -l" #output_format="json" # first block device to be used for temporary VM disk mounts #blkdev_start="xvda" # default option to run hotplug scripts from xl # if disabled the old behaviour will be used, and hotplug scripts will be # launched by udev. #run_hotplug_scripts=1 # default backend domain to connect guest vifs to. This can be any # valid domain identifier. #vif.default.backend="0" # default gateway device to use with vif-route hotplug script #vif.default.gatewaydev="eth0" # default vif script to use if none is specified in the guest config #vif.default.script="vif-bridge" # default bridge device to use with vif-bridge hotplug scripts #vif.default.bridge="xenbr0" # Reserve a claim of memory when launching a guest. This guarantees immediate # feedback whether the guest can be launched due to memory exhaustion # (which can take a long time to find out if launching huge guests). # see xl.conf(5) for details. #claim_mode=1 xen-4.9.2/tools/examples/xeninfo.pl0000664000175000017500000002752513256712137015451 0ustar smbsmb#!/usr/bin/perl -w ############################################################################################################# # # # Developed by Ingard Mevåg @ Oslo University College, spring 2007 # # ingard [at] mevaag [dot] no # # # # This work is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 License. # # To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter # # to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. # # # ############################################################################################################# use strict; # http://search.cpan.org/~rjray/RPC-XML-0.59/lib/RPC/XML/Client.pm require RPC::XML; require RPC::XML::Client; # for debug purposes #use Data::Dumper; ##### CONFIG ###### my %xenhosts = ("192.0.2.10" => {"port" => "9363"}, "192.0.2.11" => {"port" => "9363"}, "192.0.2.12" => {"port" => "9363"}, "192.0.2.13" => {"port" => "9363"}); ##### CONFIG END ### ##### STATIC VARS ##### my %host_info; ####################### sub apiconnect { foreach my $xenhost (keys %xenhosts) { my $xen = RPC::XML::Client->new("http://$xenhost:$xenhosts{$xenhost}{'port'}"); my $session = $xen->simple_request("session.login_with_password", "user",""); if (! $session) { print "Can't connect to $xenhost :(\n"; $xenhosts{$xenhost} = {'xen' => $xen, 'session' => ""}; } else { $xenhosts{$xenhost} = {'xen' => $xen, 'session' => $session->{'Value'}}; print "Connected successfully to $xenhost..\n"; } } } sub validate_response { my ($result_ref) = @_; if ($result_ref->{'Status'} eq "Success") { return $result_ref->{'Value'}; } else { # status = Failure ! # die ("xmlrpc failed! ErrorDescription: $result_ref->{'ErrorDescription'}[1] -> $result_ref->{'ErrorDescription'}[0]"); print "xmlrpc failed! ErrorDescription: $result_ref->{'ErrorDescription'}[1] -> $result_ref->{'ErrorDescription'}[0]\n"; } } sub get_host_cpu_utilisation { my ($xen, $session, $host_name, $host_ref) = @_; my $host_cpu_ref = validate_response($xen->simple_request("host.get_host_CPUs", $session, $host_ref)); foreach (@$host_cpu_ref) { my $host_cpu_utilisation = validate_response($xen->simple_request("host_cpu.get_utilisation", $session, $_)); $host_info{$host_name}{'cpus'}{$_} = $host_cpu_utilisation; print " CPUiNFO: $host_cpu_utilisation\n"; } } sub get_host_pif_utilisation { my ($xen, $session, $host_name, $host_ref) = @_; # This method isnt implemented yet it seems so using PIF.get_all for now.. # This will break when xen is made cluster aware.. # my $host_pif_ref = validate_response($xen->simple_request("host.get_PIFs", $session, $host_ref)); my $host_pif_ref = validate_response($xen->simple_request("PIF.get_all", $session)); foreach (@$host_pif_ref) { my $host_pif_device = validate_response($xen->simple_request("PIF.get_device", $session, $_)); my $host_pif_metrics_ref = validate_response($xen->simple_request("PIF.get_metrics", $session, $_)); # Whats the best solution performancewise? # Collecting stats from get_records, or pulling individually? # my $host_pif_record = validate_response($xen->simple_request("PIF_metrics.get_record", $session, $host_pif_metrics_ref)); # my $host_pif_io_read = $host_pif_record->{'io_read_kbs'}; # my $host_pif_io_write = $host_pif_record->{'io_write_kbs'}; my $host_pif_io_read = validate_response($xen->simple_request("PIF_metrics.get_io_read_kbs", $session, $host_pif_metrics_ref)); my $host_pif_io_write = validate_response($xen->simple_request("PIF_metrics.get_io_write_kbs", $session, $host_pif_metrics_ref)); $host_info{$host_name}{'pifs'}{$host_pif_device} = {'read' => $host_pif_io_read, 'write' => $host_pif_io_write}; print " PiFiNFO: $host_pif_device READ: $host_pif_io_read - WRITE: $host_pif_io_write\n"; # $host_info{$host_name}{'pifs'}{$host_pif_device}{'read'} = $host_pif_io_read; # $host_info{$host_name}{'pifs'}{$host_pif_device}{'write'} = $host_pif_io_write; } } sub get_host_mem_utilisation { my ($xen, $session, $host_name, $host_ref) = @_; my $host_metrics_ref = validate_response($xen->simple_request("host.get_metrics", $session, $host_ref)); my $host_mem_total = validate_response($xen->simple_request("host_metrics.get_memory_total", $session, $host_metrics_ref)) / 1024 / 1024; my $host_mem_free = validate_response($xen->simple_request("host_metrics.get_memory_free", $session, $host_metrics_ref)) / 1024 / 1024; $host_info{$host_name}{'memory'} = {'total' => $host_mem_total, 'free' => $host_mem_free}; print " MEMiNFO: Total: $host_mem_total MB - Free: $host_mem_free MB\n"; } sub get_vm_mem_info { my ($xen, $session, $host_name, $vm_ref, $vm_name_label) = @_; my $vm_mem_stat_max = validate_response($xen->simple_request("VM.get_memory_static_max",$session,$vm_ref)); my $vm_mem_stat_min = validate_response($xen->simple_request("VM.get_memory_static_min",$session,$vm_ref)); my $vm_mem_dyn_max = validate_response($xen->simple_request("VM.get_memory_dynamic_max",$session,$vm_ref)); my $vm_mem_dyn_min = validate_response($xen->simple_request("VM.get_memory_dynamic_min",$session,$vm_ref)); # not implemented yet.. We'll do this at the same time as getting cpu utilisation # in the get_vm_metrics sub instead.. #my $vm_metrics_ref = validate_response($xen->simple_request("VM.get_metrics",$session,$vm_ref)); #my $vm_mem_actual = validate_response($xen->simple_request("VM_metrics.get_memory_actual",$session,$vm_metrics_ref)); $host_info{$host_name}{'vms'}{$vm_name_label}{'memory'} = {'static_max' => $vm_mem_stat_max, 'static_min' => $vm_mem_stat_min, 'dynamic_max' => $vm_mem_dyn_max, 'dynamic_min' => $vm_mem_dyn_min}; # xm list uses the dynamic min var as far as i can tell.. or? # Lets print the memactual info instead of this... I'll do that in the get_vm_metrics sub instead.. # print " |- MEMiNFO: Dynamic Min: $vm_mem_dyn_min - Actually in use: $vm_mem_actual\n"; } sub get_vm_metrics { my ($xen, $session, $host_name, $vm_ref, $vm_name_label) = @_; my $vm_metrics_ref = validate_response($xen->simple_request("VM.get_metrics",$session,$vm_ref)); my %vm_vcpu_utilisation = %{validate_response($xen->simple_request("VM_metrics.get_vcpus_utilisation",$session,$vm_metrics_ref))}; for my $tempcpu (keys %vm_vcpu_utilisation) { print " |- CPUiNFO: $tempcpu - $vm_vcpu_utilisation{$tempcpu}\n"; $host_info{$host_name}{'vms'}{$vm_name_label}{'vcpus'} = {$tempcpu => $vm_vcpu_utilisation{$tempcpu}}; } my $vm_mem_actual = validate_response($xen->simple_request("VM_metrics.get_memory_actual",$session,$vm_metrics_ref)) / 1024 / 1024; $host_info{$host_name}{'vms'}{$vm_name_label}{'memory'}{'actual'} = "$vm_mem_actual"; print " |- MEMiNFO: Actually in use: $vm_mem_actual MB\n"; } sub get_vm_vif_utilisation { my ($xen, $session, $host_name, $vm_ref, $vm_name_label) = @_; my $vm_vifs = validate_response($xen->simple_request("VM.get_VIFs",$session,$vm_ref)); foreach (@$vm_vifs) { my $vif_device = validate_response($xen->simple_request("VIF.get_device",$session,$_)); my $vif_io_read = validate_response($xen->simple_request("VIF_metrics.get_io_read_kbs", $session, $_)); my $vif_io_write = validate_response($xen->simple_request("VIF_metrics.get_io_write_kbs", $session, $_)); $host_info{$host_name}{'vms'}{$vm_name_label}{'vifs'}{$vif_device} = {'read' => $vif_io_read, 'write' => $vif_io_write}; print " |- ViFiNFO: $vif_device READ: $vif_io_read - WRITE: $vif_io_write\n"; } } sub get_vm_vbd_utilisation { my ($xen, $session, $host_name, $vm_ref, $vm_name_label) = @_; my $vm_vbds = validate_response($xen->simple_request("VM.get_VBDs",$session,$vm_ref)); foreach (@$vm_vbds) { my $vbd_device = validate_response($xen->simple_request("VBD.get_device",$session,$_)); my $vbd_io_read = validate_response($xen->simple_request("VBD_metrics.get_io_read_kbs", $session, $_)); my $vbd_io_write = validate_response($xen->simple_request("VBD_metrics.get_io_write_kbs", $session, $_)); $host_info{$host_name}{'vms'}{$vm_name_label}{'vbds'}{$vbd_device} = {'read' => $vbd_io_read, 'write' => $vbd_io_write}; print " |- VBDiNFO: $vbd_device READ: $vbd_io_read - WRITE: $vbd_io_write\n"; } } sub get_vm_type { my ($xen, $session, $host_name, $vm_ref, $vm_name_label) = @_; # not running response through validate_response() here to stop it from crashing.. # # api docs says if this (following) field is set, its a HVM domain. my $vm_bootloader_results = $xen->simple_request("VM.get_HVM_boot_policy",$session,$vm_ref); if ("$vm_bootloader_results->{'Status'}" eq "Success") { if ("$vm_bootloader_results->{'Value'}" ne "") { $host_info{$host_name}{'vms'}{$vm_name_label}{'type'} = "HVM"; } else { $host_info{$host_name}{'vms'}{$vm_name_label}{'type'} = "PV"; } } else { # However, xen 3.0.4 doest support this part of the api, so afaik I can get the difference with: my $vm_pv_kernel_results = $xen->simple_request("VM.get_PV_kernel",$session,$vm_ref); # which is something like: # 'PV_kernel': '/boot/vmlinuz-2.6.18-xen', # or # 'PV_kernel': 'hvmloader', if ("$vm_pv_kernel_results->{'Value'}" =~ m/hvm/i) { $host_info{$host_name}{'vms'}{$vm_name_label}{'type'} = "HVM"; } else { $host_info{$host_name}{'vms'}{$vm_name_label}{'type'} = "PV"; } } } sub get_complete_info { my %all_vms; foreach my $xenhost (sort keys %xenhosts) { next unless $xenhosts{$xenhost}{'session'}; my $xen = $xenhosts{$xenhost}{'xen'}; my $session = $xenhosts{$xenhost}{'session'}; print "_______________________\n## $xenhost ##\n-----------------------\n"; my $host_ref = validate_response($xen->simple_request("session.get_this_host", $session)); my $host_name = validate_response($xen->simple_request("host.get_name_label", $session, $host_ref)); $xenhosts{$xenhost}{'hostname'} = $host_name; $host_info{$host_name}{'ip'} = $xenhost; get_host_cpu_utilisation($xen, $session, $host_name, $host_ref); get_host_mem_utilisation($xen, $session, $host_name, $host_ref); get_host_pif_utilisation($xen, $session, $host_name, $host_ref); my $all_vm_refs = validate_response($xen->simple_request("host.get_resident_VMs",$session, $host_ref)); foreach my $vm_ref (@$all_vm_refs) { my $vm_name_label = validate_response($xen->simple_request("VM.get_name_label",$session,$vm_ref)); get_vm_type($xen,$session,$host_name,$vm_ref,$vm_name_label); my $vm_id = validate_response($xen->simple_request("VM.get_domid",$session,$vm_ref)); print "vm: $vm_id\t$vm_name_label\ttype: $host_info{$host_name}{'vms'}->{$vm_name_label}{'type'}\n"; # vm_metrics includes both mem_actual & cpu utilisation # So we'll add all stats found in that class in one go.. get_vm_metrics($xen,$session,$host_name,$vm_ref,$vm_name_label); # get_vm_cpu_utilisation($xen,$session,$host_name,$vm_ref,$vm_name_label); # all other mem stats are added seperately.. # This might not be needed at all as xen doesnt have functionality to # resize mem for a VM atm (afaik) get_vm_mem_info($xen,$session,$host_name,$vm_ref,$vm_name_label); get_vm_vif_utilisation($xen,$session,$host_name,$vm_ref,$vm_name_label); get_vm_vbd_utilisation($xen,$session,$host_name,$vm_ref,$vm_name_label); $all_vms{$vm_name_label} = "" unless ("$vm_name_label" eq "Domain-0"); } print "\n"; } # Debug: Uncomment to see the nested datastructure.. #print Dumper(%host_info); } apiconnect(); get_complete_info(); xen-4.9.2/tools/examples/cpupool0000664000175000017500000000122513256712137015037 0ustar smbsmb#============================================================================ # Configuration setup for 'xm cpupool-create' or 'xl cpupool-create'. # This script sets the parameters used when a cpupool is created using # 'xm cpupool-create' or 'xl cpupool-create'. # You use a separate script for each cpupool you want to create, or # you can set the parameters for the cpupool on the xm command line. #============================================================================ # the name of the new cpupool name = "Example-Cpupool" # the scheduler to use: valid are e.g. credit, credit2 and rtds sched = "credit" # list of cpus to use cpus = ["2", "3"] xen-4.9.2/tools/examples/vnc/0000775000175000017500000000000013256712137014221 5ustar smbsmbxen-4.9.2/tools/examples/vnc/Xvnc-xen0000775000175000017500000000267013256712137015662 0ustar smbsmb#!/bin/bash #============================================================================ # This script should be installed in /usr/X11R6/bin/Xvnc-xen. #============================================================================ # # Start Xvnc and use vncconnect to connect back to a vncviewer listening in # domain 0. The host and port to connect to are given by # # VNC_VIEWER=: # # in the kernel command line (/proc/cmdline). # # The '--vnc' option to 'xm create' will start a vncviewer and # pass its address in VNC_VIEWER for this script to find. # # Usage: # Xvnc-xen [args] # # Any arguments are passed to Xvnc. # #============================================================================ # Prefix for messages. M="[$(basename $0)]" # Usage: vnc_addr # Print : for the vncviewer given in # the kernel command line. vnc_addr () { sed -n -e "s/.*VNC_VIEWER=\([^ ]*\).*/\1/p" /proc/cmdline } # Usage: vnc_connect # If a vncviewer address was given on the kernel command line, # run vncconnect for it. vnc_connect () { local addr=$(vnc_addr) if [ -n "${addr}" ] ; then echo "$M Connecting to ${addr}." vncconnect ${addr} else echo "$M No VNC_VIEWER in kernel command line." echo "$M Create the domain with 'xm create --vnc '." return 1 fi } # Start the vnc server. Xvnc "$@" >/dev/null 2>&1 & # Connect back to the viewer in domain-0. vnc_connect xen-4.9.2/tools/examples/vnc/Xservers0000664000175000017500000000034213256712137015764 0ustar smbsmb# Configuration lines to go in /etc/X11/xdm/Xservers to # start Xvnc and connect back to a vncviewer in domain-0. # See 'man xdm' under 'LOCAL SERVER SPECIFICATION' for format details. :1 Xvnc local /usr/X11R6/bin/Xvnc-xen :1xen-4.9.2/tools/examples/xlexample.hvm0000664000175000017500000000255513256712137016155 0ustar smbsmb# ===================================================================== # Example HVM guest configuration # ===================================================================== # # This is a fairly minimal example of what is required for an # HVM guest. For a more complete guide see xl.cfg(5) # This configures an HVM rather than PV guest builder = "hvm" # Guest name name = "example.hvm" # 128-bit UUID for the domain as a hexadecimal number. # Use "uuidgen" to generate one if required. # The default behavior is to generate a new UUID each time the guest is started. #uuid = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" # Enable Microsoft Hyper-V compatibile paravirtualisation / # enlightenment interfaces. Turning this on can improve Windows guest # performance and is therefore recommended #viridian = 1 # Initial memory allocation (MB) memory = 128 # Maximum memory (MB) # If this is greater than `memory' then the slack will start ballooned # (this assumes guest kernel support for ballooning) #maxmem = 512 # Number of VCPUS vcpus = 2 # Network devices # A list of 'vifspec' entries as described in # docs/misc/xl-network-configuration.markdown vif = [ '' ] # Disk Devices # A list of `diskspec' entries as described in # docs/misc/xl-disk-configuration.txt disk = [ '/dev/vg/guest-volume,raw,xvda,rw' ] # Guest VGA console configuration, either SDL or VNC sdl = 1 #vnc = 1 xen-4.9.2/tools/examples/Makefile0000664000175000017500000000216613256712137015100 0ustar smbsmbXEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk # Xen configuration dir and configs to go there. XEN_READMES = README XEN_READMES += README.incompatibilities XEN_CONFIGS += xlexample.hvm XEN_CONFIGS += xlexample.pvlinux XEN_CONFIGS += xl.conf XEN_CONFIGS += cpupool XEN_CONFIGS += $(XEN_CONFIGS-y) .PHONY: all all: .PHONY: build build: .PHONY: install install: all install-readmes install-configs .PHONY: install-readmes install-readmes: [ -d $(DESTDIR)$(XEN_CONFIG_DIR) ] || \ $(INSTALL_DIR) $(DESTDIR)$(XEN_CONFIG_DIR) set -e; for i in $(XEN_READMES); \ do [ -e $(DESTDIR)$(XEN_CONFIG_DIR)/$$i ] || \ $(INSTALL_DATA) $$i $(DESTDIR)$(XEN_CONFIG_DIR); \ done .PHONY: install-configs install-configs: $(XEN_CONFIGS) [ -d $(DESTDIR)$(XEN_CONFIG_DIR) ] || \ $(INSTALL_DIR) $(DESTDIR)$(XEN_CONFIG_DIR) [ -d $(DESTDIR)$(XEN_CONFIG_DIR)/auto ] || \ $(INSTALL_DIR) $(DESTDIR)$(XEN_CONFIG_DIR)/auto set -e; for i in $(XEN_CONFIGS); \ do [ -e $(DESTDIR)$(XEN_CONFIG_DIR)/$$i ] || \ $(INSTALL_DATA) $$i $(DESTDIR)$(XEN_CONFIG_DIR); \ done .PHONY: clean clean: .PHONY: distclean distclean: clean xen-4.9.2/tools/examples/README.incompatibilities0000664000175000017500000000166413256712137020031 0ustar smbsmbCommand Incompatibilities ========================= Known incompatibilities with various commands on various distributions, and the workarounds we use. brctl ----- brctl show fails on SLES9 SP2. Workaround is to use brctl show without arguments, and grep, though this would be difficult were you to need to check for a specific bridge-interface pair, since brctl does not show the bridge name on every line. ifup / ifdown ------------- SuSE requires an extra parameter to ifup, which is created by calling getcfg appropriately. See xen-network-common.sh for details. Gentoo doesn't have ifup/ifdown; appropriate alternatives are defined in xen-network-common.sh. ip -- Newer ip commands (from iproute2) do not accept the abbreviated syntax "ip r a ..." etc. "ip route add ..." must be used instead. sed --- \s is not supported in regexps on Debian etch (sed 4.1.2), Ubuntu 4.10. We hand-craft character classes instead. xen-4.9.2/tools/examples/xlexample.pvlinux0000664000175000017500000000234213256712137017062 0ustar smbsmb# ===================================================================== # Example PV Linux guest configuration # ===================================================================== # # This is a fairly minimal example of what is required for a # Paravirtualised Linux guest. For a more complete guide see xl.cfg(5) # Guest name name = "example.pvlinux" # 128-bit UUID for the domain as a hexadecimal number. # Use "uuidgen" to generate one if required. # The default behavior is to generate a new UUID each time the guest is started. #uuid = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" # Kernel image to boot kernel = "/boot/vmlinuz" # Ramdisk (optional) #ramdisk = "/boot/initrd.gz" # Kernel command line options extra = "root=/dev/xvda1" # Initial memory allocation (MB) memory = 128 # Maximum memory (MB) # If this is greater than `memory' then the slack will start ballooned # (this assumes guest kernel support for ballooning) #maxmem = 512 # Number of VCPUS vcpus = 2 # Network devices # A list of 'vifspec' entries as described in # docs/misc/xl-network-configuration.markdown vif = [ '' ] # Disk Devices # A list of `diskspec' entries as described in # docs/misc/xl-disk-configuration.txt disk = [ '/dev/vg/guest-volume,raw,xvda,rw' ] xen-4.9.2/tools/examples/README0000664000175000017500000000447713256712137014327 0ustar smbsmbXen Control Tools - Examples =================================== This directory contains example scripts and configurations for Xen. For many operations you will either be able to use these scripts directly, or incorporate code from them into your own scripts. If you write a useful script and would like to share it, please do send it (preferably with a little summary to go in this file) to so we can add it to this directory. block - called by xen-backend.agent to bind/unbind dev block-common.sh - sourced by block, block-* block-enbd - binds/unbinds network block devices block-nbd - binds/unbinds network block devices cpupool - example configuration script for 'xm cpupool-create' external-device-migrate - called by xend for migrating external devices locking.sh - locking functions to prevent concurrent access to critical sections inside script files logging.sh - logging function to log output using syslog vif-bridge - virtual network start/stop script in bridged mode vif-common.sh - sourced by vif-bridge vif-nat - xen virtual network start/stop script in NAT mode vif-route - xen virtual network start/stop script in routed mode xen-backend.agent - calls block, vif-* scripts to add, remove, hotplug devices xen-hotplug-common.sh - sourced by vif-common.sh xen-network-common.sh - sourced by vif-common.sh xen-script-common.sh - sourced by xen-hotplug-common.sh xmexample1 - example configuration script for 'xm create' xmexample2 - a more complex configuration script for 'xm create' xmexample3 - an advanced configuration script for 'xm create' that utilizes the vmid xmexample.nbd - configuration script that uses NBD filesystems xmexample.hvm - a configuration script for creating a hvm domain with 'xm create' xmexample.hvm-stubdom - a configuration script for creating a hvm domain with 'xm create' that utilizes a stubdomain for device model xmexample.pv-grub - a configuration script for creating a domain with 'xm create' which boots PV-GRUB. xmexample.vti - a configuration script for creating a domain on vti xen-4.9.2/tools/libfsimage/0000775000175000017500000000000013256712137013717 5ustar smbsmbxen-4.9.2/tools/libfsimage/Rules.mk0000664000175000017500000000116513256712137015345 0ustar smbsmbinclude $(XEN_ROOT)/tools/Rules.mk CFLAGS += -Wno-unknown-pragmas -I$(XEN_ROOT)/tools/libfsimage/common/ -DFSIMAGE_FSDIR=\"$(FSDIR)\" CFLAGS += -Werror -D_GNU_SOURCE LDFLAGS += -L../common/ PIC_OBJS := $(patsubst %.c,%.opic,$(LIB_SRCS-y)) FSDIR = $(libdir)/fs FSLIB = fsimage.so .PHONY: fs-all fs-all: $(FSLIB) .PHONY: fs-install fs-install: fs-all $(INSTALL_DIR) $(DESTDIR)$(FSDIR)/$(FS) $(INSTALL_PROG) $(FSLIB) $(DESTDIR)$(FSDIR)/$(FS) $(FSLIB): $(PIC_OBJS) $(CC) $(LDFLAGS) $(SHLIB_LDFLAGS) -o $@ $^ -lfsimage $(FS_LIBDEPS) $(APPEND_LDFLAGS) clean distclean:: rm -f $(PIC_OBJS) $(FSLIB) $(DEPS) -include $(DEPS) xen-4.9.2/tools/libfsimage/ufs/0000775000175000017500000000000013256712137014514 5ustar smbsmbxen-4.9.2/tools/libfsimage/ufs/fsys_ufs.c0000664000175000017500000001535313256712137016530 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 2006 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* From Solaris usr/src/stand/lib/fs/ufs/ufsops.c */ #include #include "ufs.h" /* These are the pools of buffers, etc. */ #define SUPERBLOCK ((struct fs *)(FSYS_BUF + 0x2000)) #define INODE ((struct icommon *)(FSYS_BUF + 0x1000)) #define DIRENT (FSYS_BUF + 0x4000) #define MAXBSIZE ((FSYS_BUFLEN - 0x4000) / 2) #define INDIRBLK1 ((grub_daddr32_t *)(FSYS_BUF + 0x4000)) /* 2+ indir blk */ #define INDIRBLK0 ((grub_daddr32_t *)(FSYS_BUF+ 0x4000 + MAXBSIZE)) /* 1st indirect blk */ #define indirblk0 (*fsig_int1(ffi)) #define indirblk1 (*fsig_int2(ffi)) static int openi(fsi_file_t *, grub_ino_t); static grub_ino_t dlook(fsi_file_t *, grub_ino_t, char *); static grub_daddr32_t sbmap(fsi_file_t *, grub_daddr32_t); /* read superblock and check fs magic */ static int ufs_mount(fsi_file_t *ffi, const char *options) { if (/*! IS_PC_SLICE_TYPE_SOLARIS(current_slice) || */ !devread(ffi, UFS_SBLOCK, 0, UFS_SBSIZE, (char *)SUPERBLOCK) || SUPERBLOCK->fs_magic != UFS_MAGIC || MAXBSIZE < SUPERBLOCK->fs_bsize) return 0; return 1; } /* * searching for a file, if successful, inode will be loaded in INODE * The entry point should really be named ufs_open(char *pathname). * For now, keep it consistent with the rest of fsys modules. */ static int ufs_dir(fsi_file_t *ffi, char *dirname) { grub_ino_t inode = ROOTINO; /* start from root */ char *fname, ch; indirblk0 = indirblk1 = 0; /* skip leading slashes */ while (*dirname == '/') dirname++; while (inode && *dirname && !isspace((uint8_t)*dirname)) { if (!openi(ffi, inode)) return 0; /* parse for next path component */ fname = dirname; while (*dirname && !isspace((uint8_t)*dirname) && *dirname != '/') dirname++; ch = *dirname; *dirname = 0; /* ensure null termination */ inode = dlook(ffi, inode, fname); *dirname = ch; while (*dirname == '/') dirname++; } /* return 1 only if inode exists and is a regular file */ if (! openi(ffi, inode)) return (0); filepos = 0; filemax = INODE->ic_sizelo; return (inode && ((INODE->ic_smode & IFMT) == IFREG)); } /* * This is the high-level read function. */ static int ufs_read(fsi_file_t *ffi, char *buf, int len) { int off, size, ret = 0, ok; grub_daddr32_t lblk, dblk; while (len) { off = blkoff(SUPERBLOCK, filepos); lblk = lblkno(SUPERBLOCK, filepos); size = SUPERBLOCK->fs_bsize; size -= off; if (size > len) size = len; if ((dblk = sbmap(ffi, lblk)) <= 0) { /* we are in a file hole, just zero the buf */ grub_memset(buf, 0, size); } else { disk_read_func = disk_read_hook; ok = devread(ffi, fsbtodb(SUPERBLOCK, dblk), off, size, buf); disk_read_func = 0; if (!ok) return 0; } buf += size; len -= size; filepos += size; ret += size; } return (ret); } int ufs_embed (int *start_sector, int needed_sectors) { if (needed_sectors > 14) return 0; *start_sector = 2; return 1; } /* read inode and place content in INODE */ static int openi(fsi_file_t *ffi, grub_ino_t inode) { grub_daddr32_t dblk; int off; /* get block and byte offset into the block */ dblk = fsbtodb(SUPERBLOCK, itod(SUPERBLOCK, inode)); off = itoo(SUPERBLOCK, inode) * sizeof (struct icommon); return (devread(ffi, dblk, off, sizeof (struct icommon), (char *)INODE)); } /* * Performs fileblock mapping. Convert file block no. to disk block no. * Returns 0 when block doesn't exist and <0 when block isn't initialized * (i.e belongs to a hole in the file). */ grub_daddr32_t sbmap(fsi_file_t *ffi, grub_daddr32_t bn) { int level, bound, i, index; grub_daddr32_t nb, blkno; grub_daddr32_t *db = INODE->ic_db; /* blocks 0..UFS_NDADDR are direct blocks */ if (bn < UFS_NDADDR) { return db[bn]; } /* determine how many levels of indirection. */ level = 0; bn -= UFS_NDADDR; bound = UFS_NINDIR(SUPERBLOCK); while (bn >= bound) { level++; bn -= bound; bound *= UFS_NINDIR(SUPERBLOCK); } if (level >= UFS_NIADDR) /* bn too big */ return ((grub_daddr32_t)0); /* fetch the first indirect block */ nb = INODE->ic_ib[level]; if (nb == 0) { return ((grub_daddr32_t)0); } if (indirblk0 != nb) { indirblk0 = 0; blkno = fsbtodb(SUPERBLOCK, nb); if (!devread(ffi, blkno, 0, SUPERBLOCK->fs_bsize, (char *)INDIRBLK0)) return (0); indirblk0 = nb; } bound /= UFS_NINDIR(SUPERBLOCK); index = (bn / bound) % UFS_NINDIR(SUPERBLOCK); nb = INDIRBLK0[index]; /* fetch through the indirect blocks */ for (i = 1; i <= level; i++) { if (indirblk1 != nb) { blkno = fsbtodb(SUPERBLOCK, nb); if (!devread(ffi, blkno, 0, SUPERBLOCK->fs_bsize, (char *)INDIRBLK1)) return (0); indirblk1 = nb; } bound /= UFS_NINDIR(SUPERBLOCK); index = (bn / bound) % UFS_NINDIR(SUPERBLOCK); nb = INDIRBLK1[index]; if (nb == 0) return ((grub_daddr32_t)0); } return (nb); } /* search directory content for name, return inode number */ static grub_ino_t dlook(fsi_file_t *ffi, grub_ino_t dir_ino, char *name) { int loc, off; grub_daddr32_t lbn, dbn, dblk; struct direct *dp; if ((INODE->ic_smode & IFMT) != IFDIR) return 0; loc = 0; while (loc < INODE->ic_sizelo) { /* offset into block */ off = blkoff(SUPERBLOCK, loc); if (off == 0) { /* need to read in a new block */ /* get logical block number */ lbn = lblkno(SUPERBLOCK, loc); /* resolve indrect blocks */ dbn = sbmap(ffi, lbn); if (dbn == 0) return (0); dblk = fsbtodb(SUPERBLOCK, dbn); if (!devread(ffi, dblk, 0, SUPERBLOCK->fs_bsize, (char *)DIRENT)) { return 0; } } dp = (struct direct *)(DIRENT + off); if (dp->d_ino && substring(name, dp->d_name) == 0) return (dp->d_ino); loc += dp->d_reclen; } return (0); } fsi_plugin_ops_t * fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) { static fsig_plugin_ops_t ops = { FSIMAGE_PLUGIN_VERSION, .fpo_mount = ufs_mount, .fpo_dir = ufs_dir, .fpo_read = ufs_read }; *name = "ufs"; return (fsig_init(fp, &ops)); } xen-4.9.2/tools/libfsimage/ufs/ufs.h0000664000175000017500000002274613256712137015475 0ustar smbsmb/* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _GRUB_UFS_H #define _GRUB_UFS_H /* ufs specific constants */ #define UFS_SBLOCK 16 #define UFS_SBSIZE 8192 #define UFS_MAGIC 0x011954 #define ROOTINO 2 /* i number of all roots */ #define UFS_NDADDR 12 /* direct blocks */ #define UFS_NIADDR 3 /* indirect blocks */ #define MAXMNTLEN 512 #define MAXCSBUFS 32 #define MAXNAMELEN 256 /* file types */ #define IFMT 0xf000 #define IFREG 0x8000 #define IFDIR 0x4000 typedef unsigned char grub_uchar_t; typedef unsigned short grub_ushort_t; typedef unsigned short grub_o_mode_t; typedef unsigned short grub_o_uid_t; typedef unsigned short grub_o_gid_t; typedef uint32_t grub_ino_t; typedef int32_t grub_int32_t; typedef int32_t grub_uid_t; typedef int32_t grub_gid_t; typedef uint32_t grub_uint32_t; typedef uint32_t grub_daddr32_t; typedef uint32_t grub_time32_t; typedef struct { int val[2]; } grub_quad_t; struct timeval32 { grub_time32_t tv_sec; grub_int32_t tv_usec; }; /* * Per cylinder group information; summarized in blocks allocated * from first cylinder group data blocks. These blocks have to be * read in from fs_csaddr (size fs_cssize) in addition to the * super block. * * N.B. sizeof (struct csum) must be a power of two in order for * the ``fs_cs'' macro to work (see below). */ struct csum { grub_int32_t cs_ndir; /* number of directories */ grub_int32_t cs_nbfree; /* number of free blocks */ grub_int32_t cs_nifree; /* number of free inodes */ grub_int32_t cs_nffree; /* number of free frags */ }; /* Ufs super block */ struct fs { grub_uint32_t fs_link; /* linked list of file systems */ grub_uint32_t fs_rolled; /* logging only: fs fully rolled */ grub_daddr32_t fs_sblkno; /* addr of super-block in filesys */ grub_daddr32_t fs_cblkno; /* offset of cyl-block in filesys */ grub_daddr32_t fs_iblkno; /* offset of inode-blocks in filesys */ grub_daddr32_t fs_dblkno; /* offset of first data after cg */ grub_int32_t fs_cgoffset; /* cylinder group offset in cylinder */ grub_int32_t fs_cgmask; /* used to calc mod fs_ntrak */ grub_time32_t fs_time; /* last time written */ grub_int32_t fs_size; /* number of blocks in fs */ grub_int32_t fs_dsize; /* number of data blocks in fs */ grub_int32_t fs_ncg; /* number of cylinder groups */ grub_int32_t fs_bsize; /* size of basic blocks in fs */ grub_int32_t fs_fsize; /* size of frag blocks in fs */ grub_int32_t fs_frag; /* number of frags in a block in fs */ /* these are configuration parameters */ grub_int32_t fs_minfree; /* minimum percentage of free blocks */ grub_int32_t fs_rotdelay; /* num of ms for optimal next block */ grub_int32_t fs_rps; /* disk revolutions per second */ /* these fields can be computed from the others */ grub_int32_t fs_bmask; /* ``blkoff'' calc of blk offsets */ grub_int32_t fs_fmask; /* ``fragoff'' calc of frag offsets */ grub_int32_t fs_bshift; /* ``lblkno'' calc of logical blkno */ grub_int32_t fs_fshift; /* ``numfrags'' calc number of frags */ /* these are configuration parameters */ grub_int32_t fs_maxcontig; /* max number of contiguous blks */ grub_int32_t fs_maxbpg; /* max number of blks per cyl group */ /* these fields can be computed from the others */ grub_int32_t fs_fragshift; /* block to frag shift */ grub_int32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ grub_int32_t fs_sbsize; /* actual size of super block */ grub_int32_t fs_csmask; /* csum block offset */ grub_int32_t fs_csshift; /* csum block number */ grub_int32_t fs_nindir; /* value of NINDIR */ grub_int32_t fs_inopb; /* value of INOPB */ grub_int32_t fs_nspf; /* value of NSPF */ /* yet another configuration parameter */ grub_int32_t fs_optim; /* optimization preference, see below */ /* these fields are derived from the hardware */ /* USL SVR4 compatibility */ /* * * USL SVR4 compatibility * * There was a significant divergence here between Solaris and * SVR4 for x86. By swapping these two members in the superblock, * we get read-only compatibility of SVR4 filesystems. Otherwise * there would be no compatibility. This change was introduced * during bootstrapping of Solaris on x86. By making this ifdef'ed * on byte order, we provide ongoing compatibility across all * platforms with the same byte order, the highest compatibility * that can be achieved. */ grub_int32_t fs_state; /* file system state time stamp */ grub_int32_t fs_si; /* summary info state - lufs only */ grub_int32_t fs_trackskew; /* sector 0 skew, per track */ /* unique id for this filesystem (currently unused and unmaintained) */ /* In 4.3 Tahoe this space is used by fs_headswitch and fs_trkseek */ /* Neither of those fields is used in the Tahoe code right now but */ /* there could be problems if they are. */ grub_int32_t fs_id[2]; /* file system id */ /* sizes determined by number of cylinder groups and their sizes */ grub_daddr32_t fs_csaddr; /* blk addr of cyl grp summary area */ grub_int32_t fs_cssize; /* size of cyl grp summary area */ grub_int32_t fs_cgsize; /* cylinder group size */ /* these fields are derived from the hardware */ grub_int32_t fs_ntrak; /* tracks per cylinder */ grub_int32_t fs_nsect; /* sectors per track */ grub_int32_t fs_spc; /* sectors per cylinder */ /* this comes from the disk driver partitioning */ grub_int32_t fs_ncyl; /* cylinders in file system */ /* these fields can be computed from the others */ grub_int32_t fs_cpg; /* cylinders per group */ grub_int32_t fs_ipg; /* inodes per group */ grub_int32_t fs_fpg; /* blocks per group * fs_frag */ /* this data must be re-computed after crashes */ struct csum fs_cstotal; /* cylinder summary information */ /* these fields are cleared at mount time */ char fs_fmod; /* super block modified flag */ char fs_clean; /* file system state flag */ char fs_ronly; /* mounted read-only flag */ char fs_flags; /* largefiles flag, etc. */ char fs_fsmnt[MAXMNTLEN]; /* name mounted on */ /* these fields retain the current block allocation info */ grub_int32_t fs_cgrotor; /* last cg searched */ /* * The following used to be fs_csp[MAXCSBUFS]. It was not * used anywhere except in old utilities. We removed this * in 5.6 and expect fs_u.fs_csp to be used instead. * We no longer limit fs_cssize based on MAXCSBUFS. */ union { /* fs_cs (csum) info */ grub_uint32_t fs_csp_pad[MAXCSBUFS]; struct csum *fs_csp; } fs_u; grub_int32_t fs_cpc; /* cyl per cycle in postbl */ short fs_opostbl[16][8]; /* old rotation block list head */ grub_int32_t fs_sparecon[51]; /* reserved for future constants */ grub_int32_t fs_version; /* minor version of MTB ufs */ grub_int32_t fs_logbno; /* block # of embedded log */ grub_int32_t fs_reclaim; /* reclaim open, deleted files */ grub_int32_t fs_sparecon2; /* reserved for future constant */ /* USL SVR4 compatibility */ grub_int32_t fs_npsect; /* # sectors/track including spares */ grub_quad_t fs_qbmask; /* ~fs_bmask - for use with quad size */ grub_quad_t fs_qfmask; /* ~fs_fmask - for use with quad size */ grub_int32_t fs_postblformat; /* fmt of positional layout tables */ grub_int32_t fs_nrpos; /* number of rotaional positions */ grub_int32_t fs_postbloff; /* (short) rotation block list head */ grub_int32_t fs_rotbloff; /* (grub_uchar_t) blocks for each */ /* rotation */ grub_int32_t fs_magic; /* magic number */ grub_uchar_t fs_space[1]; /* list of blocks for each rotation */ /* actually longer */ }; struct icommon { grub_o_mode_t ic_smode; /* 0: mode and type of file */ short ic_nlink; /* 2: number of links to file */ grub_o_uid_t ic_suid; /* 4: owner's user id */ grub_o_gid_t ic_sgid; /* 6: owner's group id */ grub_uint32_t ic_sizelo; /* 8: number of bytes in file */ grub_uint32_t ic_sizehi; /* 12: number of bytes in file */ struct timeval32 ic_atime; /* 16: time last accessed */ struct timeval32 ic_mtime; /* 24: time last modified */ struct timeval32 ic_ctime; /* 32: last time inode changed */ grub_daddr32_t ic_db[UFS_NDADDR]; /* 40: disk block addresses */ grub_daddr32_t ic_ib[UFS_NIADDR]; /* 88: indirect blocks */ grub_int32_t ic_flags; /* 100: cflags */ grub_int32_t ic_blocks; /* 104: 512 byte blocks actually held */ grub_int32_t ic_gen; /* 108: generation number */ grub_int32_t ic_shadow; /* 112: shadow inode */ grub_uid_t ic_uid; /* 116: long EFT version of uid */ grub_gid_t ic_gid; /* 120: long EFT version of gid */ grub_uint32_t ic_oeftflag; /* 124: extended attr directory ino, */ /* 0 = none */ }; struct direct { grub_ino_t d_ino; grub_ushort_t d_reclen; grub_ushort_t d_namelen; char d_name[MAXNAMELEN + 1]; }; /* inode macros */ #define INOPB(fs) ((fs)->fs_inopb) #define itoo(fs, x) ((x) % (grub_uint32_t)INOPB(fs)) #define itog(fs, x) ((x) / (grub_uint32_t)(fs)->fs_ipg) #define itod(fs, x) ((grub_daddr32_t)(cgimin(fs, itog(fs, x)) + \ (blkstofrags((fs), \ ((x) % (grub_uint32_t)(fs)->fs_ipg / (grub_uint32_t)INOPB(fs)))))) /* block conversion macros */ #define UFS_NINDIR(fs) ((fs)->fs_nindir) /* # of indirects */ #define blkoff(fs, loc) ((int)((loc & ~(fs)->fs_bmask))) #define lblkno(fs, loc) ((grub_int32_t)((loc) >> (fs)->fs_bshift)) /* frag to blk */ #define fsbtodb(fs, b) (((grub_daddr32_t)(b)) << (fs)->fs_fsbtodb) #define blkstofrags(fs, b) ((b) << (fs)->fs_fragshift) /* cynlinder group macros */ #define cgbase(fs, c) ((grub_daddr32_t)((fs)->fs_fpg * (c))) #define cgimin(fs, c) (cgstart(fs, c) + (fs)->fs_iblkno) /* inode block */ #define cgstart(fs, c) \ (cgbase(fs, c) + (fs)->fs_cgoffset * ((c) & ~((fs)->fs_cgmask))) #endif /* !_GRUB_UFS_H */ xen-4.9.2/tools/libfsimage/ufs/Makefile0000664000175000017500000000025613256712137016157 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. LIB_SRCS-y = fsys_ufs.c FS = ufs .PHONY: all all: fs-all .PHONY: install install: fs-install include $(XEN_ROOT)/tools/libfsimage/Rules.mk xen-4.9.2/tools/libfsimage/zfs/0000775000175000017500000000000013256712137014521 5ustar smbsmbxen-4.9.2/tools/libfsimage/zfs/fsi_zfs.c0000664000175000017500000000705413256712137016336 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifdef FSYS_ZFS #include #include #include #include #include "mb_info.h" #undef filemax #undef filepos #undef errnum #define MAXNAMELEN 256 #define MAXPATHLEN 1024 /**** START FROM disk_io.c ****/ char current_rootpool[MAXNAMELEN]; char current_bootfs[MAXNAMELEN]; uint64_t current_bootfs_obj; char current_bootpath[MAXPATHLEN]; char current_devid[MAXPATHLEN]; int is_zfs_mount; unsigned long best_drive; unsigned long best_part; int find_best_root; unsigned long part_length; /**** END FROM disk_io.c ****/ uint64_t filemax; uint64_t filepos; struct multiboot_info mbi; fsi_file_t *zfs_ffi; int errnum; char *bootstring = NULL; extern int zfs_mount(void); extern int zfs_open(char *filename); extern int zfs_read(char *buf, int len); #define ZFS_SCRATCH_SIZE 0x400000 #define FSI_MOS_SHIFT 10 #define FSI_MOS_MASK ((1 << FSI_MOS_SHIFT) - 1) unsigned char fsi_mos_buf[ZFS_SCRATCH_SIZE + FSI_MOS_MASK + 1]; #define FSI_MOS_ALIGN(addr) (((uintptr_t)addr + FSI_MOS_MASK) & \ ~FSI_MOS_MASK) #define FSI_MOS(buf) ((FSI_MOS_ALIGN(buf) + \ ZFS_SCRATCH_SIZE - 0x100000) >> FSI_MOS_SHIFT) static int fsi_zfs_mount(fsi_file_t *ffi, const char *options) { zfs_ffi = ffi; mbi.mem_upper = FSI_MOS(fsi_mos_buf); /* If an boot filesystem is passed in, set it to current_bootfs */ if (options != NULL) { if (strlen(options) < MAXNAMELEN) { strcpy(current_bootfs, options); } } return (zfs_mount()); } static int fsi_zfs_open(fsi_file_t *ffi, char *filename) { char *fsi_bootstring; uint64_t *fmax; uint64_t *fpos; int rc; zfs_ffi = ffi; fmax = fsig_filemax(ffi); fpos = fsig_filepos(ffi); rc = zfs_open(filename); if (rc != 1) { return (rc); } *fmax = filemax; *fpos = filepos; if (bootstring == NULL) { rc = asprintf(&bootstring, "zfs-bootfs=%s/%"PRIu64",bootpath='%s'", current_rootpool, current_bootfs_obj, current_bootpath); if (rc == -1) { return (rc); } fsi_bootstring = fsi_bootstring_alloc(ffi->ff_fsi, strlen(bootstring) + 1); strcpy(fsi_bootstring, bootstring); } return (rc); } static int fsi_zfs_read(fsi_file_t *ffi, char *buf, int len) { uint64_t *fpos; int rc; zfs_ffi = ffi; fpos = fsig_filepos(ffi); filepos = *fpos; rc = zfs_read(buf, len); *fpos = filepos; return (rc); } fsi_plugin_ops_t * fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) { static fsig_plugin_ops_t ops = { FSIMAGE_PLUGIN_VERSION, .fpo_mount = fsi_zfs_mount, .fpo_dir = fsi_zfs_open, .fpo_read = fsi_zfs_read, }; *name = "zfs"; return (fsig_init(fp, &ops)); } #endif /* FSYS_ZFS */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/0000775000175000017500000000000013256712137016744 5ustar smbsmbxen-4.9.2/tools/libfsimage/zfs/zfs-include/zap_impl.h0000664000175000017500000000700113256712137020726 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_ZAP_IMPL_H #define _SYS_ZAP_IMPL_H #define ZAP_MAGIC 0x2F52AB2ABULL #define MZAP_ENT_LEN 64 #define MZAP_NAME_LEN (MZAP_ENT_LEN - 8 - 4 - 2) #define MZAP_MAX_BLKSHIFT SPA_MAXBLOCKSHIFT #define MZAP_MAX_BLKSZ (1 << MZAP_MAX_BLKSHIFT) typedef struct mzap_ent_phys { uint64_t mze_value; uint32_t mze_cd; uint16_t mze_pad; /* in case we want to chain them someday */ char mze_name[MZAP_NAME_LEN]; } mzap_ent_phys_t; typedef struct mzap_phys { uint64_t mz_block_type; /* ZBT_MICRO */ uint64_t mz_salt; uint64_t mz_pad[6]; mzap_ent_phys_t mz_chunk[1]; /* actually variable size depending on block size */ } mzap_phys_t; /* * The (fat) zap is stored in one object. It is an array of * 1<= 6] [zap_leaf_t] [ptrtbl] ... * */ #define ZBT_LEAF ((1ULL << 63) + 0) #define ZBT_HEADER ((1ULL << 63) + 1) #define ZBT_MICRO ((1ULL << 63) + 3) /* any other values are ptrtbl blocks */ /* * the embedded pointer table takes up half a block: * block size / entry size (2^3) / 2 */ #define ZAP_EMBEDDED_PTRTBL_SHIFT(zap) (FZAP_BLOCK_SHIFT(zap) - 3 - 1) /* * The embedded pointer table starts half-way through the block. Since * the pointer table itself is half the block, it starts at (64-bit) * word number (1<zap_f.zap_phys) \ [(idx) + (1<. */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_VDEV_IMPL_H #define _SYS_VDEV_IMPL_H #define VDEV_PAD_SIZE (8 << 10) /* 2 padding areas (vl_pad1 and vl_pad2) to skip */ #define VDEV_SKIP_SIZE VDEV_PAD_SIZE * 2 #define VDEV_PHYS_SIZE (112 << 10) #define VDEV_UBERBLOCK_RING (128 << 10) typedef struct vdev_phys { char vp_nvlist[VDEV_PHYS_SIZE - sizeof (zio_eck_t)]; zio_eck_t vp_zbt; } vdev_phys_t; typedef struct vdev_label { char vl_pad1[VDEV_PAD_SIZE]; /* 8K */ char vl_pad2[VDEV_PAD_SIZE]; /* 8K */ vdev_phys_t vl_vdev_phys; /* 112K */ char vl_uberblock[VDEV_UBERBLOCK_RING]; /* 128K */ } vdev_label_t; /* 256K total */ /* * Size and offset of embedded boot loader region on each label. * The total size of the first two labels plus the boot area is 4MB. */ #define VDEV_BOOT_OFFSET (2 * sizeof (vdev_label_t)) #define VDEV_BOOT_SIZE (7ULL << 19) /* 3.5M */ /* * Size of label regions at the start and end of each leaf device. */ #define VDEV_LABEL_START_SIZE (2 * sizeof (vdev_label_t) + VDEV_BOOT_SIZE) #define VDEV_LABEL_END_SIZE (2 * sizeof (vdev_label_t)) #define VDEV_LABELS 4 #endif /* _SYS_VDEV_IMPL_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/zfs.h0000664000175000017500000000757513256712137017735 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_FS_ZFS_H #define _SYS_FS_ZFS_H /* * On-disk version number. */ #define SPA_VERSION 24ULL /* * The following are configuration names used in the nvlist describing a pool's * configuration. */ #define ZPOOL_CONFIG_VERSION "version" #define ZPOOL_CONFIG_POOL_NAME "name" #define ZPOOL_CONFIG_POOL_STATE "state" #define ZPOOL_CONFIG_POOL_TXG "txg" #define ZPOOL_CONFIG_POOL_GUID "pool_guid" #define ZPOOL_CONFIG_CREATE_TXG "create_txg" #define ZPOOL_CONFIG_TOP_GUID "top_guid" #define ZPOOL_CONFIG_VDEV_TREE "vdev_tree" #define ZPOOL_CONFIG_TYPE "type" #define ZPOOL_CONFIG_CHILDREN "children" #define ZPOOL_CONFIG_ID "id" #define ZPOOL_CONFIG_GUID "guid" #define ZPOOL_CONFIG_PATH "path" #define ZPOOL_CONFIG_DEVID "devid" #define ZPOOL_CONFIG_METASLAB_ARRAY "metaslab_array" #define ZPOOL_CONFIG_METASLAB_SHIFT "metaslab_shift" #define ZPOOL_CONFIG_ASHIFT "ashift" #define ZPOOL_CONFIG_ASIZE "asize" #define ZPOOL_CONFIG_DTL "DTL" #define ZPOOL_CONFIG_STATS "stats" #define ZPOOL_CONFIG_WHOLE_DISK "whole_disk" #define ZPOOL_CONFIG_ERRCOUNT "error_count" #define ZPOOL_CONFIG_NOT_PRESENT "not_present" #define ZPOOL_CONFIG_SPARES "spares" #define ZPOOL_CONFIG_IS_SPARE "is_spare" #define ZPOOL_CONFIG_NPARITY "nparity" #define ZPOOL_CONFIG_PHYS_PATH "phys_path" #define ZPOOL_CONFIG_L2CACHE "l2cache" #define ZPOOL_CONFIG_HOLE_ARRAY "hole_array" #define ZPOOL_CONFIG_VDEV_CHILDREN "vdev_children" #define ZPOOL_CONFIG_IS_HOLE "is_hole" #define ZPOOL_CONFIG_DDT_HISTOGRAM "ddt_histogram" #define ZPOOL_CONFIG_DDT_OBJ_STATS "ddt_object_stats" #define ZPOOL_CONFIG_DDT_STATS "ddt_stats" /* * The persistent vdev state is stored as separate values rather than a single * 'vdev_state' entry. This is because a device can be in multiple states, such * as offline and degraded. */ #define ZPOOL_CONFIG_OFFLINE "offline" #define ZPOOL_CONFIG_FAULTED "faulted" #define ZPOOL_CONFIG_DEGRADED "degraded" #define ZPOOL_CONFIG_REMOVED "removed" #define VDEV_TYPE_ROOT "root" #define VDEV_TYPE_MIRROR "mirror" #define VDEV_TYPE_REPLACING "replacing" #define VDEV_TYPE_RAIDZ "raidz" #define VDEV_TYPE_DISK "disk" #define VDEV_TYPE_FILE "file" #define VDEV_TYPE_MISSING "missing" #define VDEV_TYPE_HOLE "hole" #define VDEV_TYPE_SPARE "spare" #define VDEV_TYPE_L2CACHE "l2cache" /* * pool state. The following states are written to disk as part of the normal * SPA lifecycle: ACTIVE, EXPORTED, DESTROYED, SPARE, L2CACHE. The remaining * states are software abstractions used at various levels to communicate pool * state. */ typedef enum pool_state { POOL_STATE_ACTIVE = 0, /* In active use */ POOL_STATE_EXPORTED, /* Explicitly exported */ POOL_STATE_DESTROYED, /* Explicitly destroyed */ POOL_STATE_SPARE, /* Reserved for hot spare use */ POOL_STATE_L2CACHE, /* Level 2 ARC device */ POOL_STATE_UNINITIALIZED, /* Internal spa_t state */ POOL_STATE_UNAVAIL, /* Internal libzfs state */ POOL_STATE_POTENTIALLY_ACTIVE /* Internal libzfs state */ } pool_state_t; #endif /* _SYS_FS_ZFS_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/zil.h0000664000175000017500000000400413256712137017711 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_ZIL_H #define _SYS_ZIL_H /* * Intent log format: * * Each objset has its own intent log. The log header (zil_header_t) * for objset N's intent log is kept in the Nth object of the SPA's * intent_log objset. The log header points to a chain of log blocks, * each of which contains log records (i.e., transactions) followed by * a log block trailer (zil_trailer_t). The format of a log record * depends on the record (or transaction) type, but all records begin * with a common structure that defines the type, length, and txg. */ /* * Intent log header - this on disk structure holds fields to manage * the log. All fields are 64 bit to easily handle cross architectures. */ typedef struct zil_header { uint64_t zh_claim_txg; /* txg in which log blocks were claimed */ uint64_t zh_replay_seq; /* highest replayed sequence number */ blkptr_t zh_log; /* log chain */ uint64_t zh_claim_seq; /* highest claimed sequence number */ uint64_t zh_flags; /* header flags */ uint64_t zh_pad[4]; } zil_header_t; /* * zh_flags bit settings */ #define ZIL_REPLAY_NEEDED 0x1 /* replay needed - internal only */ #endif /* _SYS_ZIL_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/spa.h0000664000175000017500000002612113256712137017702 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_SPA_H #define _SYS_SPA_H /* * General-purpose 32-bit and 64-bit bitfield encodings. */ #define BF32_DECODE(x, low, len) P2PHASE((x) >> (low), 1U << (len)) #define BF64_DECODE(x, low, len) P2PHASE((x) >> (low), 1ULL << (len)) #define BF32_ENCODE(x, low, len) (P2PHASE((x), 1U << (len)) << (low)) #define BF64_ENCODE(x, low, len) (P2PHASE((x), 1ULL << (len)) << (low)) #define BF32_GET(x, low, len) BF32_DECODE(x, low, len) #define BF64_GET(x, low, len) BF64_DECODE(x, low, len) #define BF32_SET(x, low, len, val) \ ((x) ^= BF32_ENCODE((x >> low) ^ (val), low, len)) #define BF64_SET(x, low, len, val) \ ((x) ^= BF64_ENCODE((x >> low) ^ (val), low, len)) #define BF32_GET_SB(x, low, len, shift, bias) \ ((BF32_GET(x, low, len) + (bias)) << (shift)) #define BF64_GET_SB(x, low, len, shift, bias) \ ((BF64_GET(x, low, len) + (bias)) << (shift)) #define BF32_SET_SB(x, low, len, shift, bias, val) \ BF32_SET(x, low, len, ((val) >> (shift)) - (bias)) #define BF64_SET_SB(x, low, len, shift, bias, val) \ BF64_SET(x, low, len, ((val) >> (shift)) - (bias)) /* * We currently support nine block sizes, from 512 bytes to 128K. * We could go higher, but the benefits are near-zero and the cost * of COWing a giant block to modify one byte would become excessive. */ #define SPA_MINBLOCKSHIFT 9 #define SPA_MAXBLOCKSHIFT 17 #define SPA_MINBLOCKSIZE (1ULL << SPA_MINBLOCKSHIFT) #define SPA_MAXBLOCKSIZE (1ULL << SPA_MAXBLOCKSHIFT) #define SPA_BLOCKSIZES (SPA_MAXBLOCKSHIFT - SPA_MINBLOCKSHIFT + 1) /* * Size of block to hold the configuration data (a packed nvlist) */ #define SPA_CONFIG_BLOCKSIZE (1 << 14) /* * The DVA size encodings for LSIZE and PSIZE support blocks up to 32MB. * The ASIZE encoding should be at least 64 times larger (6 more bits) * to support up to 4-way RAID-Z mirror mode with worst-case gang block * overhead, three DVAs per bp, plus one more bit in case we do anything * else that expands the ASIZE. */ #define SPA_LSIZEBITS 16 /* LSIZE up to 32M (2^16 * 512) */ #define SPA_PSIZEBITS 16 /* PSIZE up to 32M (2^16 * 512) */ #define SPA_ASIZEBITS 24 /* ASIZE up to 64 times larger */ /* * All SPA data is represented by 128-bit data virtual addresses (DVAs). * The members of the dva_t should be considered opaque outside the SPA. */ typedef struct dva { uint64_t dva_word[2]; } dva_t; /* * Each block has a 256-bit checksum -- strong enough for cryptographic hashes. */ typedef struct zio_cksum { uint64_t zc_word[4]; } zio_cksum_t; /* * Each block is described by its DVAs, time of birth, checksum, etc. * The word-by-word, bit-by-bit layout of the blkptr is as follows: * * 64 56 48 40 32 24 16 8 0 * +-------+-------+-------+-------+-------+-------+-------+-------+ * 0 | vdev1 | GRID | ASIZE | * +-------+-------+-------+-------+-------+-------+-------+-------+ * 1 |G| offset1 | * +-------+-------+-------+-------+-------+-------+-------+-------+ * 2 | vdev2 | GRID | ASIZE | * +-------+-------+-------+-------+-------+-------+-------+-------+ * 3 |G| offset2 | * +-------+-------+-------+-------+-------+-------+-------+-------+ * 4 | vdev3 | GRID | ASIZE | * +-------+-------+-------+-------+-------+-------+-------+-------+ * 5 |G| offset3 | * +-------+-------+-------+-------+-------+-------+-------+-------+ * 6 |BDX|lvl| type | cksum | comp | PSIZE | LSIZE | * +-------+-------+-------+-------+-------+-------+-------+-------+ * 7 | padding | * +-------+-------+-------+-------+-------+-------+-------+-------+ * 8 | padding | * +-------+-------+-------+-------+-------+-------+-------+-------+ * 9 | physical birth txg | * +-------+-------+-------+-------+-------+-------+-------+-------+ * a | logical birth txg | * +-------+-------+-------+-------+-------+-------+-------+-------+ * b | fill count | * +-------+-------+-------+-------+-------+-------+-------+-------+ * c | checksum[0] | * +-------+-------+-------+-------+-------+-------+-------+-------+ * d | checksum[1] | * +-------+-------+-------+-------+-------+-------+-------+-------+ * e | checksum[2] | * +-------+-------+-------+-------+-------+-------+-------+-------+ * f | checksum[3] | * +-------+-------+-------+-------+-------+-------+-------+-------+ * * Legend: * * vdev virtual device ID * offset offset into virtual device * LSIZE logical size * PSIZE physical size (after compression) * ASIZE allocated size (including RAID-Z parity and gang block headers) * GRID RAID-Z layout information (reserved for future use) * cksum checksum function * comp compression function * G gang block indicator * B byteorder (endianness) * D dedup * X unused * lvl level of indirection * type DMU object type * phys birth txg of block allocation; zero if same as logical birth txg * log. birth transaction group in which the block was logically born * fill count number of non-zero blocks under this bp * checksum[4] 256-bit checksum of the data this bp describes */ #define SPA_BLKPTRSHIFT 7 /* blkptr_t is 128 bytes */ #define SPA_DVAS_PER_BP 3 /* Number of DVAs in a bp */ typedef struct blkptr { dva_t blk_dva[SPA_DVAS_PER_BP]; /* Data Virtual Addresses */ uint64_t blk_prop; /* size, compression, type, etc */ uint64_t blk_pad[2]; /* Extra space for the future */ uint64_t blk_phys_birth; /* txg when block was allocated */ uint64_t blk_birth; /* transaction group at birth */ uint64_t blk_fill; /* fill count */ zio_cksum_t blk_cksum; /* 256-bit checksum */ } blkptr_t; /* * Macros to get and set fields in a bp or DVA. */ #define DVA_GET_ASIZE(dva) \ BF64_GET_SB((dva)->dva_word[0], 0, 24, SPA_MINBLOCKSHIFT, 0) #define DVA_SET_ASIZE(dva, x) \ BF64_SET_SB((dva)->dva_word[0], 0, 24, SPA_MINBLOCKSHIFT, 0, x) #define DVA_GET_GRID(dva) BF64_GET((dva)->dva_word[0], 24, 8) #define DVA_SET_GRID(dva, x) BF64_SET((dva)->dva_word[0], 24, 8, x) #define DVA_GET_VDEV(dva) BF64_GET((dva)->dva_word[0], 32, 32) #define DVA_SET_VDEV(dva, x) BF64_SET((dva)->dva_word[0], 32, 32, x) #define DVA_GET_OFFSET(dva) \ BF64_GET_SB((dva)->dva_word[1], 0, 63, SPA_MINBLOCKSHIFT, 0) #define DVA_SET_OFFSET(dva, x) \ BF64_SET_SB((dva)->dva_word[1], 0, 63, SPA_MINBLOCKSHIFT, 0, x) #define DVA_GET_GANG(dva) BF64_GET((dva)->dva_word[1], 63, 1) #define DVA_SET_GANG(dva, x) BF64_SET((dva)->dva_word[1], 63, 1, x) #define BP_GET_LSIZE(bp) \ BF64_GET_SB((bp)->blk_prop, 0, 16, SPA_MINBLOCKSHIFT, 1) #define BP_SET_LSIZE(bp, x) \ BF64_SET_SB((bp)->blk_prop, 0, 16, SPA_MINBLOCKSHIFT, 1, x) #define BP_GET_PSIZE(bp) \ BF64_GET_SB((bp)->blk_prop, 16, 16, SPA_MINBLOCKSHIFT, 1) #define BP_SET_PSIZE(bp, x) \ BF64_SET_SB((bp)->blk_prop, 16, 16, SPA_MINBLOCKSHIFT, 1, x) #define BP_GET_COMPRESS(bp) BF64_GET((bp)->blk_prop, 32, 8) #define BP_SET_COMPRESS(bp, x) BF64_SET((bp)->blk_prop, 32, 8, x) #define BP_GET_CHECKSUM(bp) BF64_GET((bp)->blk_prop, 40, 8) #define BP_SET_CHECKSUM(bp, x) BF64_SET((bp)->blk_prop, 40, 8, x) #define BP_GET_TYPE(bp) BF64_GET((bp)->blk_prop, 48, 8) #define BP_SET_TYPE(bp, x) BF64_SET((bp)->blk_prop, 48, 8, x) #define BP_GET_LEVEL(bp) BF64_GET((bp)->blk_prop, 56, 5) #define BP_SET_LEVEL(bp, x) BF64_SET((bp)->blk_prop, 56, 5, x) #define BP_GET_PROP_BIT_61(bp) BF64_GET((bp)->blk_prop, 61, 1) #define BP_SET_PROP_BIT_61(bp, x) BF64_SET((bp)->blk_prop, 61, 1, x) #define BP_GET_DEDUP(bp) BF64_GET((bp)->blk_prop, 62, 1) #define BP_SET_DEDUP(bp, x) BF64_SET((bp)->blk_prop, 62, 1, x) #define BP_GET_BYTEORDER(bp) (0 - BF64_GET((bp)->blk_prop, 63, 1)) #define BP_SET_BYTEORDER(bp, x) BF64_SET((bp)->blk_prop, 63, 1, x) #define BP_PHYSICAL_BIRTH(bp) \ ((bp)->blk_phys_birth ? (bp)->blk_phys_birth : (bp)->blk_birth) #define BP_SET_BIRTH(bp, logical, physical) \ { \ (bp)->blk_birth = (logical); \ (bp)->blk_phys_birth = ((logical) == (physical) ? 0 : (physical)); \ } #define BP_GET_ASIZE(bp) \ (DVA_GET_ASIZE(&(bp)->blk_dva[0]) + DVA_GET_ASIZE(&(bp)->blk_dva[1]) + \ DVA_GET_ASIZE(&(bp)->blk_dva[2])) #define BP_GET_UCSIZE(bp) \ ((BP_GET_LEVEL(bp) > 0 || dmu_ot[BP_GET_TYPE(bp)].ot_metadata) ? \ BP_GET_PSIZE(bp) : BP_GET_LSIZE(bp)); #define BP_GET_NDVAS(bp) \ (!!DVA_GET_ASIZE(&(bp)->blk_dva[0]) + \ !!DVA_GET_ASIZE(&(bp)->blk_dva[1]) + \ !!DVA_GET_ASIZE(&(bp)->blk_dva[2])) #define BP_COUNT_GANG(bp) \ (DVA_GET_GANG(&(bp)->blk_dva[0]) + \ DVA_GET_GANG(&(bp)->blk_dva[1]) + \ DVA_GET_GANG(&(bp)->blk_dva[2])) #define DVA_EQUAL(dva1, dva2) \ ((dva1)->dva_word[1] == (dva2)->dva_word[1] && \ (dva1)->dva_word[0] == (dva2)->dva_word[0]) #define BP_EQUAL(bp1, bp2) \ (BP_PHYSICAL_BIRTH(bp1) == BP_PHYSICAL_BIRTH(bp2) && \ DVA_EQUAL(&(bp1)->blk_dva[0], &(bp2)->blk_dva[0]) && \ DVA_EQUAL(&(bp1)->blk_dva[1], &(bp2)->blk_dva[1]) && \ DVA_EQUAL(&(bp1)->blk_dva[2], &(bp2)->blk_dva[2])) #define ZIO_CHECKSUM_EQUAL(zc1, zc2) \ (0 == (((zc1).zc_word[0] - (zc2).zc_word[0]) | \ ((zc1).zc_word[1] - (zc2).zc_word[1]) | \ ((zc1).zc_word[2] - (zc2).zc_word[2]) | \ ((zc1).zc_word[3] - (zc2).zc_word[3]))) #define DVA_IS_VALID(dva) (DVA_GET_ASIZE(dva) != 0) #define ZIO_SET_CHECKSUM(zcp, w0, w1, w2, w3) \ { \ (zcp)->zc_word[0] = w0; \ (zcp)->zc_word[1] = w1; \ (zcp)->zc_word[2] = w2; \ (zcp)->zc_word[3] = w3; \ } #define BP_IDENTITY(bp) (&(bp)->blk_dva[0]) #define BP_IS_GANG(bp) DVA_GET_GANG(BP_IDENTITY(bp)) #define BP_IS_HOLE(bp) ((bp)->blk_birth == 0) /* BP_IS_RAIDZ(bp) assumes no block compression */ #define BP_IS_RAIDZ(bp) (DVA_GET_ASIZE(&(bp)->blk_dva[0]) > \ BP_GET_PSIZE(bp)) #define BP_ZERO(bp) \ { \ (bp)->blk_dva[0].dva_word[0] = 0; \ (bp)->blk_dva[0].dva_word[1] = 0; \ (bp)->blk_dva[1].dva_word[0] = 0; \ (bp)->blk_dva[1].dva_word[1] = 0; \ (bp)->blk_dva[2].dva_word[0] = 0; \ (bp)->blk_dva[2].dva_word[1] = 0; \ (bp)->blk_prop = 0; \ (bp)->blk_pad[0] = 0; \ (bp)->blk_pad[1] = 0; \ (bp)->blk_phys_birth = 0; \ (bp)->blk_birth = 0; \ (bp)->blk_fill = 0; \ ZIO_SET_CHECKSUM(&(bp)->blk_cksum, 0, 0, 0, 0); \ } /* * Note: the byteorder is either 0 or -1, both of which are palindromes. * This simplifies the endianness handling a bit. */ #ifdef _BIG_ENDIAN #define ZFS_HOST_BYTEORDER (0ULL) #else #define ZFS_HOST_BYTEORDER (-1ULL) #endif #define BP_SHOULD_BYTESWAP(bp) (BP_GET_BYTEORDER(bp) != ZFS_HOST_BYTEORDER) #define BP_SPRINTF_LEN 320 #endif /* _SYS_SPA_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/uberblock_impl.h0000664000175000017500000000356113256712137022113 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_UBERBLOCK_IMPL_H #define _SYS_UBERBLOCK_IMPL_H #pragma ident "%Z%%M% %I% %E% SMI" /* * The uberblock version is incremented whenever an incompatible on-disk * format change is made to the SPA, DMU, or ZAP. * * Note: the first two fields should never be moved. When a storage pool * is opened, the uberblock must be read off the disk before the version * can be checked. If the ub_version field is moved, we may not detect * version mismatch. If the ub_magic field is moved, applications that * expect the magic number in the first word won't work. */ #define UBERBLOCK_MAGIC 0x00bab10c /* oo-ba-bloc! */ #define UBERBLOCK_SHIFT 10 /* up to 1K */ struct uberblock { uint64_t ub_magic; /* UBERBLOCK_MAGIC */ uint64_t ub_version; /* ZFS_VERSION */ uint64_t ub_txg; /* txg of last sync */ uint64_t ub_guid_sum; /* sum of all vdev guids */ uint64_t ub_timestamp; /* UTC time of last sync */ blkptr_t ub_rootbp; /* MOS objset_phys_t */ }; #endif /* _SYS_UBERBLOCK_IMPL_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/zap_leaf.h0000664000175000017500000000635713256712137020711 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_ZAP_LEAF_H #define _SYS_ZAP_LEAF_H #pragma ident "%Z%%M% %I% %E% SMI" #define ZAP_LEAF_MAGIC 0x2AB1EAF /* chunk size = 24 bytes */ #define ZAP_LEAF_CHUNKSIZE 24 /* * The amount of space within the chunk available for the array is: * chunk size - space for type (1) - space for next pointer (2) */ #define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3) typedef enum zap_chunk_type { ZAP_CHUNK_FREE = 253, ZAP_CHUNK_ENTRY = 252, ZAP_CHUNK_ARRAY = 251, ZAP_CHUNK_TYPE_MAX = 250 } zap_chunk_type_t; /* * TAKE NOTE: * If zap_leaf_phys_t is modified, zap_leaf_byteswap() must be modified. */ typedef struct zap_leaf_phys { struct zap_leaf_header { uint64_t lh_block_type; /* ZBT_LEAF */ uint64_t lh_pad1; uint64_t lh_prefix; /* hash prefix of this leaf */ uint32_t lh_magic; /* ZAP_LEAF_MAGIC */ uint16_t lh_nfree; /* number free chunks */ uint16_t lh_nentries; /* number of entries */ uint16_t lh_prefix_len; /* num bits used to id this */ /* above is accessable to zap, below is zap_leaf private */ uint16_t lh_freelist; /* chunk head of free list */ uint8_t lh_pad2[12]; } l_hdr; /* 2 24-byte chunks */ /* * The header is followed by a hash table with * ZAP_LEAF_HASH_NUMENTRIES(zap) entries. The hash table is * followed by an array of ZAP_LEAF_NUMCHUNKS(zap) * zap_leaf_chunk structures. These structures are accessed * with the ZAP_LEAF_CHUNK() macro. */ uint16_t l_hash[1]; } zap_leaf_phys_t; typedef union zap_leaf_chunk { struct zap_leaf_entry { uint8_t le_type; /* always ZAP_CHUNK_ENTRY */ uint8_t le_int_size; /* size of ints */ uint16_t le_next; /* next entry in hash chain */ uint16_t le_name_chunk; /* first chunk of the name */ uint16_t le_name_length; /* bytes in name, incl null */ uint16_t le_value_chunk; /* first chunk of the value */ uint16_t le_value_length; /* value length in ints */ uint32_t le_cd; /* collision differentiator */ uint64_t le_hash; /* hash value of the name */ } l_entry; struct zap_leaf_array { uint8_t la_type; /* always ZAP_CHUNK_ARRAY */ uint8_t la_array[ZAP_LEAF_ARRAY_BYTES]; uint16_t la_next; /* next blk or CHAIN_END */ } l_array; struct zap_leaf_free { uint8_t lf_type; /* always ZAP_CHUNK_FREE */ uint8_t lf_pad[ZAP_LEAF_ARRAY_BYTES]; uint16_t lf_next; /* next in free list, or CHAIN_END */ } l_free; } zap_leaf_chunk_t; #endif /* _SYS_ZAP_LEAF_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/dnode.h0000664000175000017500000000537113256712137020214 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_DNODE_H #define _SYS_DNODE_H /* * Fixed constants. */ #define DNODE_SHIFT 9 /* 512 bytes */ #define DN_MIN_INDBLKSHIFT 10 /* 1k */ #define DN_MAX_INDBLKSHIFT 14 /* 16k */ #define DNODE_BLOCK_SHIFT 14 /* 16k */ #define DNODE_CORE_SIZE 64 /* 64 bytes for dnode sans blkptrs */ #define DN_MAX_OBJECT_SHIFT 48 /* 256 trillion (zfs_fid_t limit) */ #define DN_MAX_OFFSET_SHIFT 64 /* 2^64 bytes in a dnode */ /* * Derived constants. */ #define DNODE_SIZE (1 << DNODE_SHIFT) #define DN_MAX_NBLKPTR ((DNODE_SIZE - DNODE_CORE_SIZE) >> SPA_BLKPTRSHIFT) #define DN_MAX_BONUSLEN (DNODE_SIZE - DNODE_CORE_SIZE - (1 << SPA_BLKPTRSHIFT)) #define DN_MAX_OBJECT (1ULL << DN_MAX_OBJECT_SHIFT) #define DNODES_PER_BLOCK_SHIFT (DNODE_BLOCK_SHIFT - DNODE_SHIFT) #define DNODES_PER_BLOCK (1ULL << DNODES_PER_BLOCK_SHIFT) #define DNODES_PER_LEVEL_SHIFT (DN_MAX_INDBLKSHIFT - SPA_BLKPTRSHIFT) #define DNODE_FLAG_SPILL_BLKPTR (1<<2) #define DN_BONUS(dnp) ((void*)((dnp)->dn_bonus + \ (((dnp)->dn_nblkptr - 1) * sizeof (blkptr_t)))) typedef struct dnode_phys { uint8_t dn_type; /* dmu_object_type_t */ uint8_t dn_indblkshift; /* ln2(indirect block size) */ uint8_t dn_nlevels; /* 1=dn_blkptr->data blocks */ uint8_t dn_nblkptr; /* length of dn_blkptr */ uint8_t dn_bonustype; /* type of data in bonus buffer */ uint8_t dn_checksum; /* ZIO_CHECKSUM type */ uint8_t dn_compress; /* ZIO_COMPRESS type */ uint8_t dn_flags; /* DNODE_FLAG_* */ uint16_t dn_datablkszsec; /* data block size in 512b sectors */ uint16_t dn_bonuslen; /* length of dn_bonus */ uint8_t dn_pad2[4]; /* accounting is protected by dn_dirty_mtx */ uint64_t dn_maxblkid; /* largest allocated block ID */ uint64_t dn_used; /* bytes (or sectors) of disk space */ uint64_t dn_pad3[4]; blkptr_t dn_blkptr[1]; uint8_t dn_bonus[DN_MAX_BONUSLEN - sizeof (blkptr_t)]; blkptr_t dn_spill; } dnode_phys_t; #endif /* _SYS_DNODE_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/zfs_acl.h0000664000175000017500000000374413256712137020546 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_FS_ZFS_ACL_H #define _SYS_FS_ZFS_ACL_H #pragma ident "%Z%%M% %I% %E% SMI" #ifndef _UID_T #define _UID_T typedef unsigned int uid_t; /* UID type */ #endif /* _UID_T */ typedef struct zfs_oldace { uint32_t z_fuid; /* "who" */ uint32_t z_access_mask; /* access mask */ uint16_t z_flags; /* flags, i.e inheritance */ uint16_t z_type; /* type of entry allow/deny */ } zfs_oldace_t; #define ACE_SLOT_CNT 6 typedef struct zfs_znode_acl_v0 { uint64_t z_acl_extern_obj; /* ext acl pieces */ uint32_t z_acl_count; /* Number of ACEs */ uint16_t z_acl_version; /* acl version */ uint16_t z_acl_pad; /* pad */ zfs_oldace_t z_ace_data[ACE_SLOT_CNT]; /* 6 standard ACEs */ } zfs_znode_acl_v0_t; #define ZFS_ACE_SPACE (sizeof (zfs_oldace_t) * ACE_SLOT_CNT) typedef struct zfs_znode_acl { uint64_t z_acl_extern_obj; /* ext acl pieces */ uint32_t z_acl_size; /* Number of bytes in ACL */ uint16_t z_acl_version; /* acl version */ uint16_t z_acl_count; /* ace count */ uint8_t z_ace_data[ZFS_ACE_SPACE]; /* space for embedded ACEs */ } zfs_znode_acl_t; #endif /* _SYS_FS_ZFS_ACL_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/zfs_znode.h0000664000175000017500000000501013256712137021112 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_FS_ZFS_ZNODE_H #define _SYS_FS_ZFS_ZNODE_H #define MASTER_NODE_OBJ 1 #define ZFS_ROOT_OBJ "ROOT" #define ZPL_VERSION_STR "VERSION" #define ZFS_SA_ATTRS "SA_ATTRS" #define ZPL_VERSION 5ULL #define ZFS_DIRENT_OBJ(de) BF64_GET(de, 0, 48) /* * This is the persistent portion of the znode. It is stored * in the "bonus buffer" of the file. Short symbolic links * are also stored in the bonus buffer. */ typedef struct znode_phys { uint64_t zp_atime[2]; /* 0 - last file access time */ uint64_t zp_mtime[2]; /* 16 - last file modification time */ uint64_t zp_ctime[2]; /* 32 - last file change time */ uint64_t zp_crtime[2]; /* 48 - creation time */ uint64_t zp_gen; /* 64 - generation (txg of creation) */ uint64_t zp_mode; /* 72 - file mode bits */ uint64_t zp_size; /* 80 - size of file */ uint64_t zp_parent; /* 88 - directory parent (`..') */ uint64_t zp_links; /* 96 - number of links to file */ uint64_t zp_xattr; /* 104 - DMU object for xattrs */ uint64_t zp_rdev; /* 112 - dev_t for VBLK & VCHR files */ uint64_t zp_flags; /* 120 - persistent flags */ uint64_t zp_uid; /* 128 - file owner */ uint64_t zp_gid; /* 136 - owning group */ uint64_t zp_pad[4]; /* 144 - future */ zfs_znode_acl_t zp_acl; /* 176 - 263 ACL */ /* * Data may pad out any remaining bytes in the znode buffer, eg: * * |<---------------------- dnode_phys (512) ------------------------>| * |<-- dnode (192) --->|<----------- "bonus" buffer (320) ---------->| * |<---- znode (264) ---->|<---- data (56) ---->| * * At present, we only use this space to store symbolic links. */ } znode_phys_t; #endif /* _SYS_FS_ZFS_ZNODE_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/zio.h0000664000175000017500000000422513256712137017721 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _ZIO_H #define _ZIO_H #define ZEC_MAGIC 0x210da7ab10c7a11ULL /* zio data bloc tail */ typedef struct zio_eck { uint64_t zec_magic; /* for validation, endianness */ zio_cksum_t zec_cksum; /* 256-bit checksum */ } zio_eck_t; /* * Gang block headers are self-checksumming and contain an array * of block pointers. */ #define SPA_GANGBLOCKSIZE SPA_MINBLOCKSIZE #define SPA_GBH_NBLKPTRS ((SPA_GANGBLOCKSIZE - \ sizeof (zio_eck_t)) / sizeof (blkptr_t)) #define SPA_GBH_FILLER ((SPA_GANGBLOCKSIZE - \ sizeof (zio_eck_t) - \ (SPA_GBH_NBLKPTRS * sizeof (blkptr_t))) /\ sizeof (uint64_t)) #define ZIO_GET_IOSIZE(zio) \ (BP_IS_GANG((zio)->io_bp) ? \ SPA_GANGBLOCKSIZE : BP_GET_PSIZE((zio)->io_bp)) typedef struct zio_gbh { blkptr_t zg_blkptr[SPA_GBH_NBLKPTRS]; uint64_t zg_filler[SPA_GBH_FILLER]; zio_eck_t zg_tail; } zio_gbh_phys_t; enum zio_checksum { ZIO_CHECKSUM_INHERIT = 0, ZIO_CHECKSUM_ON, ZIO_CHECKSUM_OFF, ZIO_CHECKSUM_LABEL, ZIO_CHECKSUM_GANG_HEADER, ZIO_CHECKSUM_ZILOG, ZIO_CHECKSUM_FLETCHER_2, ZIO_CHECKSUM_FLETCHER_4, ZIO_CHECKSUM_SHA256, ZIO_CHECKSUM_ZILOG2, ZIO_CHECKSUM_FUNCTIONS }; enum zio_compress { ZIO_COMPRESS_INHERIT = 0, ZIO_COMPRESS_ON, ZIO_COMPRESS_OFF, ZIO_COMPRESS_LZJB, ZIO_COMPRESS_EMPTY, ZIO_COMPRESS_FUNCTIONS }; #endif /* _ZIO_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/dmu.h0000664000175000017500000000720113256712137017702 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_DMU_H #define _SYS_DMU_H /* * This file describes the interface that the DMU provides for its * consumers. * * The DMU also interacts with the SPA. That interface is described in * dmu_spa.h. */ typedef enum dmu_object_type { DMU_OT_NONE, /* general: */ DMU_OT_OBJECT_DIRECTORY, /* ZAP */ DMU_OT_OBJECT_ARRAY, /* UINT64 */ DMU_OT_PACKED_NVLIST, /* UINT8 (XDR by nvlist_pack/unpack) */ DMU_OT_PACKED_NVLIST_SIZE, /* UINT64 */ DMU_OT_BPLIST, /* UINT64 */ DMU_OT_BPLIST_HDR, /* UINT64 */ /* spa: */ DMU_OT_SPACE_MAP_HEADER, /* UINT64 */ DMU_OT_SPACE_MAP, /* UINT64 */ /* zil: */ DMU_OT_INTENT_LOG, /* UINT64 */ /* dmu: */ DMU_OT_DNODE, /* DNODE */ DMU_OT_OBJSET, /* OBJSET */ /* dsl: */ DMU_OT_DSL_DIR, /* UINT64 */ DMU_OT_DSL_DIR_CHILD_MAP, /* ZAP */ DMU_OT_DSL_DS_SNAP_MAP, /* ZAP */ DMU_OT_DSL_PROPS, /* ZAP */ DMU_OT_DSL_DATASET, /* UINT64 */ /* zpl: */ DMU_OT_ZNODE, /* ZNODE */ DMU_OT_OLDACL, /* OLD ACL */ DMU_OT_PLAIN_FILE_CONTENTS, /* UINT8 */ DMU_OT_DIRECTORY_CONTENTS, /* ZAP */ DMU_OT_MASTER_NODE, /* ZAP */ DMU_OT_UNLINKED_SET, /* ZAP */ /* zvol: */ DMU_OT_ZVOL, /* UINT8 */ DMU_OT_ZVOL_PROP, /* ZAP */ /* other; for testing only! */ DMU_OT_PLAIN_OTHER, /* UINT8 */ DMU_OT_UINT64_OTHER, /* UINT64 */ DMU_OT_ZAP_OTHER, /* ZAP */ /* new object types: */ DMU_OT_ERROR_LOG, /* ZAP */ DMU_OT_SPA_HISTORY, /* UINT8 */ DMU_OT_SPA_HISTORY_OFFSETS, /* spa_his_phys_t */ DMU_OT_POOL_PROPS, /* ZAP */ DMU_OT_DSL_PERMS, /* ZAP */ DMU_OT_ACL, /* ACL */ DMU_OT_SYSACL, /* SYSACL */ DMU_OT_FUID, /* FUID table (Packed NVLIST UINT8) */ DMU_OT_FUID_SIZE, /* FUID table size UINT64 */ DMU_OT_NEXT_CLONES, /* ZAP */ DMU_OT_SCRUB_QUEUE, /* ZAP */ DMU_OT_USERGROUP_USED, /* ZAP */ DMU_OT_USERGROUP_QUOTA, /* ZAP */ DMU_OT_USERREFS, /* ZAP */ DMU_OT_DDT_ZAP, /* ZAP */ DMU_OT_DDT_STATS, /* ZAP */ DMU_OT_SA, /* System attr */ DMU_OT_SA_MASTER_NODE, /* ZAP */ DMU_OT_SA_ATTR_REGISTRATION, /* ZAP */ DMU_OT_SA_ATTR_LAYOUTS, /* ZAP */ DMU_OT_NUMTYPES } dmu_object_type_t; typedef enum dmu_objset_type { DMU_OST_NONE, DMU_OST_META, DMU_OST_ZFS, DMU_OST_ZVOL, DMU_OST_OTHER, /* For testing only! */ DMU_OST_ANY, /* Be careful! */ DMU_OST_NUMTYPES } dmu_objset_type_t; /* * The names of zap entries in the DIRECTORY_OBJECT of the MOS. */ #define DMU_POOL_DIRECTORY_OBJECT 1 #define DMU_POOL_CONFIG "config" #define DMU_POOL_ROOT_DATASET "root_dataset" #define DMU_POOL_SYNC_BPLIST "sync_bplist" #define DMU_POOL_ERRLOG_SCRUB "errlog_scrub" #define DMU_POOL_ERRLOG_LAST "errlog_last" #define DMU_POOL_SPARES "spares" #define DMU_POOL_DEFLATE "deflate" #define DMU_POOL_HISTORY "history" #define DMU_POOL_PROPS "pool_props" #define DMU_POOL_L2CACHE "l2cache" #endif /* _SYS_DMU_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/zio_checksum.h0000664000175000017500000000263113256712137021602 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_ZIO_CHECKSUM_H #define _SYS_ZIO_CHECKSUM_H /* * Signature for checksum functions. */ typedef void zio_checksum_t(const void *data, uint64_t size, zio_cksum_t *zcp); /* * Information about each checksum function. */ typedef struct zio_checksum_info { zio_checksum_t *ci_func[2]; /* checksum function for each byteorder */ int ci_correctable; /* number of correctable bits */ int ci_eck; /* uses zio embedded checksum? */ char *ci_name; /* descriptive name */ } zio_checksum_info_t; #endif /* _SYS_ZIO_CHECKSUM_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/sa_impl.h0000664000175000017500000000223713256712137020545 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_SA_IMPL_H #define _SYS_SA_IMPL_H typedef struct sa_hdr_phys { uint32_t sa_magic; uint16_t sa_layout_info; uint16_t sa_lengths[1]; } sa_hdr_phys_t; #define SA_HDR_SIZE(hdr) BF32_GET_SB(hdr->sa_layout_info, 10, 16, 3, 0) #define SA_SIZE_OFFSET 0x8 #endif /* _SYS_SA_IMPL_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/dmu_objset.h0000664000175000017500000000240313256712137021247 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_DMU_OBJSET_H #define _SYS_DMU_OBJSET_H typedef struct objset_phys { dnode_phys_t os_meta_dnode; zil_header_t os_zil_header; uint64_t os_type; uint64_t os_flags; char os_pad[2048 - sizeof (dnode_phys_t)*3 - sizeof (zil_header_t) - sizeof (uint64_t)*2]; dnode_phys_t os_userused_dnode; dnode_phys_t os_groupused_dnode; } objset_phys_t; #endif /* _SYS_DMU_OBJSET_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/dsl_dir.h0000664000175000017500000000324213256712137020536 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_DSL_DIR_H #define _SYS_DSL_DIR_H #pragma ident "%Z%%M% %I% %E% SMI" typedef struct dsl_dir_phys { uint64_t dd_creation_time; /* not actually used */ uint64_t dd_head_dataset_obj; uint64_t dd_parent_obj; uint64_t dd_clone_parent_obj; uint64_t dd_child_dir_zapobj; /* * how much space our children are accounting for; for leaf * datasets, == physical space used by fs + snaps */ uint64_t dd_used_bytes; uint64_t dd_compressed_bytes; uint64_t dd_uncompressed_bytes; /* Administrative quota setting */ uint64_t dd_quota; /* Administrative reservation setting */ uint64_t dd_reserved; uint64_t dd_props_zapobj; uint64_t dd_deleg_zapobj; /* dataset permissions */ uint64_t dd_pad[20]; /* pad out to 256 bytes for good measure */ } dsl_dir_phys_t; #endif /* _SYS_DSL_DIR_H */ xen-4.9.2/tools/libfsimage/zfs/zfs-include/dsl_dataset.h0000664000175000017500000000356613256712137021416 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_DSL_DATASET_H #define _SYS_DSL_DATASET_H #pragma ident "%Z%%M% %I% %E% SMI" typedef struct dsl_dataset_phys { uint64_t ds_dir_obj; uint64_t ds_prev_snap_obj; uint64_t ds_prev_snap_txg; uint64_t ds_next_snap_obj; uint64_t ds_snapnames_zapobj; /* zap obj of snaps; ==0 for snaps */ uint64_t ds_num_children; /* clone/snap children; ==0 for head */ uint64_t ds_creation_time; /* seconds since 1970 */ uint64_t ds_creation_txg; uint64_t ds_deadlist_obj; uint64_t ds_used_bytes; uint64_t ds_compressed_bytes; uint64_t ds_uncompressed_bytes; uint64_t ds_unique_bytes; /* only relevant to snapshots */ /* * The ds_fsid_guid is a 56-bit ID that can change to avoid * collisions. The ds_guid is a 64-bit ID that will never * change, so there is a small probability that it will collide. */ uint64_t ds_fsid_guid; uint64_t ds_guid; uint64_t ds_flags; blkptr_t ds_bp; uint64_t ds_pad[8]; /* pad out to 320 bytes for good measure */ } dsl_dataset_phys_t; #endif /* _SYS_DSL_DATASET_H */ xen-4.9.2/tools/libfsimage/zfs/mb_info.h0000664000175000017500000001166613256712137016315 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 2000,2003 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * The structure type "mod_list" is used by the "multiboot_info" structure. */ struct mod_list { /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ unsigned long mod_start; unsigned long mod_end; /* Module command line */ unsigned long cmdline; /* padding to take it to 16 bytes (must be zero) */ unsigned long pad; }; /* * INT-15, AX=E820 style "AddressRangeDescriptor" * ...with a "size" parameter on the front which is the structure size - 4, * pointing to the next one, up until the full buffer length of the memory * map has been reached. */ struct AddrRangeDesc { unsigned long size; unsigned long long BaseAddr; unsigned long long Length; unsigned long Type; /* unspecified optional padding... */ } __attribute__ ((packed)); /* usable memory "Type", all others are reserved. */ #define MB_ARD_MEMORY 1 /* Drive Info structure. */ struct drive_info { /* The size of this structure. */ unsigned long size; /* The BIOS drive number. */ unsigned char drive_number; /* The access mode (see below). */ unsigned char drive_mode; /* The BIOS geometry. */ unsigned short drive_cylinders; unsigned char drive_heads; unsigned char drive_sectors; /* The array of I/O ports used for the drive. */ unsigned short drive_ports[0]; }; /* Drive Mode. */ #define MB_DI_CHS_MODE 0 #define MB_DI_LBA_MODE 1 /* APM BIOS info. */ struct apm_info { unsigned short version; unsigned short cseg; unsigned long offset; unsigned short cseg_16; unsigned short dseg_16; unsigned short cseg_len; unsigned short cseg_16_len; unsigned short dseg_16_len; }; /* * MultiBoot Info description * * This is the struct passed to the boot image. This is done by placing * its address in the EAX register. */ struct multiboot_info { /* MultiBoot info version number */ unsigned long flags; /* Available memory from BIOS */ unsigned long mem_lower; unsigned long mem_upper; /* "root" partition */ unsigned long boot_device; /* Kernel command line */ unsigned long cmdline; /* Boot-Module list */ unsigned long mods_count; unsigned long mods_addr; union { struct { /* (a.out) Kernel symbol table info */ unsigned long tabsize; unsigned long strsize; unsigned long addr; unsigned long pad; } a; struct { /* (ELF) Kernel section header table */ unsigned long num; unsigned long size; unsigned long addr; unsigned long shndx; } e; } syms; /* Memory Mapping buffer */ unsigned long mmap_length; unsigned long mmap_addr; /* Drive Info buffer */ unsigned long drives_length; unsigned long drives_addr; /* ROM configuration table */ unsigned long config_table; /* Boot Loader Name */ unsigned long boot_loader_name; /* APM table */ unsigned long apm_table; /* Video */ unsigned long vbe_control_info; unsigned long vbe_mode_info; unsigned short vbe_mode; unsigned short vbe_interface_seg; unsigned short vbe_interface_off; unsigned short vbe_interface_len; }; /* * Flags to be set in the 'flags' parameter above */ /* is there basic lower/upper memory information? */ #define MB_INFO_MEMORY 0x00000001 /* is there a boot device set? */ #define MB_INFO_BOOTDEV 0x00000002 /* is the command-line defined? */ #define MB_INFO_CMDLINE 0x00000004 /* are there modules to do something with? */ #define MB_INFO_MODS 0x00000008 /* These next two are mutually exclusive */ /* is there a symbol table loaded? */ #define MB_INFO_AOUT_SYMS 0x00000010 /* is there an ELF section header table? */ #define MB_INFO_ELF_SHDR 0x00000020 /* is there a full memory map? */ #define MB_INFO_MEM_MAP 0x00000040 /* Is there drive info? */ #define MB_INFO_DRIVE_INFO 0x00000080 /* Is there a config table? */ #define MB_INFO_CONFIG_TABLE 0x00000100 /* Is there a boot loader name? */ #define MB_INFO_BOOT_LOADER_NAME 0x00000200 /* Is there a APM table? */ #define MB_INFO_APM_TABLE 0x00000400 /* Is there video information? */ #define MB_INFO_VIDEO_INFO 0x00000800 /* * The following value must be present in the EAX register. */ #define MULTIBOOT_VALID 0x2BADB002 xen-4.9.2/tools/libfsimage/zfs/zfs_lzjb.c0000664000175000017500000000331613256712137016513 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "fsys_zfs.h" #define MATCH_BITS 6 #define MATCH_MIN 3 #define OFFSET_MASK ((1 << (16 - MATCH_BITS)) - 1) /*ARGSUSED*/ int lzjb_decompress(void *s_start, void *d_start, size_t s_len, size_t d_len) { uint8_t *src = s_start; uint8_t *dst = d_start; uint8_t *d_end = (uint8_t *)d_start + d_len; uint8_t *cpy, copymap = '\0'; int copymask = 1 << (NBBY - 1); while (dst < d_end) { if ((copymask <<= 1) == (1 << NBBY)) { copymask = 1; copymap = *src++; } if (copymap & copymask) { int mlen = (src[0] >> (NBBY - MATCH_BITS)) + MATCH_MIN; int offset = ((src[0] << NBBY) | src[1]) & OFFSET_MASK; src += 2; if ((cpy = dst - offset) < (uint8_t *)d_start) return (-1); while (--mlen >= 0 && dst < d_end) *dst++ = *cpy++; } else { *dst++ = *src++; } } return (0); } xen-4.9.2/tools/libfsimage/zfs/shared.h0000664000175000017500000000201113256712137016132 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SHARED_H #define _SHARED_H #ifdef FSYS_ZFS /* THIS FILE IS INTENTIONALLY BLANK */ #endif /* FSI_ZFS */ #endif /* !_SHARED_H */ xen-4.9.2/tools/libfsimage/zfs/fsys_zfs.h0000664000175000017500000001414513256712137016545 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FSYS_ZFS_H #define _FSYS_ZFS_H #ifdef FSYS_ZFS #ifndef FSIMAGE typedef unsigned long long uint64_t; typedef unsigned int uint32_t; typedef unsigned short uint16_t; typedef unsigned char uint8_t; typedef unsigned char uchar_t; #if defined(_LP64) || defined(_I32LPx) typedef unsigned long size_t; #else typedef unsigned int size_t; #endif #else #include "fsi_zfs.h" #endif /* !FSIMAGE */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Global Memory addresses to store MOS and DNODE data */ #define MOS ((dnode_phys_t *)\ (RAW_ADDR((mbi.mem_upper << 10) + 0x100000) - ZFS_SCRATCH_SIZE)) #define DNODE (MOS+1) /* move sizeof(dnode_phys_t) bytes */ #define ZFS_SCRATCH ((char *)(DNODE+1)) /* * Verify dnode type. * Can only be used in functions returning non-0 for failure. */ #define VERIFY_DN_TYPE(dnp, type) \ if (type && (dnp)->dn_type != type) { \ return (ERR_FSYS_CORRUPT); \ } /* * Verify object set type. * Can only be used in functions returning 0 for failure. */ #define VERIFY_OS_TYPE(osp, type) \ if (type && (osp)->os_type != type) { \ errnum = ERR_FSYS_CORRUPT; \ return (0); \ } #define ZPOOL_PROP_BOOTFS "bootfs" /* General macros */ #define BSWAP_8(x) ((x) & 0xff) #define BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8)) #define BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16)) #define BSWAP_64(x) ((BSWAP_32(x) << 32) | BSWAP_32((x) >> 32)) #define P2ROUNDUP(x, align) (-(-(x) & -(align))) /* * XXX Match these macro up with real zfs once we have nvlist support so that we * can support large sector disks. */ #define UBERBLOCK_SIZE (1ULL << UBERBLOCK_SHIFT) #define VDEV_UBERBLOCK_SHIFT UBERBLOCK_SHIFT #include #define VDEV_UBERBLOCK_OFFSET(n) \ offsetof(vdev_label_t, vl_uberblock[(n) << VDEV_UBERBLOCK_SHIFT]) typedef struct uberblock uberblock_t; /* XXX Uberblock_phys_t is no longer in the kernel zfs */ typedef struct uberblock_phys { uberblock_t ubp_uberblock; char ubp_pad[UBERBLOCK_SIZE - sizeof (uberblock_t) - sizeof (zio_eck_t)]; zio_eck_t ubp_zec; } uberblock_phys_t; /* * Macros to get fields in a bp or DVA. */ #define P2PHASE(x, align) ((x) & ((align) - 1)) #define DVA_OFFSET_TO_PHYS_SECTOR(offset) \ ((offset + VDEV_LABEL_START_SIZE) >> SPA_MINBLOCKSHIFT) /* * return x rounded down to an align boundary * eg, P2ALIGN(1200, 1024) == 1024 (1*align) * eg, P2ALIGN(1024, 1024) == 1024 (1*align) * eg, P2ALIGN(0x1234, 0x100) == 0x1200 (0x12*align) * eg, P2ALIGN(0x5600, 0x100) == 0x5600 (0x56*align) */ #define P2ALIGN(x, align) ((x) & -(align)) /* * For nvlist manipulation. (from nvpair.h) */ #define NV_ENCODE_NATIVE 0 #define NV_ENCODE_XDR 1 #define HOST_ENDIAN 1 /* for x86 machine */ #define DATA_TYPE_UINT64 8 #define DATA_TYPE_STRING 9 #define DATA_TYPE_NVLIST 19 #define DATA_TYPE_NVLIST_ARRAY 20 /* * Decompression Entry - lzjb */ #ifndef NBBY #define NBBY 8 #endif typedef int zfs_decomp_func_t(void *s_start, void *d_start, size_t s_len, size_t d_len); typedef struct decomp_entry { char *name; zfs_decomp_func_t *decomp_func; } decomp_entry_t; /* * FAT ZAP data structures */ #define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */ #define ZAP_HASH_IDX(hash, n) (((n) == 0) ? 0 : ((hash) >> (64 - (n)))) #define CHAIN_END 0xffff /* end of the chunk chain */ /* * The amount of space within the chunk available for the array is: * chunk size - space for type (1) - space for next pointer (2) */ #define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3) #define ZAP_LEAF_HASH_SHIFT(bs) (bs - 5) #define ZAP_LEAF_HASH_NUMENTRIES(bs) (1 << ZAP_LEAF_HASH_SHIFT(bs)) #define LEAF_HASH(bs, h) \ ((ZAP_LEAF_HASH_NUMENTRIES(bs)-1) & \ ((h) >> (64 - ZAP_LEAF_HASH_SHIFT(bs)-l->l_hdr.lh_prefix_len))) /* * The amount of space available for chunks is: * block size shift - hash entry size (2) * number of hash * entries - header space (2*chunksize) */ #define ZAP_LEAF_NUMCHUNKS(bs) \ (((1<l_hash + ZAP_LEAF_HASH_NUMENTRIES(bs)))[idx] #define ZAP_LEAF_ENTRY(l, bs, idx) (&ZAP_LEAF_CHUNK(l, bs, idx).l_entry) extern void fletcher_2_native(const void *, uint64_t, zio_cksum_t *); extern void fletcher_2_byteswap(const void *, uint64_t, zio_cksum_t *); extern void fletcher_4_native(const void *, uint64_t, zio_cksum_t *); extern void fletcher_4_byteswap(const void *, uint64_t, zio_cksum_t *); extern void zio_checksum_SHA256(const void *, uint64_t, zio_cksum_t *); extern int lzjb_decompress(void *, void *, size_t, size_t); #endif /* FSYS_ZFS */ #endif /* !_FSYS_ZFS_H */ xen-4.9.2/tools/libfsimage/zfs/Makefile0000664000175000017500000000221113256712137016155 0ustar smbsmb# # GRUB -- GRand Unified Bootloader # Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; If not, see . # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # XEN_ROOT = $(CURDIR)/../../.. CFLAGS += -DFSYS_ZFS -DFSIMAGE -I$(XEN_ROOT)/tools/libfsimage/zfs LIB_SRCS-y = zfs_lzjb.c zfs_sha256.c zfs_fletcher.c fsi_zfs.c fsys_zfs.c FS = zfs .PHONY: all all: fs-all .PHONY: install install: fs-install include $(XEN_ROOT)/tools/libfsimage/Rules.mk xen-4.9.2/tools/libfsimage/zfs/zfs_sha256.c0000664000175000017500000001003013256712137016551 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "fsys_zfs.h" /* * SHA-256 checksum, as specified in FIPS 180-2, available at: * http://csrc.nist.gov/cryptval * * This is a very compact implementation of SHA-256. * It is designed to be simple and portable, not to be fast. */ /* * The literal definitions according to FIPS180-2 would be: * * Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z))) * Maj(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) * * We use logical equivalents which require one less op. */ #define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define Maj(x, y, z) (((x) & (y)) ^ ((z) & ((x) ^ (y)))) #define Rot32(x, s) (((x) >> s) | ((x) << (32 - s))) #define SIGMA0(x) (Rot32(x, 2) ^ Rot32(x, 13) ^ Rot32(x, 22)) #define SIGMA1(x) (Rot32(x, 6) ^ Rot32(x, 11) ^ Rot32(x, 25)) #define sigma0(x) (Rot32(x, 7) ^ Rot32(x, 18) ^ ((x) >> 3)) #define sigma1(x) (Rot32(x, 17) ^ Rot32(x, 19) ^ ((x) >> 10)) static const uint32_t SHA256_K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; static void SHA256Transform(uint32_t *H, const uint8_t *cp) { uint32_t a, b, c, d, e, f, g, h, t, T1, T2, W[64]; for (t = 0; t < 16; t++, cp += 4) W[t] = (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | cp[3]; for (t = 16; t < 64; t++) W[t] = sigma1(W[t - 2]) + W[t - 7] + sigma0(W[t - 15]) + W[t - 16]; a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; for (t = 0; t < 64; t++) { T1 = h + SIGMA1(e) + Ch(e, f, g) + SHA256_K[t] + W[t]; T2 = SIGMA0(a) + Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } H[0] += a; H[1] += b; H[2] += c; H[3] += d; H[4] += e; H[5] += f; H[6] += g; H[7] += h; } void zio_checksum_SHA256(const void *buf, uint64_t size, zio_cksum_t *zcp) { uint32_t H[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; uint8_t pad[128]; int padsize = size & 63; int i; for (i = 0; i < size - padsize; i += 64) SHA256Transform(H, (uint8_t *)buf + i); for (i = 0; i < padsize; i++) pad[i] = ((uint8_t *)buf)[i]; for (pad[padsize++] = 0x80; (padsize & 63) != 56; padsize++) pad[padsize] = 0; for (i = 0; i < 8; i++) pad[padsize++] = (size << 3) >> (56 - 8 * i); for (i = 0; i < padsize; i += 64) SHA256Transform(H, pad + i); ZIO_SET_CHECKSUM(zcp, (uint64_t)H[0] << 32 | H[1], (uint64_t)H[2] << 32 | H[3], (uint64_t)H[4] << 32 | H[5], (uint64_t)H[6] << 32 | H[7]); } xen-4.9.2/tools/libfsimage/zfs/filesys.h0000664000175000017500000000201413256712137016345 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FILESYS_H #define _FILESYS_H #ifdef FSYS_ZFS /* THIS FILE IS INTENTIONALLY BLANK */ #endif /* FSI_ZFS */ #endif /* !_FILESYS_H */ xen-4.9.2/tools/libfsimage/zfs/zfs_fletcher.c0000664000175000017500000000436613256712137017354 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "fsys_zfs.h" void fletcher_2_native(const void *buf, uint64_t size, zio_cksum_t *zcp) { const uint64_t *ip = buf; const uint64_t *ipend = ip + (size / sizeof (uint64_t)); uint64_t a0, b0, a1, b1; for (a0 = b0 = a1 = b1 = 0; ip < ipend; ip += 2) { a0 += ip[0]; a1 += ip[1]; b0 += a0; b1 += a1; } ZIO_SET_CHECKSUM(zcp, a0, a1, b0, b1); } void fletcher_2_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp) { const uint64_t *ip = buf; const uint64_t *ipend = ip + (size / sizeof (uint64_t)); uint64_t a0, b0, a1, b1; for (a0 = b0 = a1 = b1 = 0; ip < ipend; ip += 2) { a0 += BSWAP_64(ip[0]); a1 += BSWAP_64(ip[1]); b0 += a0; b1 += a1; } ZIO_SET_CHECKSUM(zcp, a0, a1, b0, b1); } void fletcher_4_native(const void *buf, uint64_t size, zio_cksum_t *zcp) { const uint32_t *ip = buf; const uint32_t *ipend = ip + (size / sizeof (uint32_t)); uint64_t a, b, c, d; for (a = b = c = d = 0; ip < ipend; ip++) { a += ip[0]; b += a; c += b; d += c; } ZIO_SET_CHECKSUM(zcp, a, b, c, d); } void fletcher_4_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp) { const uint32_t *ip = buf; const uint32_t *ipend = ip + (size / sizeof (uint32_t)); uint64_t a, b, c, d; for (a = b = c = d = 0; ip < ipend; ip++) { a += BSWAP_32(ip[0]); b += a; c += b; d += c; } ZIO_SET_CHECKSUM(zcp, a, b, c, d); } xen-4.9.2/tools/libfsimage/zfs/fsi_zfs.h0000664000175000017500000000517213256712137016342 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FSI_ZFS_H #define _FSI_ZFS_H #ifdef FSYS_ZFS #include #include #include #include /*** START FROM shared.h ****/ #include "mb_info.h" /* Boot signature related defines for the findroot command */ #define BOOTSIGN_DIR "/boot/grub/bootsign" #define BOOTSIGN_BACKUP "/etc/bootsign" /* Maybe redirect memory requests through grub_scratch_mem. */ #define RAW_ADDR(x) (x) #define RAW_SEG(x) (x) /* ZFS will use the top 4 Meg of physical memory (below 4Gig) for sratch */ #define ZFS_SCRATCH_SIZE 0x400000 #define MAXPATHLEN 1024 #define MAXNAMELEN 256 #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAXUINT 0xFFFFFFFF #undef NULL #define NULL ((void *) 0) #define grub_printf printf #define grub_strcmp strcmp #define grub_strncmp strncmp #define grub_strstr strstr #define grub_strlen strlen #define grub_memmove memmove extern char current_bootpath[MAXPATHLEN]; extern char current_rootpool[MAXNAMELEN]; extern char current_bootfs[MAXNAMELEN]; extern uint64_t current_bootfs_obj; extern char current_devid[MAXPATHLEN]; extern int is_zfs_mount; extern unsigned long best_drive; extern unsigned long best_part; extern int find_best_root; extern unsigned long part_length; #undef filemax #undef filepos extern uint64_t filemax; extern uint64_t filepos; extern struct multiboot_info mbi; /*** END FROM shared.h ***/ #ifdef __linux__ typedef unsigned char uchar_t; #endif typedef struct fsi_file *fsi_file_handle_t; extern fsi_file_handle_t zfs_ffi; extern int fsig_devread(fsi_file_handle_t, unsigned int, unsigned int, unsigned int, char *); #undef devread #define devread(a, b, c, d) fsig_devread(zfs_ffi, a, b, c, d) #undef errnum extern int errnum; #endif /* FSI_ZFS */ #endif /* !_FSI_ZFS_H */ xen-4.9.2/tools/libfsimage/zfs/fsys_zfs.c0000664000175000017500000011115113256712137016533 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * The zfs plug-in routines for GRUB are: * * zfs_mount() - locates a valid uberblock of the root pool and reads * in its MOS at the memory address MOS. * * zfs_open() - locates a plain file object by following the MOS * and places its dnode at the memory address DNODE. * * zfs_read() - read in the data blocks pointed by the DNODE. * * ZFS_SCRATCH is used as a working area. * * (memory addr) MOS DNODE ZFS_SCRATCH * | | | * +-------V---------V----------V---------------+ * memory | | dnode | dnode | scratch | * | | 512B | 512B | area | * +--------------------------------------------+ */ #ifdef FSYS_ZFS #include "shared.h" #include "filesys.h" #include "fsys_zfs.h" /* cache for a file block of the currently zfs_open()-ed file */ static void *file_buf = NULL; static uint64_t file_start = 0; static uint64_t file_end = 0; /* cache for a dnode block */ static dnode_phys_t *dnode_buf = NULL; static dnode_phys_t *dnode_mdn = NULL; static uint64_t dnode_start = 0; static uint64_t dnode_end = 0; static uint64_t pool_guid = 0; static uberblock_t current_uberblock; static char *stackbase; decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = { {"inherit", 0}, /* ZIO_COMPRESS_INHERIT */ {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */ {"off", 0}, /* ZIO_COMPRESS_OFF */ {"lzjb", lzjb_decompress}, /* ZIO_COMPRESS_LZJB */ {"empty", 0} /* ZIO_COMPRESS_EMPTY */ }; static int zio_read_data(blkptr_t *bp, void *buf, char *stack); /* * Our own version of bcmp(). */ static int zfs_bcmp(const void *s1, const void *s2, size_t n) { const uint8_t *ps1 = s1; const uint8_t *ps2 = s2; if (s1 != s2 && n != 0) { do { if (*ps1++ != *ps2++) return (1); } while (--n != 0); } return (0); } /* * Our own version of log2(). Same thing as highbit()-1. */ static int zfs_log2(uint64_t num) { int i = 0; while (num > 1) { i++; num = num >> 1; } return (i); } /* Checksum Functions */ static void zio_checksum_off(const void *buf, uint64_t size, zio_cksum_t *zcp) { ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0); } /* Checksum Table and Values */ zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { { { NULL, NULL }, 0, 0, "inherit" }, { { NULL, NULL }, 0, 0, "on" }, { { zio_checksum_off, zio_checksum_off }, 0, 0, "off" }, { { zio_checksum_SHA256, zio_checksum_SHA256 }, 1, 1, "label" }, { { zio_checksum_SHA256, zio_checksum_SHA256 }, 1, 1, "gang_header" }, { { NULL, NULL }, 0, 0, "zilog" }, { { fletcher_2_native, fletcher_2_byteswap }, 0, 0, "fletcher2" }, { { fletcher_4_native, fletcher_4_byteswap }, 1, 0, "fletcher4" }, { { zio_checksum_SHA256, zio_checksum_SHA256 }, 1, 0, "SHA256" }, { { NULL, NULL }, 0, 0, "zilog2" } }; /* * zio_checksum_verify: Provides support for checksum verification. * * Fletcher2, Fletcher4, and SHA256 are supported. * * Return: * -1 = Failure * 0 = Success */ static int zio_checksum_verify(blkptr_t *bp, char *data, int size) { zio_cksum_t zc = bp->blk_cksum; uint32_t checksum = BP_GET_CHECKSUM(bp); int byteswap = BP_SHOULD_BYTESWAP(bp); zio_eck_t *zec = (zio_eck_t *)(data + size) - 1; zio_checksum_info_t *ci = &zio_checksum_table[checksum]; zio_cksum_t actual_cksum, expected_cksum; /* byteswap is not supported */ if (byteswap) return (-1); if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func[0] == NULL) return (-1); if (ci->ci_eck) { expected_cksum = zec->zec_cksum; zec->zec_cksum = zc; ci->ci_func[0](data, size, &actual_cksum); zec->zec_cksum = expected_cksum; zc = expected_cksum; } else { ci->ci_func[byteswap](data, size, &actual_cksum); } if ((actual_cksum.zc_word[0] - zc.zc_word[0]) | (actual_cksum.zc_word[1] - zc.zc_word[1]) | (actual_cksum.zc_word[2] - zc.zc_word[2]) | (actual_cksum.zc_word[3] - zc.zc_word[3])) return (-1); return (0); } /* * vdev_label_start returns the physical disk offset (in bytes) of * label "l". */ static uint64_t vdev_label_start(uint64_t psize, int l) { return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ? 0 : psize - VDEV_LABELS * sizeof (vdev_label_t))); } /* * vdev_uberblock_compare takes two uberblock structures and returns an integer * indicating the more recent of the two. * Return Value = 1 if ub2 is more recent * Return Value = -1 if ub1 is more recent * The most recent uberblock is determined using its transaction number and * timestamp. The uberblock with the highest transaction number is * considered "newer". If the transaction numbers of the two blocks match, the * timestamps are compared to determine the "newer" of the two. */ static int vdev_uberblock_compare(uberblock_t *ub1, uberblock_t *ub2) { if (ub1->ub_txg < ub2->ub_txg) return (-1); if (ub1->ub_txg > ub2->ub_txg) return (1); if (ub1->ub_timestamp < ub2->ub_timestamp) return (-1); if (ub1->ub_timestamp > ub2->ub_timestamp) return (1); return (0); } /* * Three pieces of information are needed to verify an uberblock: the magic * number, the version number, and the checksum. * * Currently Implemented: version number, magic number * Need to Implement: checksum * * Return: * 0 - Success * -1 - Failure */ static int uberblock_verify(uberblock_phys_t *ub, uint64_t offset) { uberblock_t *uber = &ub->ubp_uberblock; blkptr_t bp; BP_ZERO(&bp); BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); BP_SET_BYTEORDER(&bp, ZFS_HOST_BYTEORDER); ZIO_SET_CHECKSUM(&bp.blk_cksum, offset, 0, 0, 0); if (zio_checksum_verify(&bp, (char *)ub, UBERBLOCK_SIZE) != 0) return (-1); if (uber->ub_magic == UBERBLOCK_MAGIC && uber->ub_version > 0 && uber->ub_version <= SPA_VERSION) return (0); return (-1); } /* * Find the best uberblock. * Return: * Success - Pointer to the best uberblock. * Failure - NULL */ static uberblock_phys_t * find_bestub(uberblock_phys_t *ub_array, uint64_t sector) { uberblock_phys_t *ubbest = NULL; uint64_t offset; int i; for (i = 0; i < (VDEV_UBERBLOCK_RING >> VDEV_UBERBLOCK_SHIFT); i++) { offset = (sector << SPA_MINBLOCKSHIFT) + VDEV_UBERBLOCK_OFFSET(i); if (uberblock_verify(&ub_array[i], offset) == 0) { if (ubbest == NULL) { ubbest = &ub_array[i]; } else if (vdev_uberblock_compare( &(ub_array[i].ubp_uberblock), &(ubbest->ubp_uberblock)) > 0) { ubbest = &ub_array[i]; } } } return (ubbest); } /* * Read a block of data based on the gang block address dva, * and put its data in buf. * * Return: * 0 - success * 1 - failure */ static int zio_read_gang(blkptr_t *bp, dva_t *dva, void *buf, char *stack) { zio_gbh_phys_t *zio_gb; uint64_t offset, sector; blkptr_t tmpbp; int i; zio_gb = (zio_gbh_phys_t *)stack; stack += SPA_GANGBLOCKSIZE; offset = DVA_GET_OFFSET(dva); sector = DVA_OFFSET_TO_PHYS_SECTOR(offset); /* read in the gang block header */ if (devread(sector, 0, SPA_GANGBLOCKSIZE, (char *)zio_gb) == 0) { grub_printf("failed to read in a gang block header\n"); return (1); } /* self checksuming the gang block header */ BP_ZERO(&tmpbp); BP_SET_CHECKSUM(&tmpbp, ZIO_CHECKSUM_GANG_HEADER); BP_SET_BYTEORDER(&tmpbp, ZFS_HOST_BYTEORDER); ZIO_SET_CHECKSUM(&tmpbp.blk_cksum, DVA_GET_VDEV(dva), DVA_GET_OFFSET(dva), bp->blk_birth, 0); if (zio_checksum_verify(&tmpbp, (char *)zio_gb, SPA_GANGBLOCKSIZE)) { grub_printf("failed to checksum a gang block header\n"); return (1); } for (i = 0; i < SPA_GBH_NBLKPTRS; i++) { if (zio_gb->zg_blkptr[i].blk_birth == 0) continue; if (zio_read_data(&zio_gb->zg_blkptr[i], buf, stack)) return (1); buf += BP_GET_PSIZE(&zio_gb->zg_blkptr[i]); } return (0); } /* * Read in a block of raw data to buf. * * Return: * 0 - success * 1 - failure */ static int zio_read_data(blkptr_t *bp, void *buf, char *stack) { int i, psize; psize = BP_GET_PSIZE(bp); /* pick a good dva from the block pointer */ for (i = 0; i < SPA_DVAS_PER_BP; i++) { uint64_t offset, sector; if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0) continue; if (DVA_GET_GANG(&bp->blk_dva[i])) { if (zio_read_gang(bp, &bp->blk_dva[i], buf, stack) == 0) return (0); } else { /* read in a data block */ offset = DVA_GET_OFFSET(&bp->blk_dva[i]); sector = DVA_OFFSET_TO_PHYS_SECTOR(offset); if (devread(sector, 0, psize, buf)) return (0); } } return (1); } /* * Read in a block of data, verify its checksum, decompress if needed, * and put the uncompressed data in buf. * * Return: * 0 - success * errnum - failure */ static int zio_read(blkptr_t *bp, void *buf, char *stack) { int lsize, psize, comp; char *retbuf; comp = BP_GET_COMPRESS(bp); lsize = BP_GET_LSIZE(bp); psize = BP_GET_PSIZE(bp); if ((unsigned int)comp >= ZIO_COMPRESS_FUNCTIONS || (comp != ZIO_COMPRESS_OFF && decomp_table[comp].decomp_func == NULL)) { grub_printf("compression algorithm not supported\n"); return (ERR_FSYS_CORRUPT); } if ((char *)buf < stack && ((char *)buf) + lsize > stack) { grub_printf("not enough memory allocated\n"); return (ERR_WONT_FIT); } retbuf = buf; if (comp != ZIO_COMPRESS_OFF) { buf = stack; stack += psize; } if (zio_read_data(bp, buf, stack)) { grub_printf("zio_read_data failed\n"); return (ERR_FSYS_CORRUPT); } if (zio_checksum_verify(bp, buf, psize) != 0) { grub_printf("checksum verification failed\n"); return (ERR_FSYS_CORRUPT); } if (comp != ZIO_COMPRESS_OFF) decomp_table[comp].decomp_func(buf, retbuf, psize, lsize); return (0); } /* * Get the block from a block id. * push the block onto the stack. * * Return: * 0 - success * errnum - failure */ static int dmu_read(dnode_phys_t *dn, uint64_t blkid, void *buf, char *stack) { int idx, level; blkptr_t *bp_array = dn->dn_blkptr; int epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT; blkptr_t *bp, *tmpbuf; bp = (blkptr_t *)stack; stack += sizeof (blkptr_t); tmpbuf = (blkptr_t *)stack; stack += 1<dn_indblkshift; for (level = dn->dn_nlevels - 1; level >= 0; level--) { idx = (blkid >> (epbs * level)) & ((1<dn_datablkszsec << SPA_MINBLOCKSHIFT); break; } else if ((errnum = zio_read(bp, tmpbuf, stack))) { return (errnum); } bp_array = tmpbuf; } return (0); } /* * mzap_lookup: Looks up property described by "name" and returns the value * in "value". * * Return: * 0 - success * errnum - failure */ static int mzap_lookup(mzap_phys_t *zapobj, int objsize, char *name, uint64_t *value) { int i, chunks; mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; chunks = objsize/MZAP_ENT_LEN - 1; for (i = 0; i < chunks; i++) { if (grub_strcmp(mzap_ent[i].mze_name, name) == 0) { *value = mzap_ent[i].mze_value; return (0); } } return (ERR_FSYS_CORRUPT); } static uint64_t zap_hash(uint64_t salt, const char *name) { static uint64_t table[256]; const uint8_t *cp; uint8_t c; uint64_t crc = salt; if (table[128] == 0) { uint64_t *ct; int i, j; for (i = 0; i < 256; i++) { for (ct = table + i, *ct = i, j = 8; j > 0; j--) *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY); } } if (crc == 0 || table[128] != ZFS_CRC64_POLY) { errnum = ERR_FSYS_CORRUPT; return (0); } for (cp = (const uint8_t *)name; (c = *cp) != '\0'; cp++) crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF]; /* * Only use 28 bits, since we need 4 bits in the cookie for the * collision differentiator. We MUST use the high bits, since * those are the onces that we first pay attention to when * chosing the bucket. */ crc &= ~((1ULL << (64 - 28)) - 1); return (crc); } /* * Only to be used on 8-bit arrays. * array_len is actual len in bytes (not encoded le_value_length). * buf is null-terminated. */ static int zap_leaf_array_equal(zap_leaf_phys_t *l, int blksft, int chunk, int array_len, const char *buf) { int bseen = 0; while (bseen < array_len) { struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(l, blksft, chunk).l_array; int toread = MIN(array_len - bseen, ZAP_LEAF_ARRAY_BYTES); if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft)) return (0); if (zfs_bcmp(la->la_array, buf + bseen, toread) != 0) break; chunk = la->la_next; bseen += toread; } return (bseen == array_len); } /* * Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the * value for the property "name". * * Return: * 0 - success * errnum - failure */ static int zap_leaf_lookup(zap_leaf_phys_t *l, int blksft, uint64_t h, const char *name, uint64_t *value) { uint16_t chunk; struct zap_leaf_entry *le; /* Verify if this is a valid leaf block */ if (l->l_hdr.lh_block_type != ZBT_LEAF) return (ERR_FSYS_CORRUPT); if (l->l_hdr.lh_magic != ZAP_LEAF_MAGIC) return (ERR_FSYS_CORRUPT); for (chunk = l->l_hash[LEAF_HASH(blksft, h)]; chunk != CHAIN_END; chunk = le->le_next) { if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft)) return (ERR_FSYS_CORRUPT); le = ZAP_LEAF_ENTRY(l, blksft, chunk); /* Verify the chunk entry */ if (le->le_type != ZAP_CHUNK_ENTRY) return (ERR_FSYS_CORRUPT); if (le->le_hash != h) continue; if (zap_leaf_array_equal(l, blksft, le->le_name_chunk, le->le_name_length, name)) { struct zap_leaf_array *la; uint8_t *ip; if (le->le_int_size != 8 || le->le_value_length != 1) return (ERR_FSYS_CORRUPT); /* get the uint64_t property value */ la = &ZAP_LEAF_CHUNK(l, blksft, le->le_value_chunk).l_array; ip = la->la_array; *value = (uint64_t)ip[0] << 56 | (uint64_t)ip[1] << 48 | (uint64_t)ip[2] << 40 | (uint64_t)ip[3] << 32 | (uint64_t)ip[4] << 24 | (uint64_t)ip[5] << 16 | (uint64_t)ip[6] << 8 | (uint64_t)ip[7]; return (0); } } return (ERR_FSYS_CORRUPT); } /* * Fat ZAP lookup * * Return: * 0 - success * errnum - failure */ static int fzap_lookup(dnode_phys_t *zap_dnode, zap_phys_t *zap, char *name, uint64_t *value, char *stack) { zap_leaf_phys_t *l; uint64_t hash, idx, blkid; int blksft = zfs_log2(zap_dnode->dn_datablkszsec << DNODE_SHIFT); /* Verify if this is a fat zap header block */ if (zap->zap_magic != (uint64_t)ZAP_MAGIC || zap->zap_flags != 0) return (ERR_FSYS_CORRUPT); hash = zap_hash(zap->zap_salt, name); if (errnum) return (errnum); /* get block id from index */ if (zap->zap_ptrtbl.zt_numblks != 0) { /* external pointer tables not supported */ return (ERR_FSYS_CORRUPT); } idx = ZAP_HASH_IDX(hash, zap->zap_ptrtbl.zt_shift); blkid = ((uint64_t *)zap)[idx + (1<<(blksft-3-1))]; /* Get the leaf block */ l = (zap_leaf_phys_t *)stack; stack += 1<dn_datablkszsec << SPA_MINBLOCKSHIFT; stack += size; if ((errnum = dmu_read(zap_dnode, 0, zapbuf, stack))) return (errnum); block_type = *((uint64_t *)zapbuf); if (block_type == ZBT_MICRO) { return (mzap_lookup(zapbuf, size, name, val)); } else if (block_type == ZBT_HEADER) { /* this is a fat zap */ return (fzap_lookup(zap_dnode, zapbuf, name, val, stack)); } return (ERR_FSYS_CORRUPT); } /* * Get the dnode of an object number from the metadnode of an object set. * * Input * mdn - metadnode to get the object dnode * objnum - object number for the object dnode * buf - data buffer that holds the returning dnode * stack - scratch area * * Return: * 0 - success * errnum - failure */ static int dnode_get(dnode_phys_t *mdn, uint64_t objnum, uint8_t type, dnode_phys_t *buf, char *stack) { uint64_t blkid, blksz; /* the block id this object dnode is in */ int epbs; /* shift of number of dnodes in a block */ int idx; /* index within a block */ dnode_phys_t *dnbuf; blksz = mdn->dn_datablkszsec << SPA_MINBLOCKSHIFT; epbs = zfs_log2(blksz) - DNODE_SHIFT; blkid = objnum >> epbs; idx = objnum & ((1<= dnode_start && objnum < dnode_end) { grub_memmove(buf, &dnode_buf[idx], DNODE_SIZE); VERIFY_DN_TYPE(buf, type); return (0); } if (dnode_buf && blksz == 1< ZPL_VERSION) return (-1); if ((errnum = zap_lookup(dn, ZFS_ROOT_OBJ, &objnum, stack))) return (errnum); if ((errnum = dnode_get(mdn, objnum, DMU_OT_DIRECTORY_CONTENTS, dn, stack))) return (errnum); /* skip leading slashes */ while (*path == '/') path++; while (*path && !isspace((uint8_t)*path)) { /* get the next component name */ cname = path; while (*path && !isspace((uint8_t)*path) && *path != '/') path++; ch = *path; *path = 0; /* ensure null termination */ if ((errnum = zap_lookup(dn, cname, &objnum, stack))) return (errnum); objnum = ZFS_DIRENT_OBJ(objnum); if ((errnum = dnode_get(mdn, objnum, 0, dn, stack))) return (errnum); *path = ch; while (*path == '/') path++; } /* We found the dnode for this file. Verify if it is a plain file. */ VERIFY_DN_TYPE(dn, DMU_OT_PLAIN_FILE_CONTENTS); return (0); } /* * Get the default 'bootfs' property value from the rootpool. * * Return: * 0 - success * errnum -failure */ static int get_default_bootfsobj(dnode_phys_t *mosmdn, uint64_t *obj, char *stack) { uint64_t objnum = 0; dnode_phys_t *dn = (dnode_phys_t *)stack; stack += DNODE_SIZE; if ((errnum = dnode_get(mosmdn, DMU_POOL_DIRECTORY_OBJECT, DMU_OT_OBJECT_DIRECTORY, dn, stack))) return (errnum); /* * find the object number for 'pool_props', and get the dnode * of the 'pool_props'. */ if (zap_lookup(dn, DMU_POOL_PROPS, &objnum, stack)) return (ERR_FILESYSTEM_NOT_FOUND); if ((errnum = dnode_get(mosmdn, objnum, DMU_OT_POOL_PROPS, dn, stack))) return (errnum); if (zap_lookup(dn, ZPOOL_PROP_BOOTFS, &objnum, stack)) return (ERR_FILESYSTEM_NOT_FOUND); if (!objnum) return (ERR_FILESYSTEM_NOT_FOUND); *obj = objnum; return (0); } /* * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname), * e.g. pool/rootfs, or a given object number (obj), e.g. the object number * of pool/rootfs. * * If no fsname and no obj are given, return the DSL_DIR metadnode. * If fsname is given, return its metadnode and its matching object number. * If only obj is given, return the metadnode for this object number. * * Return: * 0 - success * errnum - failure */ static int get_objset_mdn(dnode_phys_t *mosmdn, char *fsname, uint64_t *obj, dnode_phys_t *mdn, char *stack) { uint64_t objnum, headobj; char *cname, ch; blkptr_t *bp; objset_phys_t *osp; int issnapshot = 0; char *snapname = NULL; if (fsname == NULL && obj) { headobj = *obj; goto skip; } if ((errnum = dnode_get(mosmdn, DMU_POOL_DIRECTORY_OBJECT, DMU_OT_OBJECT_DIRECTORY, mdn, stack))) return (errnum); if ((errnum = zap_lookup(mdn, DMU_POOL_ROOT_DATASET, &objnum, stack))) return (errnum); if ((errnum = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, stack))) return (errnum); if (fsname == NULL) { headobj = ((dsl_dir_phys_t *)DN_BONUS(mdn))->dd_head_dataset_obj; goto skip; } /* take out the pool name */ while (*fsname && !isspace((uint8_t)*fsname) && *fsname != '/') fsname++; while (*fsname && !isspace((uint8_t)*fsname)) { uint64_t childobj; while (*fsname == '/') fsname++; cname = fsname; while (*fsname && !isspace((uint8_t)*fsname) && *fsname != '/') fsname++; ch = *fsname; *fsname = 0; snapname = cname; while (*snapname && !isspace((uint8_t)*snapname) && *snapname != '@') snapname++; if (*snapname == '@') { issnapshot = 1; *snapname = 0; } childobj = ((dsl_dir_phys_t *)DN_BONUS(mdn))->dd_child_dir_zapobj; if ((errnum = dnode_get(mosmdn, childobj, DMU_OT_DSL_DIR_CHILD_MAP, mdn, stack))) return (errnum); if (zap_lookup(mdn, cname, &objnum, stack)) return (ERR_FILESYSTEM_NOT_FOUND); if ((errnum = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, stack))) return (errnum); *fsname = ch; if (issnapshot) *snapname = '@'; } headobj = ((dsl_dir_phys_t *)DN_BONUS(mdn))->dd_head_dataset_obj; if (obj) *obj = headobj; skip: if ((errnum = dnode_get(mosmdn, headobj, DMU_OT_DSL_DATASET, mdn, stack))) return (errnum); if (issnapshot) { uint64_t snapobj; snapobj = ((dsl_dataset_phys_t *)DN_BONUS(mdn))-> ds_snapnames_zapobj; if ((errnum = dnode_get(mosmdn, snapobj, DMU_OT_DSL_DS_SNAP_MAP, mdn, stack))) return (errnum); if (zap_lookup(mdn, snapname + 1, &headobj, stack)) return (ERR_FILESYSTEM_NOT_FOUND); if ((errnum = dnode_get(mosmdn, headobj, DMU_OT_DSL_DATASET, mdn, stack))) return (errnum); if (obj) *obj = headobj; } bp = &((dsl_dataset_phys_t *)DN_BONUS(mdn))->ds_bp; osp = (objset_phys_t *)stack; stack += sizeof (objset_phys_t); if ((errnum = zio_read(bp, osp, stack))) return (errnum); grub_memmove((char *)mdn, (char *)&osp->os_meta_dnode, DNODE_SIZE); return (0); } /* * For a given XDR packed nvlist, verify the first 4 bytes and move on. * * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) : * * encoding method/host endian (4 bytes) * nvl_version (4 bytes) * nvl_nvflag (4 bytes) * encoded nvpairs: * encoded size of the nvpair (4 bytes) * decoded size of the nvpair (4 bytes) * name string size (4 bytes) * name string data (sizeof(NV_ALIGN4(string)) * data type (4 bytes) * # of elements in the nvpair (4 bytes) * data * 2 zero's for the last nvpair * (end of the entire list) (8 bytes) * * Return: * 0 - success * 1 - failure */ static int nvlist_unpack(char *nvlist, char **out) { /* Verify if the 1st and 2nd byte in the nvlist are valid. */ if (nvlist[0] != NV_ENCODE_XDR || nvlist[1] != HOST_ENDIAN) return (1); nvlist += 4; *out = nvlist; return (0); } static char * nvlist_array(char *nvlist, int index) { int i, encode_size; for (i = 0; i < index; i++) { /* skip the header, nvl_version, and nvl_nvflag */ nvlist = nvlist + 4 * 2; while ((encode_size = BSWAP_32(*(uint32_t *)nvlist))) nvlist += encode_size; /* goto the next nvpair */ nvlist = nvlist + 4 * 2; /* skip the ending 2 zeros - 8 bytes */ } return (nvlist); } static int nvlist_lookup_value(char *nvlist, char *name, void *val, int valtype, int *nelmp) { int name_len, type, slen, encode_size; char *nvpair, *nvp_name, *strval = val; uint64_t *intval = val; /* skip the header, nvl_version, and nvl_nvflag */ nvlist = nvlist + 4 * 2; /* * Loop thru the nvpair list * The XDR representation of an integer is in big-endian byte order. */ while ((encode_size = BSWAP_32(*(uint32_t *)nvlist))) { nvpair = nvlist + 4 * 2; /* skip the encode/decode size */ name_len = BSWAP_32(*(uint32_t *)nvpair); nvpair += 4; nvp_name = nvpair; nvpair = nvpair + ((name_len + 3) & ~3); /* align */ type = BSWAP_32(*(uint32_t *)nvpair); nvpair += 4; if ((grub_strncmp(nvp_name, name, name_len) == 0) && type == valtype) { int nelm; if ((nelm = BSWAP_32(*(uint32_t *)nvpair)) < 1) return (1); nvpair += 4; switch (valtype) { case DATA_TYPE_STRING: slen = BSWAP_32(*(uint32_t *)nvpair); nvpair += 4; grub_memmove(strval, nvpair, slen); strval[slen] = '\0'; return (0); case DATA_TYPE_UINT64: *intval = BSWAP_64(*(uint64_t *)nvpair); return (0); case DATA_TYPE_NVLIST: *(void **)val = (void *)nvpair; return (0); case DATA_TYPE_NVLIST_ARRAY: *(void **)val = (void *)nvpair; if (nelmp) *nelmp = nelm; return (0); } } nvlist += encode_size; /* goto the next nvpair */ } return (1); } /* * Check if this vdev is online and is in a good state. */ static int vdev_validate(char *nv) { uint64_t ival; if (nvlist_lookup_value(nv, ZPOOL_CONFIG_OFFLINE, &ival, DATA_TYPE_UINT64, NULL) == 0 || nvlist_lookup_value(nv, ZPOOL_CONFIG_FAULTED, &ival, DATA_TYPE_UINT64, NULL) == 0 || nvlist_lookup_value(nv, ZPOOL_CONFIG_REMOVED, &ival, DATA_TYPE_UINT64, NULL) == 0) return (ERR_DEV_VALUES); return (0); } /* * Get a valid vdev pathname/devid from the boot device. * The caller should already allocate MAXPATHLEN memory for bootpath and devid. */ static int vdev_get_bootpath(char *nv, uint64_t inguid, char *devid, char *bootpath, int is_spare) { char type[16]; if (nvlist_lookup_value(nv, ZPOOL_CONFIG_TYPE, &type, DATA_TYPE_STRING, NULL)) return (ERR_FSYS_CORRUPT); if (strcmp(type, VDEV_TYPE_DISK) == 0) { uint64_t guid; if (vdev_validate(nv) != 0) return (ERR_NO_BOOTPATH); if (nvlist_lookup_value(nv, ZPOOL_CONFIG_GUID, &guid, DATA_TYPE_UINT64, NULL) != 0) return (ERR_NO_BOOTPATH); if (guid != inguid) return (ERR_NO_BOOTPATH); /* for a spare vdev, pick the disk labeled with "is_spare" */ if (is_spare) { uint64_t spare = 0; (void) nvlist_lookup_value(nv, ZPOOL_CONFIG_IS_SPARE, &spare, DATA_TYPE_UINT64, NULL); if (!spare) return (ERR_NO_BOOTPATH); } if (nvlist_lookup_value(nv, ZPOOL_CONFIG_PHYS_PATH, bootpath, DATA_TYPE_STRING, NULL) != 0) bootpath[0] = '\0'; if (nvlist_lookup_value(nv, ZPOOL_CONFIG_DEVID, devid, DATA_TYPE_STRING, NULL) != 0) devid[0] = '\0'; if (strlen(bootpath) >= MAXPATHLEN || strlen(devid) >= MAXPATHLEN) return (ERR_WONT_FIT); return (0); } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0 || strcmp(type, VDEV_TYPE_REPLACING) == 0 || (is_spare = (strcmp(type, VDEV_TYPE_SPARE) == 0))) { int nelm, i; char *child; if (nvlist_lookup_value(nv, ZPOOL_CONFIG_CHILDREN, &child, DATA_TYPE_NVLIST_ARRAY, &nelm)) return (ERR_FSYS_CORRUPT); for (i = 0; i < nelm; i++) { char *child_i; child_i = nvlist_array(child, i); if (vdev_get_bootpath(child_i, inguid, devid, bootpath, is_spare) == 0) return (0); } } return (ERR_NO_BOOTPATH); } /* * Check the disk label information and retrieve needed vdev name-value pairs. * * Return: * 0 - success * ERR_* - failure */ int check_pool_label(uint64_t sector, char *stack, char *outdevid, char *outpath, uint64_t *outguid) { vdev_phys_t *vdev; uint64_t pool_state, txg = 0; char *nvlist, *nv; uint64_t diskguid; uint64_t version; sector += (VDEV_SKIP_SIZE >> SPA_MINBLOCKSHIFT); /* Read in the vdev name-value pair list (112K). */ if (devread(sector, 0, VDEV_PHYS_SIZE, stack) == 0) return (ERR_READ); vdev = (vdev_phys_t *)stack; stack += sizeof (vdev_phys_t); if (nvlist_unpack(vdev->vp_nvlist, &nvlist)) return (ERR_FSYS_CORRUPT); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_POOL_STATE, &pool_state, DATA_TYPE_UINT64, NULL)) return (ERR_FSYS_CORRUPT); if (pool_state == POOL_STATE_DESTROYED) return (ERR_FILESYSTEM_NOT_FOUND); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_POOL_NAME, current_rootpool, DATA_TYPE_STRING, NULL)) return (ERR_FSYS_CORRUPT); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_POOL_TXG, &txg, DATA_TYPE_UINT64, NULL)) return (ERR_FSYS_CORRUPT); /* not an active device */ if (txg == 0) return (ERR_NO_BOOTPATH); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_VERSION, &version, DATA_TYPE_UINT64, NULL)) return (ERR_FSYS_CORRUPT); if (version > SPA_VERSION) return (ERR_NEWER_VERSION); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_VDEV_TREE, &nv, DATA_TYPE_NVLIST, NULL)) return (ERR_FSYS_CORRUPT); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_GUID, &diskguid, DATA_TYPE_UINT64, NULL)) return (ERR_FSYS_CORRUPT); if (vdev_get_bootpath(nv, diskguid, outdevid, outpath, 0)) return (ERR_NO_BOOTPATH); if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_POOL_GUID, outguid, DATA_TYPE_UINT64, NULL)) return (ERR_FSYS_CORRUPT); return (0); } /* * zfs_mount() locates a valid uberblock of the root pool and read in its MOS * to the memory address MOS. * * Return: * 1 - success * 0 - failure */ int zfs_mount(void) { char *stack; int label = 0; uberblock_phys_t *ub_array, *ubbest; objset_phys_t *osp; char tmp_bootpath[MAXNAMELEN]; char tmp_devid[MAXNAMELEN]; uint64_t tmp_guid; uint64_t adjpl = (uint64_t)part_length << SPA_MINBLOCKSHIFT; int err = errnum; /* preserve previous errnum state */ /* if it's our first time here, zero the best uberblock out */ if (best_drive == 0 && best_part == 0 && find_best_root) { grub_memset(¤t_uberblock, 0, sizeof (uberblock_t)); pool_guid = 0; } stackbase = ZFS_SCRATCH; stack = stackbase; ub_array = (uberblock_phys_t *)stack; stack += VDEV_UBERBLOCK_RING; osp = (objset_phys_t *)stack; stack += sizeof (objset_phys_t); adjpl = P2ALIGN(adjpl, (uint64_t)sizeof (vdev_label_t)); for (label = 0; label < VDEV_LABELS; label++) { uint64_t sector; /* * some eltorito stacks don't give us a size and * we end up setting the size to MAXUINT, further * some of these devices stop working once a single * read past the end has been issued. Checking * for a maximum part_length and skipping the backup * labels at the end of the slice/partition/device * avoids breaking down on such devices. */ if (part_length == MAXUINT && label == 2) break; sector = vdev_label_start(adjpl, label) >> SPA_MINBLOCKSHIFT; /* Read in the uberblock ring (128K). */ if (devread(sector + ((VDEV_SKIP_SIZE + VDEV_PHYS_SIZE) >> SPA_MINBLOCKSHIFT), 0, VDEV_UBERBLOCK_RING, (char *)ub_array) == 0) continue; if ((ubbest = find_bestub(ub_array, sector)) != NULL && zio_read(&ubbest->ubp_uberblock.ub_rootbp, osp, stack) == 0) { VERIFY_OS_TYPE(osp, DMU_OST_META); if (check_pool_label(sector, stack, tmp_devid, tmp_bootpath, &tmp_guid)) continue; if (pool_guid == 0) pool_guid = tmp_guid; if (find_best_root && ((pool_guid != tmp_guid) || vdev_uberblock_compare(&ubbest->ubp_uberblock, &(current_uberblock)) <= 0)) continue; /* Got the MOS. Save it at the memory addr MOS. */ grub_memmove(MOS, &osp->os_meta_dnode, DNODE_SIZE); grub_memmove(¤t_uberblock, &ubbest->ubp_uberblock, sizeof (uberblock_t)); grub_memmove(current_bootpath, tmp_bootpath, MAXNAMELEN); grub_memmove(current_devid, tmp_devid, grub_strlen(tmp_devid)); is_zfs_mount = 1; return (1); } } /* * While some fs impls. (tftp) rely on setting and keeping * global errnums set, others won't reset it and will break * when issuing rawreads. The goal here is to simply not * have zfs mount attempts impact the previous state. */ errnum = err; return (0); } /* * zfs_open() locates a file in the rootpool by following the * MOS and places the dnode of the file in the memory address DNODE. * * Return: * 1 - success * 0 - failure */ int zfs_open(char *filename) { char *stack; dnode_phys_t *mdn; file_buf = NULL; stackbase = ZFS_SCRATCH; stack = stackbase; mdn = (dnode_phys_t *)stack; stack += sizeof (dnode_phys_t); dnode_mdn = NULL; dnode_buf = (dnode_phys_t *)stack; stack += 1<dn_bonustype == DMU_OT_SA) { sa_hdr_phys_t *sahdrp; int hdrsize; if (DNODE->dn_bonuslen != 0) { sahdrp = (sa_hdr_phys_t *)DN_BONUS(DNODE); } else { if (DNODE->dn_flags & DNODE_FLAG_SPILL_BLKPTR) { blkptr_t *bp = &DNODE->dn_spill; void *buf; buf = (void *)stack; stack += BP_GET_LSIZE(bp); /* reset errnum to rawread() failure */ errnum = 0; if (zio_read(bp, buf, stack) != 0) { return (0); } sahdrp = buf; } else { errnum = ERR_FSYS_CORRUPT; return (0); } } hdrsize = SA_HDR_SIZE(sahdrp); filemax = *(uint64_t *)((char *)sahdrp + hdrsize + SA_SIZE_OFFSET); } else { filemax = ((znode_phys_t *)DN_BONUS(DNODE))->zp_size; } filepos = 0; dnode_buf = NULL; return (1); } /* * zfs_read reads in the data blocks pointed by the DNODE. * * Return: * len - the length successfully read in to the buffer * 0 - failure */ int zfs_read(char *buf, int len) { char *stack; int blksz, length, movesize; if (file_buf == NULL) { file_buf = stackbase; stackbase += SPA_MAXBLOCKSIZE; file_start = file_end = 0; } stack = stackbase; /* * If offset is in memory, move it into the buffer provided and return. */ if (filepos >= file_start && filepos+len <= file_end) { grub_memmove(buf, file_buf + filepos - file_start, len); filepos += len; return (len); } blksz = DNODE->dn_datablkszsec << SPA_MINBLOCKSHIFT; /* * Entire Dnode is too big to fit into the space available. We * will need to read it in chunks. This could be optimized to * read in as large a chunk as there is space available, but for * now, this only reads in one data block at a time. */ length = len; while (length) { /* * Find requested blkid and the offset within that block. */ uint64_t blkid = filepos / blksz; if ((errnum = dmu_read(DNODE, blkid, file_buf, stack))) return (0); file_start = blkid * blksz; file_end = file_start + blksz; movesize = MIN(length, file_end - filepos); grub_memmove(buf, file_buf + filepos - file_start, movesize); buf += movesize; length -= movesize; filepos += movesize; } return (len); } /* * No-Op */ int zfs_embed(int *start_sector, int needed_sectors) { return (1); } #endif /* FSYS_ZFS */ xen-4.9.2/tools/libfsimage/reiserfs/0000775000175000017500000000000013256712137015541 5ustar smbsmbxen-4.9.2/tools/libfsimage/reiserfs/fsys_reiserfs.c0000664000175000017500000011232113256712137020573 0ustar smbsmb/* fsys_reiserfs.c - an implementation for the ReiserFS filesystem */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2000, 2001 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #include #undef REISERDEBUG /* Some parts of this code (mainly the structures and defines) are * from the original reiser fs code, as found in the linux kernel. */ /* include/asm-i386/types.h */ typedef __signed__ char __s8; typedef unsigned char __u8; typedef __signed__ short __s16; typedef unsigned short __u16; typedef __signed__ int __s32; typedef unsigned int __u32; typedef unsigned long long __u64; /* linux/posix_type.h */ typedef long linux_off_t; /* linux/little_endian.h */ #define __cpu_to_le64(x) ((__u64) (x)) #define __le64_to_cpu(x) ((__u64) (x)) #define __cpu_to_le32(x) ((__u32) (x)) #define __le32_to_cpu(x) ((__u32) (x)) #define __cpu_to_le16(x) ((__u16) (x)) #define __le16_to_cpu(x) ((__u16) (x)) /* include/linux/reiser_fs.h */ /* This is the new super block of a journaling reiserfs system */ struct reiserfs_super_block { __u32 s_block_count; /* blocks count */ __u32 s_free_blocks; /* free blocks count */ __u32 s_root_block; /* root block number */ __u32 s_journal_block; /* journal block number */ __u32 s_journal_dev; /* journal device number */ __u32 s_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */ __u32 s_journal_trans_max; /* max number of blocks in a transaction. */ __u32 s_journal_magic; /* random value made on fs creation */ __u32 s_journal_max_batch; /* max number of blocks to batch into a trans */ __u32 s_journal_max_commit_age; /* in seconds, how old can an async commit be */ __u32 s_journal_max_trans_age; /* in seconds, how old can a transaction be */ __u16 s_blocksize; /* block size */ __u16 s_oid_maxsize; /* max size of object id array */ __u16 s_oid_cursize; /* current size of object id array */ __u16 s_state; /* valid or error */ char s_magic[16]; /* reiserfs magic string indicates that file system is reiserfs */ __u16 s_tree_height; /* height of disk tree */ __u16 s_bmap_nr; /* amount of bitmap blocks needed to address each block of file system */ __u16 s_version; char s_unused[128]; /* zero filled by mkreiserfs */ }; #define REISERFS_MAX_SUPPORTED_VERSION 2 #define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER3FS_SUPER_MAGIC_STRING "ReIsEr3Fs" #define MAX_HEIGHT 7 /* must be correct to keep the desc and commit structs at 4k */ #define JOURNAL_TRANS_HALF 1018 /* first block written in a commit. */ struct reiserfs_journal_desc { __u32 j_trans_id; /* id of commit */ __u32 j_len; /* length of commit. len +1 is the commit block */ __u32 j_mount_id; /* mount id of this trans*/ __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the first blocks */ char j_magic[12]; }; /* last block written in a commit */ struct reiserfs_journal_commit { __u32 j_trans_id; /* must match j_trans_id from the desc block */ __u32 j_len; /* ditto */ __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the last blocks */ char j_digest[16]; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */ }; /* this header block gets written whenever a transaction is considered fully flushed, and is more recent than the last fully flushed transaction. fully flushed means all the log blocks and all the real blocks are on disk, and this transaction does not need to be replayed. */ struct reiserfs_journal_header { /* id of last fully flushed transaction */ __u32 j_last_flush_trans_id; /* offset in the log of where to start replay after a crash */ __u32 j_first_unflushed_offset; /* mount id to detect very old transactions */ __u32 j_mount_id; }; /* magic string to find desc blocks in the journal */ #define JOURNAL_DESC_MAGIC "ReIsErLB" /* * directories use this key as well as old files */ struct offset_v1 { /* * for regular files this is the offset to the first byte of the * body, contained in the object-item, as measured from the start of * the entire body of the object. * * for directory entries, k_offset consists of hash derived from * hashing the name and using few bits (23 or more) of the resulting * hash, and generation number that allows distinguishing names with * hash collisions. If number of collisions overflows generation * number, we return EEXIST. High order bit is 0 always */ __u32 k_offset; __u32 k_uniqueness; }; struct offset_v2 { /* * for regular files this is the offset to the first byte of the * body, contained in the object-item, as measured from the start of * the entire body of the object. * * for directory entries, k_offset consists of hash derived from * hashing the name and using few bits (23 or more) of the resulting * hash, and generation number that allows distinguishing names with * hash collisions. If number of collisions overflows generation * number, we return EEXIST. High order bit is 0 always */ __u64 k_offset:60; __u64 k_type: 4; }; struct key { /* packing locality: by default parent directory object id */ __u32 k_dir_id; /* object identifier */ __u32 k_objectid; /* the offset and node type (old and new form) */ union { struct offset_v1 v1; struct offset_v2 v2; } u; }; #define KEY_SIZE (sizeof (struct key)) /* Header of a disk block. More precisely, header of a formatted leaf or internal node, and not the header of an unformatted node. */ struct block_head { __u16 blk_level; /* Level of a block in the tree. */ __u16 blk_nr_item; /* Number of keys/items in a block. */ __u16 blk_free_space; /* Block free space in bytes. */ struct key blk_right_delim_key; /* Right delimiting key for this block (supported for leaf level nodes only) */ }; #define BLKH_SIZE (sizeof (struct block_head)) #define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */ struct item_head { struct key ih_key; /* Everything in the tree is found by searching for it based on its key.*/ union { __u16 ih_free_space; /* The free space in the last unformatted node of an indirect item if this is an indirect item. This equals 0xFFFF iff this is a direct item or stat data item. Note that the key, not this field, is used to determine the item type, and thus which field this union contains. */ __u16 ih_entry_count; /* Iff this is a directory item, this field equals the number of directory entries in the directory item. */ } u; __u16 ih_item_len; /* total size of the item body */ __u16 ih_item_location; /* an offset to the item body within the block */ __u16 ih_version; /* ITEM_VERSION_1 for all old items, ITEM_VERSION_2 for new ones. Highest bit is set by fsck temporary, cleaned after all done */ }; /* size of item header */ #define IH_SIZE (sizeof (struct item_head)) #define ITEM_VERSION_1 0 #define ITEM_VERSION_2 1 #define IH_KEY_OFFSET(ih) ((ih)->ih_version == ITEM_VERSION_1 \ ? (ih)->ih_key.u.v1.k_offset \ : (ih)->ih_key.u.v2.k_offset) #define IH_KEY_ISTYPE(ih, type) ((ih)->ih_version == ITEM_VERSION_1 \ ? (ih)->ih_key.u.v1.k_uniqueness == V1_##type \ : (ih)->ih_key.u.v2.k_type == V2_##type) struct disk_child { __u32 dc_block_number; /* Disk child's block number. */ __u16 dc_size; /* Disk child's used space. */ }; #define DC_SIZE (sizeof (struct disk_child)) /* Stat Data on disk. * * Note that reiserfs has two different forms of stat data. Luckily * the fields needed by grub are at the same position. */ struct stat_data { __u16 sd_mode; /* file type, permissions */ __u16 sd_notused1[3]; /* fields not needed by reiserfs */ __u32 sd_size; /* file size */ __u32 sd_size_hi; /* file size high 32 bits (since version 2) */ }; struct reiserfs_de_head { __u32 deh_offset; /* third component of the directory entry key */ __u32 deh_dir_id; /* objectid of the parent directory of the object, that is referenced by directory entry */ __u32 deh_objectid;/* objectid of the object, that is referenced by directory entry */ __u16 deh_location;/* offset of name in the whole item */ __u16 deh_state; /* whether 1) entry contains stat data (for future), and 2) whether entry is hidden (unlinked) */ }; #define DEH_SIZE (sizeof (struct reiserfs_de_head)) #define DEH_Statdata (1 << 0) /* not used now */ #define DEH_Visible (1 << 2) #define SD_OFFSET 0 #define SD_UNIQUENESS 0 #define DOT_OFFSET 1 #define DOT_DOT_OFFSET 2 #define DIRENTRY_UNIQUENESS 500 #define V1_TYPE_STAT_DATA 0x0 #define V1_TYPE_DIRECT 0xffffffff #define V1_TYPE_INDIRECT 0xfffffffe #define V1_TYPE_DIRECTORY_MAX 0xfffffffd #define V2_TYPE_STAT_DATA 0 #define V2_TYPE_INDIRECT 1 #define V2_TYPE_DIRECT 2 #define V2_TYPE_DIRENTRY 3 #define REISERFS_ROOT_OBJECTID 2 #define REISERFS_ROOT_PARENT_OBJECTID 1 #define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) /* the spot for the super in versions 3.5 - 3.5.11 (inclusive) */ #define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024) #define REISERFS_OLD_BLOCKSIZE 4096 #define S_ISREG(mode) (((mode) & 0170000) == 0100000) #define S_ISDIR(mode) (((mode) & 0170000) == 0040000) #define S_ISLNK(mode) (((mode) & 0170000) == 0120000) #define PATH_MAX 1024 /* include/linux/limits.h */ #define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ /* The size of the node cache */ #define FSYSREISER_CACHE_SIZE 24*1024 #define FSYSREISER_MIN_BLOCKSIZE SECTOR_SIZE #define FSYSREISER_MAX_BLOCKSIZE FSYSREISER_CACHE_SIZE / 3 /* Info about currently opened file */ struct fsys_reiser_fileinfo { __u32 k_dir_id; __u32 k_objectid; }; /* In memory info about the currently mounted filesystem */ struct fsys_reiser_info { /* The last read item head */ struct item_head *current_ih; /* The last read item */ char *current_item; /* The information for the currently opened file */ struct fsys_reiser_fileinfo fileinfo; /* The start of the journal */ __u32 journal_block; /* The size of the journal */ __u32 journal_block_count; /* The first valid descriptor block in journal (relative to journal_block) */ __u32 journal_first_desc; /* The ReiserFS version. */ __u16 version; /* The current depth of the reiser tree. */ __u16 tree_depth; /* SECTOR_SIZE << blocksize_shift == blocksize. */ __u8 blocksize_shift; /* 1 << full_blocksize_shift == blocksize. */ __u8 fullblocksize_shift; /* The reiserfs block size (must be a power of 2) */ __u16 blocksize; /* The number of cached tree nodes */ __u16 cached_slots; /* The number of valid transactions in journal */ __u16 journal_transactions; unsigned int blocks[MAX_HEIGHT]; unsigned int next_key_nr[MAX_HEIGHT]; }; /* The cached s+tree blocks in FSYS_BUF, see below * for a more detailed description. */ #define ROOT ((char *) FSYS_BUF) #define CACHE(i) (ROOT + ((i) << INFO->fullblocksize_shift)) #define LEAF CACHE (DISK_LEAF_NODE_LEVEL) #define BLOCKHEAD(cache) ((struct block_head *) cache) #define ITEMHEAD ((struct item_head *) ((char *) LEAF + BLKH_SIZE)) #define KEY(cache) ((struct key *) ((char *) cache + BLKH_SIZE)) #define DC(cache) ((struct disk_child *) \ ((char *) cache + BLKH_SIZE + KEY_SIZE * nr_item)) /* The fsys_reiser_info block. */ #define INFO \ ((struct fsys_reiser_info *) ((char *) FSYS_BUF + FSYSREISER_CACHE_SIZE)) /* * The journal cache. For each transaction it contains the number of * blocks followed by the real block numbers of this transaction. * * If the block numbers of some transaction won't fit in this space, * this list is stopped with a 0xffffffff marker and the remaining * uncommitted transactions aren't cached. */ #define JOURNAL_START ((__u32 *) (INFO + 1)) #define JOURNAL_END ((__u32 *) (FSYS_BUF + FSYS_BUFLEN)) #define log2 grub_log2 static __inline__ int is_power_of_two (unsigned long word) { return (word & -word) == word; } static int journal_read (fsi_file_t *ffi, int block, int len, char *buffer) { return devread (ffi, (INFO->journal_block + block) << INFO->blocksize_shift, 0, len, buffer); } /* Read a block from ReiserFS file system, taking the journal into * account. If the block nr is in the journal, the block from the * journal taken. */ static int block_read (fsi_file_t *ffi, int blockNr, int start, int len, char *buffer) { int transactions = INFO->journal_transactions; int desc_block = INFO->journal_first_desc; int journal_mask = INFO->journal_block_count - 1; int translatedNr = blockNr; __u32 *journal_table = JOURNAL_START; while (transactions-- > 0) { int i = 0; int j_len; if (*journal_table != 0xffffffff) { /* Search for the blockNr in cached journal */ j_len = *journal_table++; while (i++ < j_len) { if (*journal_table++ == blockNr) { journal_table += j_len - i; goto found; } } } else { /* This is the end of cached journal marker. The remaining * transactions are still on disk. */ struct reiserfs_journal_desc desc; struct reiserfs_journal_commit commit; if (! journal_read (ffi, desc_block, sizeof (desc), (char *) &desc)) return 0; j_len = desc.j_len; while (i < j_len && i < JOURNAL_TRANS_HALF) if (desc.j_realblock[i++] == blockNr) goto found; if (j_len >= JOURNAL_TRANS_HALF) { int commit_block = (desc_block + 1 + j_len) & journal_mask; if (! journal_read (ffi, commit_block, sizeof (commit), (char *) &commit)) return 0; while (i < j_len) if (commit.j_realblock[i++ - JOURNAL_TRANS_HALF] == blockNr) goto found; } } goto not_found; found: translatedNr = INFO->journal_block + ((desc_block + i) & journal_mask); #ifdef REISERDEBUG printf ("block_read: block %d is mapped to journal block %d.\n", blockNr, translatedNr - INFO->journal_block); #endif /* We must continue the search, as this block may be overwritten * in later transactions. */ not_found: desc_block = (desc_block + 2 + j_len) & journal_mask; } return devread (ffi, translatedNr << INFO->blocksize_shift, start, len, buffer); } /* Init the journal data structure. We try to cache as much as * possible in the JOURNAL_START-JOURNAL_END space, but if it is full * we can still read the rest from the disk on demand. * * The first number of valid transactions and the descriptor block of the * first valid transaction are held in INFO. The transactions are all * adjacent, but we must take care of the journal wrap around. */ static int journal_init (fsi_file_t *ffi) { unsigned int block_count = INFO->journal_block_count; unsigned int desc_block; unsigned int commit_block; unsigned int next_trans_id; struct reiserfs_journal_header header; struct reiserfs_journal_desc desc; struct reiserfs_journal_commit commit; __u32 *journal_table = JOURNAL_START; journal_read (ffi, block_count, sizeof (header), (char *) &header); desc_block = header.j_first_unflushed_offset; if (desc_block >= block_count) return 0; INFO->journal_first_desc = desc_block; next_trans_id = header.j_last_flush_trans_id + 1; #ifdef REISERDEBUG printf ("journal_init: last flushed %d\n", header.j_last_flush_trans_id); #endif while (1) { journal_read (ffi, desc_block, sizeof (desc), (char *) &desc); if (substring (JOURNAL_DESC_MAGIC, desc.j_magic) || desc.j_trans_id != next_trans_id || desc.j_mount_id != header.j_mount_id) /* no more valid transactions */ break; commit_block = (desc_block + desc.j_len + 1) & (block_count - 1); journal_read (ffi, commit_block, sizeof (commit), (char *) &commit); if (desc.j_trans_id != commit.j_trans_id || desc.j_len != commit.j_len) /* no more valid transactions */ break; #ifdef REISERDEBUG printf ("Found valid transaction %d/%d at %d.\n", desc.j_trans_id, desc.j_mount_id, desc_block); #endif next_trans_id++; if (journal_table < JOURNAL_END) { if ((journal_table + 1 + desc.j_len) >= JOURNAL_END) { /* The table is almost full; mark the end of the cached * journal.*/ *journal_table = 0xffffffff; journal_table = JOURNAL_END; } else { int i; /* Cache the length and the realblock numbers in the table. * The block number of descriptor can easily be computed. * and need not to be stored here. */ *journal_table++ = desc.j_len; for (i = 0; i < desc.j_len && i < JOURNAL_TRANS_HALF; i++) { *journal_table++ = desc.j_realblock[i]; #ifdef REISERDEBUG printf ("block %d is in journal %d.\n", desc.j_realblock[i], desc_block); #endif } for ( ; i < desc.j_len; i++) { *journal_table++ = commit.j_realblock[i-JOURNAL_TRANS_HALF]; #ifdef REISERDEBUG printf ("block %d is in journal %d.\n", commit.j_realblock[i-JOURNAL_TRANS_HALF], desc_block); #endif } } } desc_block = (commit_block + 1) & (block_count - 1); } #ifdef REISERDEBUG printf ("Transaction %d/%d at %d isn't valid.\n", desc.j_trans_id, desc.j_mount_id, desc_block); #endif INFO->journal_transactions = next_trans_id - header.j_last_flush_trans_id - 1; return errnum == 0; } /* check filesystem types and read superblock into memory buffer */ static int reiserfs_mount (fsi_file_t *ffi, const char *options) { struct reiserfs_super_block super; int superblock = REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; if (/*part_length < superblock + (sizeof (super) >> SECTOR_BITS) || */ !devread (ffi, superblock, 0, sizeof (struct reiserfs_super_block), (char *) &super) || (substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) > 0 && substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) || (/* check that this is not a copy inside the journal log */ super.s_journal_block * super.s_blocksize <= REISERFS_DISK_OFFSET_IN_BYTES)) { /* Try old super block position */ superblock = REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; if (/*part_length < superblock + (sizeof (super) >> SECTOR_BITS) || */ ! devread (ffi, superblock, 0, sizeof (struct reiserfs_super_block), (char *) &super)) return 0; if (substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) > 0 && substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) { /* pre journaling super block ? */ if (substring (REISERFS_SUPER_MAGIC_STRING, (char*) ((char *) &super + 20)) > 0) return 0; super.s_blocksize = REISERFS_OLD_BLOCKSIZE; super.s_journal_block = 0; super.s_version = 0; } } /* check the version number. */ if (super.s_version > REISERFS_MAX_SUPPORTED_VERSION) return 0; INFO->version = super.s_version; INFO->blocksize = super.s_blocksize; INFO->fullblocksize_shift = log2 (super.s_blocksize); INFO->blocksize_shift = INFO->fullblocksize_shift - SECTOR_BITS; INFO->cached_slots = (FSYSREISER_CACHE_SIZE >> INFO->fullblocksize_shift) - 1; #ifdef REISERDEBUG printf ("reiserfs_mount: version=%d, blocksize=%d\n", INFO->version, INFO->blocksize); #endif /* REISERDEBUG */ /* Clear node cache. */ memset (INFO->blocks, 0, sizeof (INFO->blocks)); if (super.s_blocksize < FSYSREISER_MIN_BLOCKSIZE || super.s_blocksize > FSYSREISER_MAX_BLOCKSIZE || (SECTOR_SIZE << INFO->blocksize_shift) != super.s_blocksize) return 0; /* Initialize journal code. If something fails we end with zero * journal_transactions, so we don't access the journal at all. */ INFO->journal_transactions = 0; if (super.s_journal_block != 0 && super.s_journal_dev == 0) { INFO->journal_block = super.s_journal_block; INFO->journal_block_count = super.s_journal_size; if (is_power_of_two (INFO->journal_block_count)) journal_init (ffi); /* Read in super block again, maybe it is in the journal */ block_read (ffi, superblock >> INFO->blocksize_shift, 0, sizeof (struct reiserfs_super_block), (char *) &super); } if (! block_read (ffi, super.s_root_block, 0, INFO->blocksize, (char*) ROOT)) return 0; INFO->tree_depth = BLOCKHEAD (ROOT)->blk_level; #ifdef REISERDEBUG printf ("root read_in: block=%d, depth=%d\n", super.s_root_block, INFO->tree_depth); #endif /* REISERDEBUG */ if (INFO->tree_depth >= MAX_HEIGHT) return 0; if (INFO->tree_depth == DISK_LEAF_NODE_LEVEL) { /* There is only one node in the whole filesystem, * which is simultanously leaf and root */ memcpy (LEAF, ROOT, INFO->blocksize); } return 1; } /***************** TREE ACCESSING METHODS *****************************/ /* I assume you are familiar with the ReiserFS tree, if not go to * http://www.namesys.com/content_table.html * * My tree node cache is organized as following * 0 ROOT node * 1 LEAF node (if the ROOT is also a LEAF it is copied here * 2-n other nodes on current path from bottom to top. * if there is not enough space in the cache, the top most are * omitted. * * I have only two methods to find a key in the tree: * search_stat(dir_id, objectid) searches for the stat entry (always * the first entry) of an object. * next_key() gets the next key in tree order. * * This means, that I can only sequential reads of files are * efficient, but this really doesn't hurt for grub. */ /* Read in the node at the current path and depth into the node cache. * You must set INFO->blocks[depth] before. */ static char * read_tree_node (fsi_file_t *ffi, unsigned int blockNr, int depth) { char* cache = CACHE(depth); int num_cached = INFO->cached_slots; if (depth < num_cached) { /* This is the cached part of the path. Check if same block is * needed. */ if (blockNr == INFO->blocks[depth]) return cache; } else cache = CACHE(num_cached); #ifdef REISERDEBUG printf (" next read_in: block=%d (depth=%d)\n", blockNr, depth); #endif /* REISERDEBUG */ if (! block_read (ffi, blockNr, 0, INFO->blocksize, cache)) return 0; /* Make sure it has the right node level */ if (BLOCKHEAD (cache)->blk_level != depth) { errnum = ERR_FSYS_CORRUPT; return 0; } INFO->blocks[depth] = blockNr; return cache; } /* Get the next key, i.e. the key following the last retrieved key in * tree order. INFO->current_ih and * INFO->current_info are adapted accordingly. */ static int next_key (fsi_file_t *ffi) { int depth; struct item_head *ih = INFO->current_ih + 1; char *cache; #ifdef REISERDEBUG printf ("next_key:\n old ih: key %d:%d:%d:%d version:%d\n", INFO->current_ih->ih_key.k_dir_id, INFO->current_ih->ih_key.k_objectid, INFO->current_ih->ih_key.u.v1.k_offset, INFO->current_ih->ih_key.u.v1.k_uniqueness, INFO->current_ih->ih_version); #endif /* REISERDEBUG */ if (ih == &ITEMHEAD[BLOCKHEAD (LEAF)->blk_nr_item]) { depth = DISK_LEAF_NODE_LEVEL; /* The last item, was the last in the leaf node. * Read in the next block */ do { if (depth == INFO->tree_depth) { /* There are no more keys at all. * Return a dummy item with MAX_KEY */ ih = (struct item_head *) &BLOCKHEAD (LEAF)->blk_right_delim_key; goto found; } depth++; #ifdef REISERDEBUG printf (" depth=%d, i=%d\n", depth, INFO->next_key_nr[depth]); #endif /* REISERDEBUG */ } while (INFO->next_key_nr[depth] == 0); if (depth == INFO->tree_depth) cache = ROOT; else if (depth <= INFO->cached_slots) cache = CACHE (depth); else { cache = read_tree_node (ffi, INFO->blocks[depth], depth); if (! cache) return 0; } do { int nr_item = BLOCKHEAD (cache)->blk_nr_item; int key_nr = INFO->next_key_nr[depth]++; #ifdef REISERDEBUG printf (" depth=%d, i=%d/%d\n", depth, key_nr, nr_item); #endif /* REISERDEBUG */ if (key_nr == nr_item) /* This is the last item in this block, set the next_key_nr to 0 */ INFO->next_key_nr[depth] = 0; cache = read_tree_node (ffi, DC (cache)[key_nr].dc_block_number, --depth); if (! cache) return 0; } while (depth > DISK_LEAF_NODE_LEVEL); ih = ITEMHEAD; } found: INFO->current_ih = ih; INFO->current_item = &LEAF[ih->ih_item_location]; #ifdef REISERDEBUG printf (" new ih: key %d:%d:%d:%d version:%d\n", INFO->current_ih->ih_key.k_dir_id, INFO->current_ih->ih_key.k_objectid, INFO->current_ih->ih_key.u.v1.k_offset, INFO->current_ih->ih_key.u.v1.k_uniqueness, INFO->current_ih->ih_version); #endif /* REISERDEBUG */ return 1; } /* preconditions: reiserfs_mount already executed, therefore * INFO block is valid * returns: 0 if error (errnum is set), * nonzero iff we were able to find the key successfully. * postconditions: on a nonzero return, the current_ih and * current_item fields describe the key that equals the * searched key. INFO->next_key contains the next key after * the searched key. * side effects: messes around with the cache. */ static int search_stat (fsi_file_t *ffi, __u32 dir_id, __u32 objectid) { char *cache; int depth; int nr_item; int i; struct item_head *ih; #ifdef REISERDEBUG printf ("search_stat:\n key %d:%d:0:0\n", dir_id, objectid); #endif /* REISERDEBUG */ depth = INFO->tree_depth; cache = ROOT; while (depth > DISK_LEAF_NODE_LEVEL) { struct key *key; nr_item = BLOCKHEAD (cache)->blk_nr_item; key = KEY (cache); for (i = 0; i < nr_item; i++) { if (key->k_dir_id > dir_id || (key->k_dir_id == dir_id && (key->k_objectid > objectid || (key->k_objectid == objectid && (key->u.v1.k_offset | key->u.v1.k_uniqueness) > 0)))) break; key++; } #ifdef REISERDEBUG printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); #endif /* REISERDEBUG */ INFO->next_key_nr[depth] = (i == nr_item) ? 0 : i+1; cache = read_tree_node (ffi, DC (cache)[i].dc_block_number, --depth); if (! cache) return 0; } /* cache == LEAF */ nr_item = BLOCKHEAD (LEAF)->blk_nr_item; ih = ITEMHEAD; for (i = 0; i < nr_item; i++) { if (ih->ih_key.k_dir_id == dir_id && ih->ih_key.k_objectid == objectid && ih->ih_key.u.v1.k_offset == 0 && ih->ih_key.u.v1.k_uniqueness == 0) { #ifdef REISERDEBUG printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); #endif /* REISERDEBUG */ INFO->current_ih = ih; INFO->current_item = &LEAF[ih->ih_item_location]; return 1; } ih++; } errnum = ERR_FSYS_CORRUPT; return 0; } static int reiserfs_read (fsi_file_t *ffi, char *buf, int len) { unsigned int blocksize; unsigned int offset; unsigned int to_read; char *prev_buf = buf; #ifdef REISERDEBUG printf ("reiserfs_read: filepos=%d len=%d, offset=%x:%x\n", filepos, len, (__u64) IH_KEY_OFFSET (INFO->current_ih) - 1); #endif /* REISERDEBUG */ if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid || IH_KEY_OFFSET (INFO->current_ih) > filepos + 1) { search_stat (ffi, INFO->fileinfo.k_dir_id, INFO->fileinfo.k_objectid); goto get_next_key; } while (! errnum) { if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid) break; offset = filepos - IH_KEY_OFFSET (INFO->current_ih) + 1; blocksize = INFO->current_ih->ih_item_len; #ifdef REISERDEBUG printf (" loop: filepos=%d len=%d, offset=%d blocksize=%d\n", filepos, len, offset, blocksize); #endif /* REISERDEBUG */ if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_DIRECT) && offset < blocksize) { #ifdef REISERDEBUG printf ("direct_read: offset=%d, blocksize=%d\n", offset, blocksize); #endif /* REISERDEBUG */ to_read = blocksize - offset; if (to_read > len) to_read = len; if (disk_read_hook != NULL) { disk_read_func = disk_read_hook; block_read (ffi, INFO->blocks[DISK_LEAF_NODE_LEVEL], (INFO->current_item - LEAF + offset), to_read, buf); disk_read_func = NULL; } else memcpy (buf, INFO->current_item + offset, to_read); goto update_buf_len; } else if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_INDIRECT)) { blocksize = (blocksize >> 2) << INFO->fullblocksize_shift; #ifdef REISERDEBUG printf ("indirect_read: offset=%d, blocksize=%d\n", offset, blocksize); #endif /* REISERDEBUG */ while (offset < blocksize) { __u32 blocknr = ((__u32 *) INFO->current_item) [offset >> INFO->fullblocksize_shift]; int blk_offset = offset & (INFO->blocksize-1); to_read = INFO->blocksize - blk_offset; if (to_read > len) to_read = len; disk_read_func = disk_read_hook; /* Journal is only for meta data. Data blocks can be read * directly without using block_read */ devread (ffi, blocknr << INFO->blocksize_shift, blk_offset, to_read, buf); disk_read_func = NULL; update_buf_len: len -= to_read; buf += to_read; offset += to_read; filepos += to_read; if (len == 0) goto done; } } get_next_key: next_key (ffi); } done: return errnum ? 0 : buf - prev_buf; } /* preconditions: reiserfs_mount already executed, therefore * INFO block is valid * returns: 0 if error, nonzero iff we were able to find the file successfully * postconditions: on a nonzero return, INFO->fileinfo contains the info * of the file we were trying to look up, filepos is 0 and filemax is * the size of the file. */ static int reiserfs_dir (fsi_file_t *ffi, char *dirname) { struct reiserfs_de_head *de_head; char *rest, ch; __u32 dir_id, objectid, parent_dir_id = 0, parent_objectid = 0; #ifndef STAGE1_5 int do_possibilities = 0; #endif /* ! STAGE1_5 */ char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ int link_count = 0; int mode; dir_id = REISERFS_ROOT_PARENT_OBJECTID; objectid = REISERFS_ROOT_OBJECTID; while (1) { #ifdef REISERDEBUG printf ("dirname=%s\n", dirname); #endif /* REISERDEBUG */ /* Search for the stat info first. */ if (! search_stat (ffi, dir_id, objectid)) return 0; #ifdef REISERDEBUG printf ("sd_mode=%x sd_size=%d\n", ((struct stat_data *) INFO->current_item)->sd_mode, ((struct stat_data *) INFO->current_item)->sd_size); #endif /* REISERDEBUG */ mode = ((struct stat_data *) INFO->current_item)->sd_mode; /* If we've got a symbolic link, then chase it. */ if (S_ISLNK (mode)) { int len; if (++link_count > MAX_LINK_COUNT) { errnum = ERR_SYMLINK_LOOP; return 0; } /* Get the symlink size. */ filemax = ((struct stat_data *) INFO->current_item)->sd_size; /* Find out how long our remaining name is. */ len = 0; while (dirname[len] && !isspace ((uint8_t)dirname[len])) len++; if (filemax + len > sizeof (linkbuf) - 1) { errnum = ERR_FILELENGTH; return 0; } /* Copy the remaining name to the end of the symlink data. Note that DIRNAME and LINKBUF may overlap! */ grub_memmove (linkbuf + filemax, dirname, len+1); INFO->fileinfo.k_dir_id = dir_id; INFO->fileinfo.k_objectid = objectid; filepos = 0; if (! next_key (ffi) || reiserfs_read (ffi, linkbuf, filemax) != filemax) { if (! errnum) errnum = ERR_FSYS_CORRUPT; return 0; } #ifdef REISERDEBUG printf ("symlink=%s\n", linkbuf); #endif /* REISERDEBUG */ dirname = linkbuf; if (*dirname == '/') { /* It's an absolute link, so look it up in root. */ dir_id = REISERFS_ROOT_PARENT_OBJECTID; objectid = REISERFS_ROOT_OBJECTID; } else { /* Relative, so look it up in our parent directory. */ dir_id = parent_dir_id; objectid = parent_objectid; } /* Now lookup the new name. */ continue; } /* if we have a real file (and we're not just printing possibilities), then this is where we want to exit */ if (! *dirname || isspace ((uint8_t)*dirname)) { if (! S_ISREG (mode)) { errnum = ERR_BAD_FILETYPE; return 0; } filepos = 0; filemax = ((struct stat_data *) INFO->current_item)->sd_size; /* If this is a new stat data and size is > 4GB set filemax to * maximum */ if (INFO->current_ih->ih_version == ITEM_VERSION_2 && ((struct stat_data *) INFO->current_item)->sd_size_hi > 0) filemax = 0xffffffff; INFO->fileinfo.k_dir_id = dir_id; INFO->fileinfo.k_objectid = objectid; return next_key (ffi); } /* continue with the file/directory name interpretation */ while (*dirname == '/') dirname++; if (! S_ISDIR (mode)) { errnum = ERR_BAD_FILETYPE; return 0; } for (rest = dirname; (ch = *rest) && ! isspace ((uint8_t)ch) && ch != '/'; rest++); *rest = 0; # ifndef STAGE1_5 if (print_possibilities && ch != '/') do_possibilities = 1; # endif /* ! STAGE1_5 */ while (1) { char *name_end; int num_entries; if (! next_key (ffi)) return 0; #ifdef REISERDEBUG printf ("ih: key %d:%d:%d:%d version:%d\n", INFO->current_ih->ih_key.k_dir_id, INFO->current_ih->ih_key.k_objectid, INFO->current_ih->ih_key.u.v1.k_offset, INFO->current_ih->ih_key.u.v1.k_uniqueness, INFO->current_ih->ih_version); #endif /* REISERDEBUG */ if (INFO->current_ih->ih_key.k_objectid != objectid) break; name_end = INFO->current_item + INFO->current_ih->ih_item_len; de_head = (struct reiserfs_de_head *) INFO->current_item; num_entries = INFO->current_ih->u.ih_entry_count; while (num_entries > 0) { char *filename = INFO->current_item + de_head->deh_location; char tmp = *name_end; if ((de_head->deh_state & DEH_Visible)) { int cmp; /* Directory names in ReiserFS are not null * terminated. We write a temporary 0 behind it. * NOTE: that this may overwrite the first block in * the tree cache. That doesn't hurt as long as we * don't call next_key () in between. */ *name_end = 0; cmp = substring (dirname, filename); *name_end = tmp; # ifndef STAGE1_5 if (do_possibilities) { if (cmp <= 0) { if (print_possibilities > 0) print_possibilities = -print_possibilities; *name_end = 0; print_a_completion (filename); *name_end = tmp; } } else # endif /* ! STAGE1_5 */ if (cmp == 0) goto found; } /* The beginning of this name marks the end of the next name. */ name_end = filename; de_head++; num_entries--; } } # ifndef STAGE1_5 if (print_possibilities < 0) return 1; # endif /* ! STAGE1_5 */ errnum = ERR_FILE_NOT_FOUND; *rest = ch; return 0; found: *rest = ch; dirname = rest; parent_dir_id = dir_id; parent_objectid = objectid; dir_id = de_head->deh_dir_id; objectid = de_head->deh_objectid; } } int reiserfs_embed (fsi_file_t *ffi, int *start_sector, int needed_sectors) { struct reiserfs_super_block super; int num_sectors; if (! devread (ffi, REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS, 0, sizeof (struct reiserfs_super_block), (char *) &super)) return 0; *start_sector = 1; /* reserve first sector for stage1 */ if ((substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) <= 0 || substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) <= 0 || substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) <= 0) && (/* check that this is not a super block copy inside * the journal log */ super.s_journal_block * super.s_blocksize > REISERFS_DISK_OFFSET_IN_BYTES)) num_sectors = (REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; else num_sectors = (REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; return (needed_sectors <= num_sectors); } fsi_plugin_ops_t * fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) { static fsig_plugin_ops_t ops = { FSIMAGE_PLUGIN_VERSION, .fpo_mount = reiserfs_mount, .fpo_dir = reiserfs_dir, .fpo_read = reiserfs_read }; *name = "reiserfs"; return (fsig_init(fp, &ops)); } xen-4.9.2/tools/libfsimage/reiserfs/Makefile0000664000175000017500000000027013256712137017200 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. LIB_SRCS-y = fsys_reiserfs.c FS = reiserfs .PHONY: all all: fs-all .PHONY: install install: fs-install include $(XEN_ROOT)/tools/libfsimage/Rules.mk xen-4.9.2/tools/libfsimage/xfs/0000775000175000017500000000000013256712137014517 5ustar smbsmbxen-4.9.2/tools/libfsimage/xfs/fsys_xfs.c0000664000175000017500000003446113256712137016537 0ustar smbsmb/* fsys_xfs.c - an implementation for the SGI XFS file system */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2001,2002,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #include #include "xfs.h" #define MAX_LINK_COUNT 8 typedef struct xad { xfs_fileoff_t offset; xfs_fsblock_t start; xfs_filblks_t len; } xad_t; struct xfs_info { int bsize; int dirbsize; int isize; unsigned int agblocks; int bdlog; int blklog; int inopblog; int agblklog; int agnolog; unsigned int nextents; xfs_daddr_t next; xfs_daddr_t daddr; xfs_dablk_t forw; xfs_dablk_t dablk; xfs_bmbt_rec_32_t *xt; xfs_bmbt_ptr_t ptr0; int btnode_ptr0_off; int i8param; int dirpos; int dirmax; int blkoff; int fpos; xfs_ino_t rootino; }; static struct xfs_info xfs; #define dirbuf ((char *)FSYS_BUF) #define filebuf ((char *)FSYS_BUF + 4096) #define inode ((xfs_dinode_t *)((char *)FSYS_BUF + 8192)) #define icore (inode->di_core) #define mask32lo(n) (((xfs_uint32_t)1 << (n)) - 1) #define XFS_INO_MASK(k) ((xfs_uint32_t)((1ULL << (k)) - 1)) #define XFS_INO_OFFSET_BITS xfs.inopblog #define XFS_INO_AGBNO_BITS xfs.agblklog #define XFS_INO_AGINO_BITS (xfs.agblklog + xfs.inopblog) #define XFS_INO_AGNO_BITS xfs.agnolog static inline xfs_agblock_t agino2agbno (xfs_agino_t agino) { return agino >> XFS_INO_OFFSET_BITS; } static inline xfs_agnumber_t ino2agno (xfs_ino_t ino) { return ino >> XFS_INO_AGINO_BITS; } static inline xfs_agino_t ino2agino (xfs_ino_t ino) { return ino & XFS_INO_MASK(XFS_INO_AGINO_BITS); } static inline int ino2offset (xfs_ino_t ino) { return ino & XFS_INO_MASK(XFS_INO_OFFSET_BITS); } static inline xfs_uint16_t le16 (xfs_uint16_t x) { __asm__("xchgb %b0,%h0" \ : "=Q" (x) \ : "0" (x)); \ return x; } static inline xfs_uint32_t le32 (xfs_uint32_t x) { #if 0 /* 386 doesn't have bswap. */ __asm__("bswap %0" : "=r" (x) : "0" (x)); #else /* This is slower but this works on all x86 architectures. */ __asm__("xchgb %b0, %h0" \ "\n\troll $16, %0" \ "\n\txchgb %b0, %h0" \ : "=Q" (x) : "0" (x)); #endif return x; } static inline xfs_uint64_t le64 (xfs_uint64_t x) { xfs_uint32_t h = x >> 32; xfs_uint32_t l = x & ((1ULL<<32)-1); return (((xfs_uint64_t)le32(l)) << 32) | ((xfs_uint64_t)(le32(h))); } static xfs_fsblock_t xt_start (xfs_bmbt_rec_32_t *r) { return (((xfs_fsblock_t)(le32 (r->l1) & mask32lo(9))) << 43) | (((xfs_fsblock_t)le32 (r->l2)) << 11) | (((xfs_fsblock_t)le32 (r->l3)) >> 21); } static xfs_fileoff_t xt_offset (xfs_bmbt_rec_32_t *r) { return (((xfs_fileoff_t)le32 (r->l0) & mask32lo(31)) << 23) | (((xfs_fileoff_t)le32 (r->l1)) >> 9); } static xfs_filblks_t xt_len (xfs_bmbt_rec_32_t *r) { return le32(r->l3) & mask32lo(21); } static inline int xfs_highbit32(xfs_uint32_t v) { int i; if (--v) { for (i = 0; i < 31; i++, v >>= 1) { if (v == 0) return i; } } return 0; } static int isinxt (xfs_fileoff_t key, xfs_fileoff_t offset, xfs_filblks_t len) { return (key >= offset) ? (key < offset + len ? 1 : 0) : 0; } static xfs_daddr_t agb2daddr (xfs_agnumber_t agno, xfs_agblock_t agbno) { return ((xfs_fsblock_t)agno*xfs.agblocks + agbno) << xfs.bdlog; } static xfs_daddr_t fsb2daddr (xfs_fsblock_t fsbno) { return agb2daddr ((xfs_agnumber_t)(fsbno >> xfs.agblklog), (xfs_agblock_t)(fsbno & mask32lo(xfs.agblklog))); } #undef offsetof #define offsetof(t,m) ((size_t)&(((t *)0)->m)) static inline int btroot_maxrecs (fsi_file_t *ffi) { int tmp = icore.di_forkoff ? (icore.di_forkoff << 3) : xfs.isize; return (tmp - sizeof(xfs_bmdr_block_t) - offsetof(xfs_dinode_t, di_u)) / (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t)); } static int di_read (fsi_file_t *ffi, xfs_ino_t ino) { xfs_agino_t agino; xfs_agnumber_t agno; xfs_agblock_t agbno; xfs_daddr_t daddr; int offset; agno = ino2agno (ino); agino = ino2agino (ino); agbno = agino2agbno (agino); offset = ino2offset (ino); daddr = agb2daddr (agno, agbno); devread (ffi, daddr, offset*xfs.isize, xfs.isize, (char *)inode); xfs.ptr0 = *(xfs_bmbt_ptr_t *) (inode->di_u.di_c + sizeof(xfs_bmdr_block_t) + btroot_maxrecs (ffi)*sizeof(xfs_bmbt_key_t)); return 1; } static void init_extents (fsi_file_t *ffi) { xfs_bmbt_ptr_t ptr0; xfs_btree_lblock_t h; switch (icore.di_format) { case XFS_DINODE_FMT_EXTENTS: xfs.xt = inode->di_u.di_bmx; xfs.nextents = le32 (icore.di_nextents); break; case XFS_DINODE_FMT_BTREE: ptr0 = xfs.ptr0; for (;;) { xfs.daddr = fsb2daddr (le64(ptr0)); devread (ffi, xfs.daddr, 0, sizeof(xfs_btree_lblock_t), (char *)&h); if (!h.bb_level) { xfs.nextents = le16(h.bb_numrecs); xfs.next = fsb2daddr (le64(h.bb_rightsib)); xfs.fpos = sizeof(xfs_btree_block_t); return; } devread (ffi, xfs.daddr, xfs.btnode_ptr0_off, sizeof(xfs_bmbt_ptr_t), (char *)&ptr0); } } } static xad_t * next_extent (fsi_file_t *ffi) { static xad_t xad; switch (icore.di_format) { case XFS_DINODE_FMT_EXTENTS: if (xfs.nextents == 0) return NULL; break; case XFS_DINODE_FMT_BTREE: if (xfs.nextents == 0) { xfs_btree_lblock_t h; if (xfs.next == 0) return NULL; xfs.daddr = xfs.next; devread (ffi, xfs.daddr, 0, sizeof(xfs_btree_lblock_t), (char *)&h); xfs.nextents = le16(h.bb_numrecs); xfs.next = fsb2daddr (le64(h.bb_rightsib)); xfs.fpos = sizeof(xfs_btree_block_t); } /* Yeah, I know that's slow, but I really don't care */ devread (ffi, xfs.daddr, xfs.fpos, sizeof(xfs_bmbt_rec_t), filebuf); xfs.xt = (xfs_bmbt_rec_32_t *)filebuf; xfs.fpos += sizeof(xfs_bmbt_rec_32_t); } xad.offset = xt_offset (xfs.xt); xad.start = xt_start (xfs.xt); xad.len = xt_len (xfs.xt); ++xfs.xt; --xfs.nextents; return &xad; } /* * Name lies - the function reads only first 100 bytes */ static void xfs_dabread (fsi_file_t *ffi) { xad_t *xad; xfs_fileoff_t offset;; init_extents (ffi); while ((xad = next_extent (ffi))) { offset = xad->offset; if (isinxt (xfs.dablk, offset, xad->len)) { devread (ffi, fsb2daddr (xad->start + xfs.dablk - offset), 0, 100, dirbuf); break; } } } static inline xfs_ino_t sf_ino (char *sfe, int namelen) { void *p = sfe + namelen + 3; return (xfs.i8param == 0) ? le64(*(xfs_ino_t *)p) : le32(*(xfs_uint32_t *)p); } static inline xfs_ino_t sf_parent_ino (fsi_file_t *ffi) { return (xfs.i8param == 0) ? le64(*(xfs_ino_t *)(&inode->di_u.di_dir2sf.hdr.parent)) : le32(*(xfs_uint32_t *)(&inode->di_u.di_dir2sf.hdr.parent)); } static inline int roundup8 (int n) { return ((n+7)&~7); } static int xfs_read (fsi_file_t *ffi, char *buf, int len); static char * next_dentry (fsi_file_t *ffi, xfs_ino_t *ino) { int namelen = 1; int toread; static char usual[2][3] = {".", ".."}; static xfs_dir2_sf_entry_t *sfe; char *name = usual[0]; if (xfs.dirpos >= xfs.dirmax) { if (xfs.forw == 0) return NULL; xfs.dablk = xfs.forw; xfs_dabread (ffi); #define h ((xfs_dir2_leaf_hdr_t *)dirbuf) xfs.dirmax = le16 (h->count) - le16 (h->stale); xfs.forw = le32 (h->info.forw); #undef h xfs.dirpos = 0; } switch (icore.di_format) { case XFS_DINODE_FMT_LOCAL: switch (xfs.dirpos) { case -2: *ino = 0; break; case -1: *ino = sf_parent_ino (ffi); ++name; ++namelen; sfe = (xfs_dir2_sf_entry_t *) (inode->di_u.di_c + sizeof(xfs_dir2_sf_hdr_t) - xfs.i8param); break; default: namelen = sfe->namelen; *ino = sf_ino ((char *)sfe, namelen); name = (char *)sfe->name; sfe = (xfs_dir2_sf_entry_t *) ((char *)sfe + namelen + 11 - xfs.i8param); } break; case XFS_DINODE_FMT_BTREE: case XFS_DINODE_FMT_EXTENTS: #define dau ((xfs_dir2_data_union_t *)dirbuf) for (;;) { if (xfs.blkoff >= xfs.dirbsize) { xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); filepos &= ~(xfs.dirbsize - 1); filepos |= xfs.blkoff; } xfs_read (ffi, dirbuf, 4); xfs.blkoff += 4; if (dau->unused.freetag == XFS_DIR2_DATA_FREE_TAG) { toread = roundup8 (le16(dau->unused.length)) - 4; xfs.blkoff += toread; filepos += toread; continue; } break; } xfs_read (ffi, (char *)dirbuf + 4, 5); *ino = le64 (dau->entry.inumber); namelen = dau->entry.namelen; #undef dau toread = roundup8 (namelen + 11) - 9; xfs_read (ffi, dirbuf, toread); name = (char *)dirbuf; xfs.blkoff += toread + 5; } ++xfs.dirpos; name[namelen] = 0; return name; } static char * first_dentry (fsi_file_t *ffi, xfs_ino_t *ino) { xfs.forw = 0; switch (icore.di_format) { case XFS_DINODE_FMT_LOCAL: xfs.dirmax = inode->di_u.di_dir2sf.hdr.count; xfs.i8param = inode->di_u.di_dir2sf.hdr.i8count ? 0 : 4; xfs.dirpos = -2; break; case XFS_DINODE_FMT_EXTENTS: case XFS_DINODE_FMT_BTREE: filepos = 0; xfs_read (ffi, dirbuf, sizeof(xfs_dir2_data_hdr_t)); if (((xfs_dir2_data_hdr_t *)dirbuf)->magic == le32(XFS_DIR2_BLOCK_MAGIC)) { #define tail ((xfs_dir2_block_tail_t *)dirbuf) filepos = xfs.dirbsize - sizeof(*tail); xfs_read (ffi, dirbuf, sizeof(*tail)); xfs.dirmax = le32 (tail->count) - le32 (tail->stale); #undef tail } else { xfs.dablk = (1ULL << 35) >> xfs.blklog; #define h ((xfs_dir2_leaf_hdr_t *)dirbuf) #define n ((xfs_da_intnode_t *)dirbuf) for (;;) { xfs_dabread (ffi); if ((n->hdr.info.magic == le16(XFS_DIR2_LEAFN_MAGIC)) || (n->hdr.info.magic == le16(XFS_DIR2_LEAF1_MAGIC))) { xfs.dirmax = le16 (h->count) - le16 (h->stale); xfs.forw = le32 (h->info.forw); break; } xfs.dablk = le32 (n->btree[0].before); } #undef n #undef h } xfs.blkoff = sizeof(xfs_dir2_data_hdr_t); filepos = xfs.blkoff; xfs.dirpos = 0; } return next_dentry (ffi, ino); } static int xfs_mount (fsi_file_t *ffi, const char *options) { xfs_sb_t super; if (!devread (ffi, 0, 0, sizeof(super), (char *)&super) || (le32(super.sb_magicnum) != XFS_SB_MAGIC) || ((le16(super.sb_versionnum) & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4) ) { return 0; } xfs.bsize = le32 (super.sb_blocksize); xfs.blklog = super.sb_blocklog; xfs.bdlog = xfs.blklog - SECTOR_BITS; xfs.rootino = le64 (super.sb_rootino); xfs.isize = le16 (super.sb_inodesize); xfs.agblocks = le32 (super.sb_agblocks); xfs.dirbsize = xfs.bsize << super.sb_dirblklog; xfs.inopblog = super.sb_inopblog; xfs.agblklog = super.sb_agblklog; xfs.agnolog = xfs_highbit32 (le32(super.sb_agcount)); xfs.btnode_ptr0_off = ((xfs.bsize - sizeof(xfs_btree_block_t)) / (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t))) * sizeof(xfs_bmbt_key_t) + sizeof(xfs_btree_block_t); return 1; } static int xfs_read (fsi_file_t *ffi, char *buf, int len) { xad_t *xad; xfs_fileoff_t endofprev, endofcur, offset; xfs_filblks_t xadlen; int toread, startpos, endpos; if (icore.di_format == XFS_DINODE_FMT_LOCAL) { grub_memmove (buf, inode->di_u.di_c + filepos, len); filepos += len; return len; } startpos = filepos; endpos = filepos + len; endofprev = (xfs_fileoff_t)-1; init_extents (ffi); while (len > 0 && (xad = next_extent (ffi))) { offset = xad->offset; xadlen = xad->len; if (isinxt (filepos >> xfs.blklog, offset, xadlen)) { endofcur = (offset + xadlen) << xfs.blklog; toread = (endofcur >= endpos) ? len : (endofcur - filepos); disk_read_func = disk_read_hook; devread (ffi, fsb2daddr (xad->start), filepos - (offset << xfs.blklog), toread, buf); disk_read_func = NULL; buf += toread; len -= toread; filepos += toread; } else if (offset > endofprev) { toread = ((offset << xfs.blklog) >= endpos) ? len : ((offset - endofprev) << xfs.blklog); len -= toread; filepos += toread; for (; toread; toread--) { *buf++ = 0; } continue; } endofprev = offset + xadlen; } return filepos - startpos; } static int xfs_dir (fsi_file_t *ffi, char *dirname) { xfs_ino_t ino, parent_ino, new_ino; xfs_fsize_t di_size; int di_mode; int cmp, n, link_count; char linkbuf[xfs.bsize]; char *rest, *name, ch; parent_ino = ino = xfs.rootino; link_count = 0; for (;;) { di_read (ffi, ino); di_size = le64 (icore.di_size); di_mode = le16 (icore.di_mode); if ((di_mode & IFMT) == IFLNK) { if (++link_count > MAX_LINK_COUNT) { errnum = ERR_SYMLINK_LOOP; return 0; } if (di_size < xfs.bsize - 1) { filepos = 0; filemax = di_size; n = xfs_read (ffi, linkbuf, filemax); } else { errnum = ERR_FILELENGTH; return 0; } ino = (linkbuf[0] == '/') ? xfs.rootino : parent_ino; while (n < (xfs.bsize - 1) && (linkbuf[n++] = *dirname++)); linkbuf[n] = 0; dirname = linkbuf; continue; } if (!*dirname || isspace ((uint8_t)*dirname)) { if ((di_mode & IFMT) != IFREG) { errnum = ERR_BAD_FILETYPE; return 0; } filepos = 0; filemax = di_size; return 1; } if ((di_mode & IFMT) != IFDIR) { errnum = ERR_BAD_FILETYPE; return 0; } for (; *dirname == '/'; dirname++); for (rest = dirname; (ch = *rest) && !isspace ((uint8_t)ch) && ch != '/'; rest++); *rest = 0; name = first_dentry (ffi, &new_ino); for (;;) { cmp = (!*dirname) ? -1 : substring (dirname, name); #ifndef STAGE1_5 if (print_possibilities && ch != '/' && cmp <= 0) { if (print_possibilities > 0) print_possibilities = -print_possibilities; print_a_completion (name); } else #endif if (cmp == 0) { parent_ino = ino; if (new_ino) ino = new_ino; *(dirname = rest) = ch; break; } name = next_dentry (ffi, &new_ino); if (name == NULL) { if (print_possibilities < 0) return 1; errnum = ERR_FILE_NOT_FOUND; *rest = ch; return 0; } } } } fsi_plugin_ops_t * fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) { static fsig_plugin_ops_t ops = { FSIMAGE_PLUGIN_VERSION, .fpo_mount = xfs_mount, .fpo_dir = xfs_dir, .fpo_read = xfs_read }; *name = "xfs"; return (fsig_init(fp, &ops)); } xen-4.9.2/tools/libfsimage/xfs/Makefile0000664000175000017500000000025613256712137016162 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. LIB_SRCS-y = fsys_xfs.c FS = xfs .PHONY: all all: fs-all .PHONY: install install: fs-install include $(XEN_ROOT)/tools/libfsimage/Rules.mk xen-4.9.2/tools/libfsimage/xfs/xfs.h0000664000175000017500000004273213256712137015500 0ustar smbsmb/* xfs.h - an extraction from xfsprogs-1.3.5/include/xfs* into one file */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. * Copyright (C) 2001,2004 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License along * with this program; If not, see . * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ */ typedef signed char xfs_int8_t; typedef unsigned char xfs_uint8_t; typedef short xfs_int16_t; typedef unsigned short xfs_uint16_t; typedef int xfs_int32_t; typedef unsigned int xfs_uint32_t; typedef long long xfs_int64_t; typedef unsigned long long xfs_uint64_t; typedef xfs_uint64_t xfs_ino_t; typedef xfs_uint32_t xfs_agino_t; typedef xfs_int64_t xfs_daddr_t; typedef xfs_int64_t xfs_off_t; typedef xfs_uint8_t uuid_t[16]; /* those are from xfs_types.h */ typedef xfs_uint32_t xfs_agblock_t; /* blockno in alloc. group */ typedef xfs_uint32_t xfs_extlen_t; /* extent length in blocks */ typedef xfs_uint32_t xfs_agnumber_t; /* allocation group number */ typedef xfs_int32_t xfs_extnum_t; /* # of extents in a file */ typedef xfs_int16_t xfs_aextnum_t; /* # extents in an attribute fork */ typedef xfs_int64_t xfs_fsize_t; /* bytes in a file */ typedef xfs_uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ typedef xfs_uint32_t xfs_dahash_t; /* dir/attr hash value */ /* * Disk based types: */ typedef xfs_uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */ typedef xfs_uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */ typedef xfs_uint64_t xfs_drtbno_t; /* extent (block) in realtime area */ typedef xfs_uint64_t xfs_dfiloff_t; /* block number in a file */ typedef xfs_uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ typedef xfs_uint64_t xfs_fileoff_t; /* block number in a file */ typedef xfs_uint64_t xfs_filblks_t; /* number of blocks in a file */ /* those are from xfs_sb.h */ #define XFS_SB_MAGIC 0x58465342 /* 'XFSB'*/ #define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */ #define XFS_SB_VERSION_NUMBITS 0x000f typedef struct xfs_sb { xfs_uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ xfs_uint32_t sb_blocksize; /* logical block size, bytes */ xfs_drfsbno_t sb_dblocks; /* number of data blocks */ xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */ xfs_drtbno_t sb_rextents; /* number of realtime extents */ uuid_t sb_uuid; /* file system unique id */ xfs_dfsbno_t sb_logstart; /* starting block of log if internal */ xfs_ino_t sb_rootino; /* root inode number */ xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */ xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */ xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */ xfs_agblock_t sb_agblocks; /* size of an allocation group */ xfs_agnumber_t sb_agcount; /* number of allocation groups */ xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */ xfs_extlen_t sb_logblocks; /* number of log blocks */ xfs_uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ xfs_uint16_t sb_sectsize; /* volume sector size, bytes */ xfs_uint16_t sb_inodesize; /* inode size, bytes */ xfs_uint16_t sb_inopblock; /* inodes per block */ char sb_fname[12]; /* file system name */ xfs_uint8_t sb_blocklog; /* log2 of sb_blocksize */ xfs_uint8_t sb_sectlog; /* log2 of sb_sectsize */ xfs_uint8_t sb_inodelog; /* log2 of sb_inodesize */ xfs_uint8_t sb_inopblog; /* log2 of sb_inopblock */ xfs_uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ xfs_uint8_t sb_rextslog; /* log2 of sb_rextents */ xfs_uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ xfs_uint8_t sb_imax_pct; /* max % of fs for inode space */ /* statistics */ /* * These fields must remain contiguous. If you really * want to change their layout, make sure you fix the * code in xfs_trans_apply_sb_deltas(). */ xfs_uint64_t sb_icount; /* allocated inodes */ xfs_uint64_t sb_ifree; /* free inodes */ xfs_uint64_t sb_fdblocks; /* free data blocks */ xfs_uint64_t sb_frextents; /* free realtime extents */ /* * End contiguous fields. */ xfs_ino_t sb_uquotino; /* user quota inode */ xfs_ino_t sb_gquotino; /* group quota inode */ xfs_uint16_t sb_qflags; /* quota flags */ xfs_uint8_t sb_flags; /* misc. flags */ xfs_uint8_t sb_shared_vn; /* shared version number */ xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */ xfs_uint32_t sb_unit; /* stripe or raid unit */ xfs_uint32_t sb_width; /* stripe or raid width */ xfs_uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */ xfs_uint8_t sb_dummy[7]; /* padding */ } xfs_sb_t; /* those are from xfs_btree.h */ /* * Long form header: bmap btrees. */ typedef struct xfs_btree_lblock { xfs_uint32_t bb_magic; /* magic number for block type */ xfs_uint16_t bb_level; /* 0 is a leaf */ xfs_uint16_t bb_numrecs; /* current # of data records */ xfs_dfsbno_t bb_leftsib; /* left sibling block or NULLDFSBNO */ xfs_dfsbno_t bb_rightsib; /* right sibling block or NULLDFSBNO */ } xfs_btree_lblock_t; /* * Combined header and structure, used by common code. */ typedef struct xfs_btree_hdr { xfs_uint32_t bb_magic; /* magic number for block type */ xfs_uint16_t bb_level; /* 0 is a leaf */ xfs_uint16_t bb_numrecs; /* current # of data records */ } xfs_btree_hdr_t; typedef struct xfs_btree_block { xfs_btree_hdr_t bb_h; /* header */ union { struct { xfs_agblock_t bb_leftsib; xfs_agblock_t bb_rightsib; } s; /* short form pointers */ struct { xfs_dfsbno_t bb_leftsib; xfs_dfsbno_t bb_rightsib; } l; /* long form pointers */ } bb_u; /* rest */ } xfs_btree_block_t; /* those are from xfs_bmap_btree.h */ /* * Bmap root header, on-disk form only. */ typedef struct xfs_bmdr_block { xfs_uint16_t bb_level; /* 0 is a leaf */ xfs_uint16_t bb_numrecs; /* current # of data records */ } xfs_bmdr_block_t; /* * Bmap btree record and extent descriptor. * For 32-bit kernels, * l0:31 is an extent flag (value 1 indicates non-normal). * l0:0-30 and l1:9-31 are startoff. * l1:0-8, l2:0-31, and l3:21-31 are startblock. * l3:0-20 are blockcount. * For 64-bit kernels, * l0:63 is an extent flag (value 1 indicates non-normal). * l0:9-62 are startoff. * l0:0-8 and l1:21-63 are startblock. * l1:0-20 are blockcount. */ #define BMBT_USE_64 1 typedef struct xfs_bmbt_rec_32 { xfs_uint32_t l0, l1, l2, l3; } xfs_bmbt_rec_32_t; typedef struct xfs_bmbt_rec_64 { xfs_uint64_t l0, l1; } xfs_bmbt_rec_64_t; #if BMBT_USE_64 typedef xfs_uint64_t xfs_bmbt_rec_base_t; /* use this for casts */ typedef xfs_bmbt_rec_64_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; #else /* !BMBT_USE_64 */ typedef xfs_uint32_t xfs_bmbt_rec_base_t; /* use this for casts */ typedef xfs_bmbt_rec_32_t xfs_bmbt_rec_t, xfs_bmdr_rec_t; #endif /* BMBT_USE_64 */ /* * Key structure for non-leaf levels of the tree. */ typedef struct xfs_bmbt_key { xfs_dfiloff_t br_startoff; /* starting file offset */ } xfs_bmbt_key_t, xfs_bmdr_key_t; typedef xfs_dfsbno_t xfs_bmbt_ptr_t, xfs_bmdr_ptr_t; /* btree pointer type */ /* btree block header type */ typedef struct xfs_btree_lblock xfs_bmbt_block_t; /* those are from xfs_dir2.h */ /* * Directory version 2. * There are 4 possible formats: * shortform * single block - data with embedded leaf at the end * multiple data blocks, single leaf+freeindex block * data blocks, node&leaf blocks (btree), freeindex blocks * * The shortform format is in xfs_dir2_sf.h. * The single block format is in xfs_dir2_block.h. * The data block format is in xfs_dir2_data.h. * The leaf and freeindex block formats are in xfs_dir2_leaf.h. * Node blocks are the same as the other version, in xfs_da_btree.h. */ /* * Byte offset in data block and shortform entry. */ typedef xfs_uint16_t xfs_dir2_data_off_t; /* * Byte offset in a directory. */ typedef xfs_off_t xfs_dir2_off_t; /* those are from xfs_da_btree.h */ /*======================================================================== * Directory Structure when greater than XFS_LBSIZE(mp) bytes. *========================================================================*/ /* * This structure is common to both leaf nodes and non-leaf nodes in the Btree. * * Is is used to manage a doubly linked list of all blocks at the same * level in the Btree, and to identify which type of block this is. */ #define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* magic number: v2 dirlf single blks */ #define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* magic number: v2 dirlf multi blks */ typedef struct xfs_da_blkinfo { xfs_dablk_t forw; /* previous block in list */ xfs_dablk_t back; /* following block in list */ xfs_uint16_t magic; /* validity check on block */ xfs_uint16_t pad; /* unused */ } xfs_da_blkinfo_t; /* * This is the structure of the root and intermediate nodes in the Btree. * The leaf nodes are defined above. * * Entries are not packed. * * Since we have duplicate keys, use a binary search but always follow * all match in the block, not just the first match found. */ typedef struct xfs_da_intnode { struct xfs_da_node_hdr { /* constant-structure header block */ xfs_da_blkinfo_t info; /* block type, links, etc. */ xfs_uint16_t count; /* count of active entries */ xfs_uint16_t level; /* level above leaves (leaf == 0) */ } hdr; struct xfs_da_node_entry { xfs_dahash_t hashval; /* hash value for this descendant */ xfs_dablk_t before; /* Btree block before this key */ } btree[1]; /* variable sized array of keys */ } xfs_da_intnode_t; /* those are from xfs_dir2_data.h */ /* * Directory format 2, data block structures. */ /* * Constants. */ #define XFS_DIR2_DATA_FREE_TAG 0xffff #define XFS_DIR2_DATA_FD_COUNT 3 /* * Structures. */ /* * Describe a free area in the data block. * The freespace will be formatted as a xfs_dir2_data_unused_t. */ typedef struct xfs_dir2_data_free { xfs_dir2_data_off_t offset; /* start of freespace */ xfs_dir2_data_off_t length; /* length of freespace */ } xfs_dir2_data_free_t; /* * Header for the data blocks. * Always at the beginning of a directory-sized block. * The code knows that XFS_DIR2_DATA_FD_COUNT is 3. */ typedef struct xfs_dir2_data_hdr { xfs_uint32_t magic; /* XFS_DIR2_DATA_MAGIC */ /* or XFS_DIR2_BLOCK_MAGIC */ xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT]; } xfs_dir2_data_hdr_t; /* * Active entry in a data block. Aligned to 8 bytes. * Tag appears as the last 2 bytes. */ typedef struct xfs_dir2_data_entry { xfs_ino_t inumber; /* inode number */ xfs_uint8_t namelen; /* name length */ xfs_uint8_t name[1]; /* name bytes, no null */ /* variable offset */ xfs_dir2_data_off_t tag; /* starting offset of us */ } xfs_dir2_data_entry_t; /* * Unused entry in a data block. Aligned to 8 bytes. * Tag appears as the last 2 bytes. */ typedef struct xfs_dir2_data_unused { xfs_uint16_t freetag; /* XFS_DIR2_DATA_FREE_TAG */ xfs_dir2_data_off_t length; /* total free length */ /* variable offset */ xfs_dir2_data_off_t tag; /* starting offset of us */ } xfs_dir2_data_unused_t; typedef union { xfs_dir2_data_entry_t entry; xfs_dir2_data_unused_t unused; } xfs_dir2_data_union_t; /* those are from xfs_dir2_leaf.h */ /* * Directory version 2, leaf block structures. */ /* * Leaf block header. */ typedef struct xfs_dir2_leaf_hdr { xfs_da_blkinfo_t info; /* header for da routines */ xfs_uint16_t count; /* count of entries */ xfs_uint16_t stale; /* count of stale entries */ } xfs_dir2_leaf_hdr_t; /* those are from xfs_dir2_block.h */ /* * xfs_dir2_block.h * Directory version 2, single block format structures */ /* * The single block format is as follows: * xfs_dir2_data_hdr_t structure * xfs_dir2_data_entry_t and xfs_dir2_data_unused_t structures * xfs_dir2_leaf_entry_t structures * xfs_dir2_block_tail_t structure */ #define XFS_DIR2_BLOCK_MAGIC 0x58443242 /* XD2B: for one block dirs */ typedef struct xfs_dir2_block_tail { xfs_uint32_t count; /* count of leaf entries */ xfs_uint32_t stale; /* count of stale lf entries */ } xfs_dir2_block_tail_t; /* those are from xfs_dir2_sf.h */ /* * Directory layout when stored internal to an inode. * * Small directories are packed as tightly as possible so as to * fit into the literal area of the inode. */ /* * Inode number stored as 8 8-bit values. */ typedef struct { xfs_uint8_t i[8]; } xfs_dir2_ino8_t; /* * Inode number stored as 4 8-bit values. * Works a lot of the time, when all the inode numbers in a directory * fit in 32 bits. */ typedef struct { xfs_uint8_t i[4]; } xfs_dir2_ino4_t; typedef union { xfs_dir2_ino8_t i8; xfs_dir2_ino4_t i4; } xfs_dir2_inou_t; /* * Normalized offset (in a data block) of the entry, really xfs_dir2_data_off_t. * Only need 16 bits, this is the byte offset into the single block form. */ typedef struct { xfs_uint8_t i[2]; } xfs_dir2_sf_off_t; /* * The parent directory has a dedicated field, and the self-pointer must * be calculated on the fly. * * Entries are packed toward the top as tightly as possible. The header * and the elements must be bcopy()'d out into a work area to get correct * alignment for the inode number fields. */ typedef struct xfs_dir2_sf_hdr { xfs_uint8_t count; /* count of entries */ xfs_uint8_t i8count; /* count of 8-byte inode #s */ xfs_dir2_inou_t parent; /* parent dir inode number */ } xfs_dir2_sf_hdr_t; typedef struct xfs_dir2_sf_entry { xfs_uint8_t namelen; /* actual name length */ xfs_dir2_sf_off_t offset; /* saved offset */ xfs_uint8_t name[1]; /* name, variable size */ xfs_dir2_inou_t inumber; /* inode number, var. offset */ } xfs_dir2_sf_entry_t; typedef struct xfs_dir2_sf { xfs_dir2_sf_hdr_t hdr; /* shortform header */ xfs_dir2_sf_entry_t list[1]; /* shortform entries */ } xfs_dir2_sf_t; /* those are from xfs_dinode.h */ #define XFS_DINODE_VERSION_1 1 #define XFS_DINODE_VERSION_2 2 #define XFS_DINODE_MAGIC 0x494e /* 'IN' */ /* * Disk inode structure. * This is just the header; the inode is expanded to fill a variable size * with the last field expanding. It is split into the core and "other" * because we only need the core part in the in-core inode. */ typedef struct xfs_timestamp { xfs_int32_t t_sec; /* timestamp seconds */ xfs_int32_t t_nsec; /* timestamp nanoseconds */ } xfs_timestamp_t; /* * Note: Coordinate changes to this structure with the XFS_DI_* #defines * below and the offsets table in xfs_ialloc_log_di(). */ typedef struct xfs_dinode_core { xfs_uint16_t di_magic; /* inode magic # = XFS_DINODE_MAGIC */ xfs_uint16_t di_mode; /* mode and type of file */ xfs_int8_t di_version; /* inode version */ xfs_int8_t di_format; /* format of di_c data */ xfs_uint16_t di_onlink; /* old number of links to file */ xfs_uint32_t di_uid; /* owner's user id */ xfs_uint32_t di_gid; /* owner's group id */ xfs_uint32_t di_nlink; /* number of links to file */ xfs_uint16_t di_projid; /* owner's project id */ xfs_uint8_t di_pad[10]; /* unused, zeroed space */ xfs_timestamp_t di_atime; /* time last accessed */ xfs_timestamp_t di_mtime; /* time last modified */ xfs_timestamp_t di_ctime; /* time created/inode modified */ xfs_fsize_t di_size; /* number of bytes in file */ xfs_drfsbno_t di_nblocks; /* # of direct & btree blocks used */ xfs_extlen_t di_extsize; /* basic/minimum extent size for file */ xfs_extnum_t di_nextents; /* number of extents in data fork */ xfs_aextnum_t di_anextents; /* number of extents in attribute fork*/ xfs_uint8_t di_forkoff; /* attr fork offs, <<3 for 64b align */ xfs_int8_t di_aformat; /* format of attr fork's data */ xfs_uint32_t di_dmevmask; /* DMIG event mask */ xfs_uint16_t di_dmstate; /* DMIG state info */ xfs_uint16_t di_flags; /* random flags, XFS_DIFLAG_... */ xfs_uint32_t di_gen; /* generation number */ } xfs_dinode_core_t; typedef struct xfs_dinode { xfs_dinode_core_t di_core; xfs_agino_t di_next_unlinked;/* agi unlinked list ptr */ union { xfs_bmdr_block_t di_bmbt; /* btree root block */ xfs_bmbt_rec_32_t di_bmx[1]; /* extent list */ xfs_dir2_sf_t di_dir2sf; /* shortform directory v2 */ char di_c[1]; /* local contents */ } di_u; } xfs_dinode_t; /* * Values for di_format */ typedef enum xfs_dinode_fmt { XFS_DINODE_FMT_DEV, /* CHR, BLK: di_dev */ XFS_DINODE_FMT_LOCAL, /* DIR, REG: di_c */ /* LNK: di_symlink */ XFS_DINODE_FMT_EXTENTS, /* DIR, REG, LNK: di_bmx */ XFS_DINODE_FMT_BTREE, /* DIR, REG, LNK: di_bmbt */ XFS_DINODE_FMT_UUID /* MNT: di_uuid */ } xfs_dinode_fmt_t; /* * File types (mode field) */ #define IFMT 0170000 /* type of file */ #define IFDIR 0040000 /* directory */ #define IFREG 0100000 /* regular */ #define IFLNK 0120000 /* symbolic link */ xen-4.9.2/tools/libfsimage/ext2fs/0000775000175000017500000000000013256712137015132 5ustar smbsmbxen-4.9.2/tools/libfsimage/ext2fs/Makefile0000664000175000017500000000026413256712137016574 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. LIB_SRCS-y = fsys_ext2fs.c FS = ext2fs .PHONY: all all: fs-all .PHONY: install install: fs-install include $(XEN_ROOT)/tools/libfsimage/Rules.mk xen-4.9.2/tools/libfsimage/ext2fs/fsys_ext2fs.c0000664000175000017500000006724413256712137017572 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999, 2001, 2003 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #include #define mapblock1 (*fsig_int1(ffi)) #define mapblock2 (*fsig_int2(ffi)) /* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ #define DEV_BSIZE 512 /* include/linux/fs.h */ #define BLOCK_SIZE 1024 /* initial block size for superblock read */ /* made up, defaults to 1 but can be passed via mount_opts */ #define WHICH_SUPER 1 /* kind of from fs/ext2/super.c */ #define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE) /* = 2 */ /* include/asm-i386/types.h */ typedef __signed__ char __s8; typedef unsigned char __u8; typedef __signed__ short __s16; typedef unsigned short __u16; typedef __signed__ int __s32; typedef unsigned int __u32; /* * Constants relative to the data blocks, from ext2_fs.h */ #define EXT2_NDIR_BLOCKS 12 #define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS #define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) /* Inode flags */ #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ /* include/linux/ext2_fs.h */ struct ext2_super_block { __u32 s_inodes_count; /* Inodes count */ __u32 s_blocks_count; /* Blocks count */ __u32 s_r_blocks_count; /* Reserved blocks count */ __u32 s_free_blocks_count; /* Free blocks count */ __u32 s_free_inodes_count; /* Free inodes count */ __u32 s_first_data_block; /* First Data Block */ __u32 s_log_block_size; /* Block size */ __s32 s_log_frag_size; /* Fragment size */ __u32 s_blocks_per_group; /* # Blocks per group */ __u32 s_frags_per_group; /* # Fragments per group */ __u32 s_inodes_per_group; /* # Inodes per group */ __u32 s_mtime; /* Mount time */ __u32 s_wtime; /* Write time */ __u16 s_mnt_count; /* Mount count */ __s16 s_max_mnt_count; /* Maximal mount count */ __u16 s_magic; /* Magic signature */ __u16 s_state; /* File system state */ __u16 s_errors; /* Behaviour when detecting errors */ __u16 s_pad; __u32 s_lastcheck; /* time of last check */ __u32 s_checkinterval; /* max. time between checks */ __u32 s_creator_os; /* OS */ __u32 s_rev_level; /* Revision level */ __u16 s_def_resuid; /* Default uid for reserved blocks */ __u16 s_def_resgid; /* Default gid for reserved blocks */ /* * These fields are for EXT2_DYNAMIC_REV superblocks only. * * Note: the difference between the compatible feature set and * the incompatible feature set is that if there is a bit set * in the incompatible feature set that the kernel doesn't * know about, it should refuse to mount the filesystem. * * e2fsck's requirements are more strict; if it doesn't know * about a feature in either the compatible or incompatible * feature set, it must abort and not try to meddle with * things it doesn't understand... */ __u32 s_first_ino; /* First non-reserved inode */ __u16 s_inode_size; /* size of inode structure */ __u16 s_block_group_nr; /* block group # of this superblock */ __u32 s_feature_compat; /* compatible feature set */ __u32 s_feature_incompat; /* incompatible feature set */ __u32 s_feature_ro_compat; /* readonly-compatible feature set */ __u8 s_uuid[16]; /* 128-bit uuid for volume */ char s_volume_name[16]; /* volume name */ char s_last_mounted[64]; /* directory where last mounted */ __u32 s_algorithm_usage_bitmap; /* For compression */ /* * Performance hints. Directory preallocation should only * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. */ __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ __u16 s_reserved_gdt_blocks;/* Per group table for online growth */ /* * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. */ __u8 s_journal_uuid[16]; /* uuid of journal superblock */ __u32 s_journal_inum; /* inode number of journal file */ __u32 s_journal_dev; /* device number of journal file */ __u32 s_last_orphan; /* start of list of inodes to delete */ __u32 s_hash_seed[4]; /* HTREE hash seed */ __u8 s_def_hash_version; /* Default hash version to use */ __u8 s_jnl_backup_type; /* Default type of journal backup */ __u16 s_reserved_word_pad; __u32 s_default_mount_opts; __u32 s_first_meta_bg; /* First metablock group */ __u32 s_mkfs_time; /* When the filesystem was created */ __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ __u32 s_reserved[172]; /* Padding to the end of the block */ }; struct ext2_group_desc { __u32 bg_block_bitmap; /* Blocks bitmap block */ __u32 bg_inode_bitmap; /* Inodes bitmap block */ __u32 bg_inode_table; /* Inodes table block */ __u16 bg_free_blocks_count; /* Free blocks count */ __u16 bg_free_inodes_count; /* Free inodes count */ __u16 bg_used_dirs_count; /* Directories count */ __u16 bg_pad; __u32 bg_reserved[3]; }; struct ext2_inode { __u16 i_mode; /* File mode */ __u16 i_uid; /* Owner Uid */ __u32 i_size; /* 4: Size in bytes */ __u32 i_atime; /* Access time */ __u32 i_ctime; /* 12: Creation time */ __u32 i_mtime; /* Modification time */ __u32 i_dtime; /* 20: Deletion Time */ __u16 i_gid; /* Group Id */ __u16 i_links_count; /* 24: Links count */ __u32 i_blocks; /* Blocks count */ __u32 i_flags; /* 32: File flags */ union { struct { __u32 l_i_reserved1; } linux1; struct { __u32 h_i_translator; } hurd1; struct { __u32 m_i_reserved1; } masix1; } osd1; /* OS dependent 1 */ __u32 i_block[EXT2_N_BLOCKS]; /* 40: Pointers to blocks */ __u32 i_version; /* File version (for NFS) */ __u32 i_file_acl; /* File ACL */ __u32 i_dir_acl; /* Directory ACL */ __u32 i_faddr; /* Fragment address */ union { struct { __u8 l_i_frag; /* Fragment number */ __u8 l_i_fsize; /* Fragment size */ __u16 i_pad1; __u32 l_i_reserved2[2]; } linux2; struct { __u8 h_i_frag; /* Fragment number */ __u8 h_i_fsize; /* Fragment size */ __u16 h_i_mode_high; __u16 h_i_uid_high; __u16 h_i_gid_high; __u32 h_i_author; } hurd2; struct { __u8 m_i_frag; /* Fragment number */ __u8 m_i_fsize; /* Fragment size */ __u16 m_pad1; __u32 m_i_reserved2[2]; } masix2; } osd2; /* OS dependent 2 */ }; /* linux/limits.h */ #define NAME_MAX 255 /* # chars in a file name */ /* linux/posix_type.h */ typedef long linux_off_t; /* linux/ext2fs.h */ #define EXT2_NAME_LEN 255 struct ext2_dir_entry { __u32 inode; /* Inode number */ __u16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type; char name[EXT2_NAME_LEN]; /* File name */ }; /* linux/ext2fs.h */ /* * EXT2_DIR_PAD defines the directory entries boundaries * * NOTE: It must be a multiple of 4 */ #define EXT2_DIR_PAD 4 #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) /* linux/ext4_fs_extents.h */ /* * This is the extent on-disk structure. * It's used at the bottom of the tree. */ struct ext4_extent { __u32 ee_block; /* first logical block extent covers */ __u16 ee_len; /* number of blocks covered by extent */ __u16 ee_start_hi; /* high 16 bits of physical block */ __u32 ee_start; /* low 32 bits of physical block */ }; /* * This is index on-disk structure. * It's used at all the levels except the bottom. */ struct ext4_extent_idx { __u32 ei_block; /* index covers logical blocks from 'block' */ __u32 ei_leaf; /* pointer to the physical block of the next * * level. leaf or next index could be there */ __u16 ei_leaf_hi; /* high 16 bits of physical block */ __u16 ei_unused; }; /* * Each block (leaves and indexes), even inode-stored has header. */ struct ext4_extent_header { __u16 eh_magic; /* probably will support different formats */ __u16 eh_entries; /* number of valid entries */ __u16 eh_max; /* capacity of store in entries */ __u16 eh_depth; /* has tree real underlying blocks? */ __u32 eh_generation; /* generation of the tree */ }; #define EXT4_EXT_MAGIC 0xf30a /* ext2/super.c */ #define log2(n) grub_log2(n) #define EXT2_SUPER_MAGIC 0xEF53 /* include/linux/ext2_fs.h */ #define EXT2_ROOT_INO 2 /* include/linux/ext2_fs.h */ #define PATH_MAX 1024 /* include/linux/limits.h */ #define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ /* made up, these are pointers into FSYS_BUF */ /* read once, always stays there: */ #define SUPERBLOCK \ ((struct ext2_super_block *)(FSYS_BUF)) #define GROUP_DESC \ ((struct ext2_group_desc *) \ ((char *)SUPERBLOCK + sizeof(struct ext2_super_block))) #define INODE \ ((struct ext2_inode *)((caddr_t)GROUP_DESC + EXT2_BLOCK_SIZE(SUPERBLOCK))) #define DATABLOCK1 \ ((char *)((caddr_t)INODE + sizeof(struct ext2_inode))) #define DATABLOCK2 \ ((char *)((caddr_t)DATABLOCK1 + EXT2_BLOCK_SIZE(SUPERBLOCK))) /* linux/ext2_fs.h */ #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) #define EXT2_ADDR_PER_BLOCK_BITS(s) (log2(EXT2_ADDR_PER_BLOCK(s))) #define EXT2_INODE_SIZE(s) (SUPERBLOCK->s_inode_size) #define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) /* linux/ext2_fs.h */ #define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) /* kind of from ext2/super.c */ #define EXT2_BLOCK_SIZE(s) (1 << EXT2_BLOCK_SIZE_BITS(s)) /* linux/ext2fs.h */ #define EXT2_DESC_PER_BLOCK(s) \ (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) /* linux/stat.h */ #define S_IFMT 00170000 #define S_IFLNK 0120000 #define S_IFREG 0100000 #define S_IFDIR 0040000 #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) /* check filesystem types and read superblock into memory buffer */ static int ext2fs_mount (fsi_file_t *ffi, const char *options) { int retval = 1; if (/*(((current_drive & 0x80) || (current_slice != 0)) && (current_slice != PC_SLICE_TYPE_EXT2FS) && (current_slice != PC_SLICE_TYPE_LINUX_RAID) && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_EXT2FS)) && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER))) || part_length < (SBLOCK + (sizeof (struct ext2_super_block) / DEV_BSIZE)) || */ !devread (ffi, SBLOCK, 0, sizeof (struct ext2_super_block), (char *) SUPERBLOCK) || SUPERBLOCK->s_magic != EXT2_SUPER_MAGIC) retval = 0; return retval; } /* Takes a file system block number and reads it into BUFFER. */ static int ext2_rdfsb (fsi_file_t *ffi, int fsblock, char *buffer) { #ifdef E2DEBUG printf ("fsblock %d buffer %d\n", fsblock, buffer); #endif /* E2DEBUG */ return devread (ffi, fsblock * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), 0, EXT2_BLOCK_SIZE (SUPERBLOCK), (char *) buffer); } /* Walk through extents index tree to find the good leaf */ static struct ext4_extent_header * ext4_recurse_extent_index(fsi_file_t *ffi, struct ext4_extent_header *extent_block, int logical_block) { int i; struct ext4_extent_idx *index = (struct ext4_extent_idx *) (extent_block + 1); if (extent_block->eh_magic != EXT4_EXT_MAGIC) return NULL; if (extent_block->eh_depth == 0) return extent_block; for (i = 0; i < extent_block->eh_entries; i++) { if (logical_block < index[i].ei_block) break; } if (i == 0 || !ext2_rdfsb(ffi, index[i-1].ei_leaf, DATABLOCK1)) return NULL; return (ext4_recurse_extent_index(ffi, (struct ext4_extent_header *) DATABLOCK1, logical_block)); } /* from ext2/inode.c:ext2_bmap() */ /* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into a physical block (the location in the file system) via an inode. */ static int ext2fs_block_map (fsi_file_t *ffi, int logical_block) { #ifdef E2DEBUG unsigned char *i; for (i = (unsigned char *) INODE; i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); i++) { printf ("%c", "0123456789abcdef"[*i >> 4]); printf ("%c", "0123456789abcdef"[*i % 16]); if (!((i + 1 - (unsigned char *) INODE) % 16)) { printf ("\n"); } else { printf (" "); } } printf ("logical block %d\n", logical_block); #endif /* E2DEBUG */ if (!(INODE->i_flags & EXT4_EXTENTS_FL)) { /* if it is directly pointed to by the inode, return that physical addr */ if (logical_block < EXT2_NDIR_BLOCKS) { #ifdef E2DEBUG printf ("returning %d\n", (unsigned char *) (INODE->i_block[logical_block])); printf ("returning %d\n", INODE->i_block[logical_block]); #endif /* E2DEBUG */ return INODE->i_block[logical_block]; } /* else */ logical_block -= EXT2_NDIR_BLOCKS; /* try the indirect block */ if (logical_block < EXT2_ADDR_PER_BLOCK (SUPERBLOCK)) { if (mapblock1 != 1 && !ext2_rdfsb (ffi, INODE->i_block[EXT2_IND_BLOCK], DATABLOCK1)) { errnum = ERR_FSYS_CORRUPT; return -1; } mapblock1 = 1; return ((__u32 *) DATABLOCK1)[logical_block]; } /* else */ logical_block -= EXT2_ADDR_PER_BLOCK (SUPERBLOCK); /* now try the double indirect block */ if (logical_block < (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2))) { int bnum; if (mapblock1 != 2 && !ext2_rdfsb (ffi, INODE->i_block[EXT2_DIND_BLOCK], DATABLOCK1)) { errnum = ERR_FSYS_CORRUPT; return -1; } mapblock1 = 2; if ((bnum = (((__u32 *) DATABLOCK1) [logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)])) != mapblock2 && !ext2_rdfsb (ffi, bnum, DATABLOCK2)) { errnum = ERR_FSYS_CORRUPT; return -1; } mapblock2 = bnum; return ((__u32 *) DATABLOCK2) [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; } /* else */ mapblock2 = -1; logical_block -= (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2)); if (mapblock1 != 3 && !ext2_rdfsb (ffi, INODE->i_block[EXT2_TIND_BLOCK], DATABLOCK1)) { errnum = ERR_FSYS_CORRUPT; return -1; } mapblock1 = 3; if (!ext2_rdfsb (ffi, ((__u32 *) DATABLOCK1) [logical_block >> (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2)], DATABLOCK2)) { errnum = ERR_FSYS_CORRUPT; return -1; } if (!ext2_rdfsb (ffi, ((__u32 *) DATABLOCK2) [(logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)) & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)], DATABLOCK2)) { errnum = ERR_FSYS_CORRUPT; return -1; } return ((__u32 *) DATABLOCK2) [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; } /* inode is in extents format */ else { int i; struct ext4_extent_header *extent_hdr = ext4_recurse_extent_index(ffi, (struct ext4_extent_header *) INODE->i_block, logical_block); struct ext4_extent *extent = (struct ext4_extent *) (extent_hdr + 1); if ( extent_hdr == NULL || extent_hdr->eh_magic != EXT4_EXT_MAGIC) { errnum = ERR_FSYS_CORRUPT; return -1; } for (i = 0; ieh_entries; i++) { if (extent[i].ee_block <= logical_block && logical_block < extent[i].ee_block + extent[i].ee_len && !(extent[i].ee_len>>15)) return (logical_block - extent[i].ee_block + extent[i].ee_start); } /* We should not arrive here */ errnum = ERR_FSYS_CORRUPT; return -1; } } /* preconditions: all preconds of ext2fs_block_map */ static int ext2fs_read (fsi_file_t *ffi, char *buf, int len) { int logical_block; int offset; int map; int ret = 0; int size = 0; #ifdef E2DEBUG static char hexdigit[] = "0123456789abcdef"; unsigned char *i; for (i = (unsigned char *) INODE; i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); i++) { printf ("%c", hexdigit[*i >> 4]); printf ("%c", hexdigit[*i % 16]); if (!((i + 1 - (unsigned char *) INODE) % 16)) { printf ("\n"); } else { printf (" "); } } #endif /* E2DEBUG */ while (len > 0) { /* find the (logical) block component of our location */ logical_block = filepos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); offset = filepos & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); map = ext2fs_block_map (ffi, logical_block); #ifdef E2DEBUG printf ("map=%d\n", map); #endif /* E2DEBUG */ if (map < 0) break; size = EXT2_BLOCK_SIZE (SUPERBLOCK); size -= offset; if (size > len) size = len; if (map == 0) { memset ((char *) buf, 0, size); } else { disk_read_func = disk_read_hook; devread (ffi, map * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), offset, size, buf); disk_read_func = NULL; } buf += size; len -= size; filepos += size; ret += size; } if (errnum) ret = 0; return ret; } /* Based on: def_blk_fops points to blkdev_open, which calls (I think): sys_open() do_open() open_namei() dir_namei() which accesses current->fs->root fs->root was set during original mount: (something)... which calls (I think): ext2_read_super() iget() __iget() read_inode() ext2_read_inode() uses desc_per_block_bits, which is set in ext2_read_super() also uses group descriptors loaded during ext2_read_super() lookup() ext2_lookup() ext2_find_entry() ext2_getblk() */ static inline int ext2_is_fast_symlink (fsi_file_t *ffi) { int ea_blocks; ea_blocks = INODE->i_file_acl ? EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE : 0; return INODE->i_blocks == ea_blocks; } /* preconditions: ext2fs_mount already executed, therefore supblk in buffer * known as SUPERBLOCK * returns: 0 if error, nonzero iff we were able to find the file successfully * postconditions: on a nonzero return, buffer known as INODE contains the * inode of the file we were trying to look up * side effects: messes up GROUP_DESC buffer area */ static int ext2fs_dir (fsi_file_t *ffi, char *dirname) { int current_ino = EXT2_ROOT_INO; /* start at the root */ int updir_ino = current_ino; /* the parent of the current directory */ int group_id; /* which group the inode is in */ int group_desc; /* fs pointer to that group */ int desc; /* index within that group */ int ino_blk; /* fs pointer of the inode's information */ int str_chk = 0; /* used to hold the results of a string compare */ struct ext2_group_desc *gdp; struct ext2_inode *raw_inode; /* inode info corresponding to current_ino */ char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ int link_count = 0; char *rest; char ch; /* temp char holder */ int off; /* offset within block of directory entry (off mod blocksize) */ int loc; /* location within a directory */ int blk; /* which data blk within dir entry (off div blocksize) */ int inodes_per_block; /* number of inodes in each block */ int inode_offset; /* inode offset in block */ long map; /* fs pointer of a particular block from dir entry */ struct ext2_dir_entry *dp; /* pointer to directory entry */ #ifdef E2DEBUG unsigned char *i; #endif /* E2DEBUG */ /* loop invariants: current_ino = inode to lookup dirname = pointer to filename component we are cur looking up within the directory known pointed to by current_ino (if any) */ while (1) { #ifdef E2DEBUG printf ("inode %d\n", current_ino); printf ("dirname=%s\n", dirname); #endif /* E2DEBUG */ /* look up an inode */ group_id = (current_ino - 1) / (SUPERBLOCK->s_inodes_per_group); group_desc = group_id >> log2 (EXT2_DESC_PER_BLOCK (SUPERBLOCK)); desc = group_id & (EXT2_DESC_PER_BLOCK (SUPERBLOCK) - 1); #ifdef E2DEBUG printf ("ipg=%d, dpb=%d\n", SUPERBLOCK->s_inodes_per_group, EXT2_DESC_PER_BLOCK (SUPERBLOCK)); printf ("group_id=%d group_desc=%d desc=%d\n", group_id, group_desc, desc); #endif /* E2DEBUG */ if (!ext2_rdfsb (ffi, (WHICH_SUPER + group_desc + SUPERBLOCK->s_first_data_block), (char *)GROUP_DESC)) { return 0; } gdp = GROUP_DESC; inodes_per_block = EXT2_BLOCK_SIZE (SUPERBLOCK) / EXT2_INODE_SIZE(SUPERBLOCK); inode_offset = ((current_ino - 1) % (SUPERBLOCK->s_inodes_per_group)); ino_blk = gdp[desc].bg_inode_table + (inode_offset / inodes_per_block); #ifdef E2DEBUG printf ("inode table fsblock=%d\n", ino_blk); #endif /* E2DEBUG */ if (!ext2_rdfsb (ffi, ino_blk, (char *)INODE)) { return 0; } /* reset indirect blocks! */ mapblock2 = mapblock1 = -1; raw_inode = (struct ext2_inode *)((char *)INODE + ((current_ino - 1) & (EXT2_INODES_PER_BLOCK (SUPERBLOCK) - 1)) * EXT2_INODE_SIZE (SUPERBLOCK)); #ifdef E2DEBUG printf ("ipb=%d, sizeof(inode)=%d\n", EXT2_INODES_PER_BLOCK (SUPERBLOCK), EXT2_INODE_SIZE (SUPERBLOCK)); printf ("inode=%x, raw_inode=%x\n", INODE, raw_inode); printf ("offset into inode table block=%d\n", (int) raw_inode - (int) INODE); for (i = (unsigned char *) INODE; i <= (unsigned char *) raw_inode; i++) { printf ("%c", "0123456789abcdef"[*i >> 4]); printf ("%c", "0123456789abcdef"[*i % 16]); if (!((i + 1 - (unsigned char *) INODE) % 16)) { printf ("\n"); } else { printf (" "); } } printf ("first word=%x\n", *((int *) raw_inode)); #endif /* E2DEBUG */ /* copy inode to fixed location */ memmove ((void *) INODE, (void *) raw_inode, sizeof (struct ext2_inode)); #ifdef E2DEBUG printf ("first word=%x\n", *((int *) INODE)); #endif /* E2DEBUG */ /* If we've got a symbolic link, then chase it. */ if (S_ISLNK (INODE->i_mode)) { int len; if (++link_count > MAX_LINK_COUNT) { errnum = ERR_SYMLINK_LOOP; return 0; } /* Find out how long our remaining name is. */ len = 0; while (dirname[len] && !isspace ((uint8_t)dirname[len])) len++; /* Get the symlink size. */ filemax = (INODE->i_size); if (filemax + len > sizeof (linkbuf) - 2) { errnum = ERR_FILELENGTH; return 0; } if (len) { /* Copy the remaining name to the end of the symlink data. Note that DIRNAME and LINKBUF may overlap! */ memmove (linkbuf + filemax, dirname, len); } linkbuf[filemax + len] = '\0'; /* Read the symlink data. */ if (! ext2_is_fast_symlink (ffi)) { /* Read the necessary blocks, and reset the file pointer. */ len = ext2fs_read (ffi, linkbuf, filemax); filepos = 0; if (!len) return 0; } else { /* Copy the data directly from the inode. */ len = filemax; memmove (linkbuf, (char *) INODE->i_block, len); } #ifdef E2DEBUG printf ("symlink=%s\n", linkbuf); #endif dirname = linkbuf; if (*dirname == '/') { /* It's an absolute link, so look it up in root. */ current_ino = EXT2_ROOT_INO; updir_ino = current_ino; } else { /* Relative, so look it up in our parent directory. */ current_ino = updir_ino; } /* Try again using the new name. */ continue; } /* if end of filename, INODE points to the file's inode */ if (!*dirname || isspace ((uint8_t)*dirname)) { if (!S_ISREG (INODE->i_mode)) { errnum = ERR_BAD_FILETYPE; return 0; } filemax = (INODE->i_size); return 1; } /* else we have to traverse a directory */ updir_ino = current_ino; /* skip over slashes */ while (*dirname == '/') dirname++; /* if this isn't a directory of sufficient size to hold our file, abort */ if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode)) { errnum = ERR_BAD_FILETYPE; return 0; } /* skip to next slash or end of filename (space) */ for (rest = dirname; (ch = *rest) && !isspace ((uint8_t)ch) && ch != '/'; rest++); /* look through this directory and find the next filename component */ /* invariant: rest points to slash after the next filename component */ *rest = 0; loc = 0; do { #ifdef E2DEBUG printf ("dirname=%s, rest=%s, loc=%d\n", dirname, rest, loc); #endif /* E2DEBUG */ /* if our location/byte offset into the directory exceeds the size, give up */ if (loc >= INODE->i_size) { if (print_possibilities < 0) { # if 0 putchar ('\n'); # endif } else { errnum = ERR_FILE_NOT_FOUND; *rest = ch; } return (print_possibilities < 0); } /* else, find the (logical) block component of our location */ blk = loc >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); /* we know which logical block of the directory entry we are looking for, now we have to translate that to the physical (fs) block on the disk */ map = ext2fs_block_map (ffi, blk); #ifdef E2DEBUG printf ("fs block=%d\n", map); #endif /* E2DEBUG */ mapblock2 = -1; if ((map < 0) || !ext2_rdfsb (ffi, map, DATABLOCK2)) { errnum = ERR_FSYS_CORRUPT; *rest = ch; return 0; } off = loc & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); dp = (struct ext2_dir_entry *) (DATABLOCK2 + off); /* advance loc prematurely to next on-disk directory entry */ loc += dp->rec_len; /* NOTE: ext2fs filenames are NOT null-terminated */ #ifdef E2DEBUG printf ("directory entry ino=%d\n", dp->inode); if (dp->inode) printf ("entry=%s\n", dp->name); #endif /* E2DEBUG */ if (dp->inode) { int saved_c = dp->name[dp->name_len]; dp->name[dp->name_len] = 0; str_chk = substring (dirname, dp->name); # ifndef STAGE1_5 if (print_possibilities && ch != '/' && (!*dirname || str_chk <= 0)) { if (print_possibilities > 0) print_possibilities = -print_possibilities; print_a_completion (dp->name); } # endif dp->name[dp->name_len] = saved_c; } } while (!dp->inode || (str_chk || (print_possibilities && ch != '/'))); current_ino = dp->inode; *(dirname = rest) = ch; } /* never get here */ } fsi_plugin_ops_t * fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) { static fsig_plugin_ops_t ops = { FSIMAGE_PLUGIN_VERSION, .fpo_mount = ext2fs_mount, .fpo_dir = ext2fs_dir, .fpo_read = ext2fs_read }; *name = "ext2fs"; return (fsig_init(fp, &ops)); } xen-4.9.2/tools/libfsimage/Makefile0000664000175000017500000000050213256712137015354 0ustar smbsmbXEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk SUBDIRS-y = common ufs reiserfs iso9660 fat zfs SUBDIRS-$(CONFIG_X86) += xfs ifneq ($(EXTFS_LIBS), ) SUBDIRS-y += ext2fs-lib else SUBDIRS-y += ext2fs endif .PHONY: all clean install all clean install: %: subdirs-% .PHONY: distclean distclean: clean xen-4.9.2/tools/libfsimage/ext2fs-lib/0000775000175000017500000000000013256712137015676 5ustar smbsmbxen-4.9.2/tools/libfsimage/ext2fs-lib/ext2fs-lib.c0000664000175000017500000000730113256712137020022 0ustar smbsmb/* * 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. * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include INCLUDE_EXTFS_H #include #include static int ext2lib_mount(fsi_t *fsi, const char *name, const char *options) { int err; char opts[30] = ""; ext2_filsys *fs; uint64_t offset = fsip_fs_offset(fsi); if (offset) snprintf(opts, 29, "offset=%" PRId64, offset); fs = malloc(sizeof (*fs)); if (fs == NULL) return (-1); err = ext2fs_open2(name, opts, 0, 0, 0, unix_io_manager, fs); if (err != 0) { free(fs); errno = EINVAL; return (-1); } fsip_fs_set_data(fsi, fs); return (0); } static int ext2lib_umount(fsi_t *fsi) { ext2_filsys *fs = fsip_fs_data(fsi); if (ext2fs_close(*fs) != 0) { free(fs); errno = EINVAL; return (-1); } free(fs); return (0); } fsi_file_t * ext2lib_open(fsi_t *fsi, const char *path) { ext2_ino_t ino; ext2_filsys *fs = fsip_fs_data(fsi); ext2_file_t *f; fsi_file_t *file; int err; err = ext2fs_namei_follow(*fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino); if (err != 0) { errno = ENOENT; return (NULL); } f = malloc(sizeof (*f)); if (f == NULL) return (NULL); err = ext2fs_file_open(*fs, ino, 0, f); if (err != 0) { free(f); errno = EINVAL; return (NULL); } file = fsip_file_alloc(fsi, f); if (file == NULL) free(f); return (file); } ssize_t ext2lib_read(fsi_file_t *file, void *buf, size_t nbytes) { ext2_file_t *f = fsip_file_data(file); unsigned int n; int err; err = ext2fs_file_read(*f, buf, nbytes, &n); if (err != 0) { errno = EINVAL; return (-1); } return (n); } ssize_t ext2lib_pread(fsi_file_t *file, void *buf, size_t nbytes, uint64_t off) { ext2_file_t *f = fsip_file_data(file); __u64 tmpoff; unsigned int n; int err; if ((err = ext2fs_file_llseek(*f, 0, EXT2_SEEK_CUR, &tmpoff)) != 0) { errno = EINVAL; return (-1); } if ((err = ext2fs_file_llseek(*f, off, EXT2_SEEK_SET, NULL)) != 0) { errno = EINVAL; return (-1); } err = ext2fs_file_read(*f, buf, nbytes, &n); ext2fs_file_llseek(*f, tmpoff, EXT2_SEEK_SET, NULL); if (err != 0) { errno = EINVAL; return (-1); } return (n); } int ext2lib_close(fsi_file_t *file) { ext2_file_t *f = fsip_file_data(file); ext2fs_file_close(*f); free(f); return (0); } fsi_plugin_ops_t * fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) { static fsi_plugin_ops_t ops = { FSIMAGE_PLUGIN_VERSION, .fpo_mount = ext2lib_mount, .fpo_umount = ext2lib_umount, .fpo_open = ext2lib_open, .fpo_read = ext2lib_read, .fpo_pread = ext2lib_pread, .fpo_close = ext2lib_close }; *name = "ext2fs-lib"; return (&ops); } xen-4.9.2/tools/libfsimage/ext2fs-lib/Makefile0000664000175000017500000000045013256712137017335 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. LIB_SRCS-y = ext2fs-lib.c FS = ext2fs-lib FS_LIBDEPS = $(EXTFS_LIBS) # Include configure output (config.h) CFLAGS += -include $(XEN_ROOT)/tools/config.h .PHONY: all all: fs-all .PHONY: install install: fs-install include $(XEN_ROOT)/tools/libfsimage/Rules.mk xen-4.9.2/tools/libfsimage/iso9660/0000775000175000017500000000000013256712137015036 5ustar smbsmbxen-4.9.2/tools/libfsimage/iso9660/Makefile0000664000175000017500000000032113256712137016472 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. LIB_SRCS-y = fsys_iso9660.c FS = iso9660 .PHONY: all all: fs-all .PHONY: install install: fs-install fsys_iso9660.c: iso9660.h include $(XEN_ROOT)/tools/libfsimage/Rules.mk xen-4.9.2/tools/libfsimage/iso9660/fsys_iso9660.c0000664000175000017500000002640113256712137017370 0ustar smbsmb/* * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) * including Rock Ridge Extensions support * * Copyright (C) 1998, 1999 Kousuke Takai * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * References: * linux/fs/isofs/rock.[ch] * mkisofs-1.11.1/diag/isoinfo.c * mkisofs-1.11.1/iso9660.h * (all are written by Eric Youngdale) * * Modifications by: * Leonid Lisovskiy 2003 */ #include #include #include "iso9660.h" #define MAXINT INT_MAX /* iso9660 super-block data in memory */ struct iso_sb_info { unsigned long vol_sector; }; /* iso fs inode data in memory */ struct iso_inode_info { unsigned long file_start; }; #define ISO_SUPER \ ((struct iso_sb_info *)(FSYS_BUF)) #define INODE \ ((struct iso_inode_info *)(FSYS_BUF+sizeof(struct iso_sb_info))) #define PRIMDESC ((struct iso_primary_descriptor *)(FSYS_BUF + 2048)) #define DIRREC ((struct iso_directory_record *)(FSYS_BUF + 4096)) #define RRCONT_BUF ((unsigned char *)(FSYS_BUF + 6144)) #define NAME_BUF ((unsigned char *)(FSYS_BUF + 8192)) #define log2 grub_log2 static int iso9660_devread (fsi_file_t *ffi, int sector, int byte_offset, int byte_len, char *buf) { static int read_count = 0, threshold = 2; unsigned short sector_size_lg2 = log2(512 /*buf_geom.sector_size*/); /* * We have to use own devread() function since BIOS return wrong geometry */ if (sector < 0) { errnum = ERR_OUTSIDE_PART; return 0; } if (byte_len <= 0) return 1; #if 0 sector += (byte_offset >> sector_size_lg2); byte_offset &= (buf_geom.sector_size - 1); asm volatile ("shl%L0 %1,%0" : "=r"(sector) : "Ic"((int8_t)(ISO_SECTOR_BITS - sector_size_lg2)), "0"(sector)); #else sector = (sector * 4) + (byte_offset >> sector_size_lg2); byte_offset &= 511; #endif #if !defined(STAGE1_5) if (disk_read_hook && debug) printf ("<%d, %d, %d>", sector, byte_offset, byte_len); #endif /* !STAGE1_5 */ read_count += (byte_len >> 9); if ((read_count >> 11) > threshold) { noisy_printf("."); threshold += 2; /* one dot every 2 MB */ } return devread(ffi, sector, byte_offset, byte_len, buf); } static int iso9660_mount (fsi_file_t *ffi, const char *options) { unsigned int sector; /* * Because there is no defined slice type ID for ISO-9660 filesystem, * this test will pass only either (1) if entire disk is used, or * (2) if current partition is BSD style sub-partition whose ID is * ISO-9660. */ #if 0 if ((current_partition != 0xFFFFFF) && !IS_PC_SLICE_TYPE_BSD_WITH_FS(current_slice, FS_ISO9660)) return 0; #endif /* * Currently, only FIRST session of MultiSession disks are supported !!! */ for (sector = 16 ; sector < 32 ; sector++) { if (!iso9660_devread(ffi, sector, 0, sizeof(*PRIMDESC), (char *)PRIMDESC)) break; /* check ISO_VD_PRIMARY and ISO_STANDARD_ID */ if (PRIMDESC->type.l == ISO_VD_PRIMARY && !memcmp(PRIMDESC->id, ISO_STANDARD_ID, sizeof(PRIMDESC->id))) { ISO_SUPER->vol_sector = sector; INODE->file_start = 0; #if 0 fsmax = PRIMDESC->volume_space_size.l; #endif return 1; } } return 0; } static int iso9660_dir (fsi_file_t *ffi, char *dirname) { struct iso_directory_record *idr; RR_ptr_t rr_ptr; struct rock_ridge *ce_ptr; unsigned int pathlen; int size; unsigned int extent; unsigned char file_type; unsigned int rr_len; unsigned char rr_flag; idr = &PRIMDESC->root_directory_record; INODE->file_start = 0; do { while (*dirname == '/') /* skip leading slashes */ dirname++; /* pathlen = strcspn(dirname, "/\n\t "); */ for (pathlen = 0 ; dirname[pathlen] && !isspace((uint8_t)dirname[pathlen]) && dirname[pathlen] != '/' ; pathlen++) ; size = idr->size.l; extent = idr->extent.l; while (size > 0) { if (!iso9660_devread(ffi, extent, 0, ISO_SECTOR_SIZE, (char *)DIRREC)) { errnum = ERR_FSYS_CORRUPT; return 0; } extent++; idr = (struct iso_directory_record *)DIRREC; for (; idr->length.l > 0; idr = (struct iso_directory_record *)((char *)idr + idr->length.l) ) { const char *name = (const char *)idr->name; unsigned int name_len = idr->name_len.l; file_type = (idr->flags.l & 2) ? ISO_DIRECTORY : ISO_REGULAR; if (name_len == 1) { if ((name[0] == 0) || /* self */ (name[0] == 1)) /* parent */ continue; } if (name_len > 2 && CHECK2(name + name_len - 2, ';', '1')) { name_len -= 2; /* truncate trailing file version */ if (name_len > 1 && name[name_len - 1] == '.') name_len--; /* truncate trailing dot */ } /* * Parse Rock-Ridge extension */ rr_len = (idr->length.l - idr->name_len.l - sizeof(struct iso_directory_record) + sizeof(idr->name)); rr_ptr.ptr = ((char *)idr + idr->name_len.l + sizeof(struct iso_directory_record) - sizeof(idr->name)); if (rr_ptr.i & 1) rr_ptr.i++, rr_len--; ce_ptr = NULL; rr_flag = RR_FLAG_NM | RR_FLAG_PX /*| RR_FLAG_SL*/; while (rr_len >= 4) { if (rr_ptr.rr->version != 1) { #ifndef STAGE1_5 if (debug) printf( "Non-supported version (%d) RockRidge chunk " "`%c%c'\n", rr_ptr.rr->version, rr_ptr.rr->signature & 0xFF, rr_ptr.rr->signature >> 8); #endif } else { switch (rr_ptr.rr->signature) { case RRMAGIC('R', 'R'): if ( rr_ptr.rr->len >= (4+sizeof(struct RR))) rr_flag &= rr_ptr.rr->u.rr.flags.l; break; case RRMAGIC('N', 'M'): name = (const char *)rr_ptr.rr->u.nm.name; name_len = rr_ptr.rr->len - (4+sizeof(struct NM)); rr_flag &= ~RR_FLAG_NM; break; case RRMAGIC('P', 'X'): if (rr_ptr.rr->len >= (4+sizeof(struct PX))) { file_type = ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) == POSIX_S_IFREG ? ISO_REGULAR : ((rr_ptr.rr->u.px.mode.l & POSIX_S_IFMT) == POSIX_S_IFDIR ? ISO_DIRECTORY : ISO_OTHER)); rr_flag &= ~RR_FLAG_PX; } break; case RRMAGIC('C', 'E'): if (rr_ptr.rr->len >= (4+sizeof(struct CE))) ce_ptr = rr_ptr.rr; break; #if 0 // RockRidge symlinks are not supported yet case RRMAGIC('S', 'L'): { int slen; unsigned char rootflag, prevflag; char *rpnt = NAME_BUF+1024; struct SL_component *slp; slen = rr_ptr.rr->len - (4+1); slp = &rr_ptr.rr->u.sl.link; while (slen > 1) { rootflag = 0; switch (slp->flags.l) { case 0: memcpy(rpnt, slp->text, slp->len); rpnt += slp->len; break; case 4: *rpnt++ = '.'; /* fallthru */ case 2: *rpnt++ = '.'; break; case 8: rootflag = 1; *rpnt++ = '/'; break; default: printf("Symlink component flag not implemented (%d)\n", slp->flags.l); slen = 0; break; } slen -= slp->len + 2; prevflag = slp->flags.l; slp = (struct SL_component *) ((char *) slp + slp->len + 2); if (slen < 2) { /* * If there is another SL record, and this component * record isn't continued, then add a slash. */ if ((!rootflag) && (rr_ptr.rr->u.sl.flags.l & 1) && !(prevflag & 1)) *rpnt++='/'; break; } /* * If this component record isn't continued, then append a '/'. */ if (!rootflag && !(prevflag & 1)) *rpnt++ = '/'; } *rpnt++ = '\0'; grub_putstr(NAME_BUF+1024);// debug print! } rr_flag &= ~RR_FLAG_SL; break; #endif default: break; } } if (!rr_flag) /* * There is no more extension we expects... */ break; rr_len -= rr_ptr.rr->len; rr_ptr.ptr += rr_ptr.rr->len; if (rr_len < 4 && ce_ptr != NULL) { /* preserve name before loading new extent. */ if( RRCONT_BUF <= (unsigned char *)name && (unsigned char *)name < RRCONT_BUF + ISO_SECTOR_SIZE ) { memcpy(NAME_BUF, name, name_len); name = (const char *)NAME_BUF; } rr_ptr.ptr = (char *)RRCONT_BUF + ce_ptr->u.ce.offset.l; rr_len = ce_ptr->u.ce.size.l; if (!iso9660_devread(ffi, ce_ptr->u.ce.extent.l, 0, ISO_SECTOR_SIZE, (char *)RRCONT_BUF)) { errnum = 0; /* this is not fatal. */ break; } ce_ptr = NULL; } } /* rr_len >= 4 */ filemax = MAXINT; if (name_len >= pathlen && !memcmp(name, dirname, pathlen)) { if (dirname[pathlen] == '/' || !print_possibilities) { /* * DIRNAME is directory component of pathname, * or we are to open a file. */ if (pathlen == name_len) { if (dirname[pathlen] == '/') { if (file_type != ISO_DIRECTORY) { errnum = ERR_BAD_FILETYPE; return 0; } goto next_dir_level; } if (file_type != ISO_REGULAR) { errnum = ERR_BAD_FILETYPE; return 0; } INODE->file_start = idr->extent.l; filepos = 0; filemax = idr->size.l; return 1; } } else /* Completion */ { #ifndef STAGE1_5 if (print_possibilities > 0) print_possibilities = -print_possibilities; memcpy(NAME_BUF, name, name_len); NAME_BUF[name_len] = '\0'; print_a_completion (NAME_BUF); #endif } } } /* for */ size -= ISO_SECTOR_SIZE; } /* size>0 */ if (dirname[pathlen] == '/' || print_possibilities >= 0) { errnum = ERR_FILE_NOT_FOUND; return 0; } next_dir_level: dirname += pathlen; } while (*dirname == '/'); return 1; } static int iso9660_read (fsi_file_t *ffi, char *buf, int len) { int sector, blkoffset, size, ret; if (INODE->file_start == 0) return 0; ret = 0; blkoffset = filepos & (ISO_SECTOR_SIZE - 1); sector = filepos >> ISO_SECTOR_BITS; while (len > 0) { size = ISO_SECTOR_SIZE - blkoffset; if (size > len) size = len; disk_read_func = disk_read_hook; if (!iso9660_devread(ffi, INODE->file_start + sector, blkoffset, size, buf)) return 0; disk_read_func = NULL; len -= size; buf += size; ret += size; filepos += size; sector++; blkoffset = 0; } return ret; } fsi_plugin_ops_t * fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) { static fsig_plugin_ops_t ops = { FSIMAGE_PLUGIN_VERSION, .fpo_mount = iso9660_mount, .fpo_dir = iso9660_dir, .fpo_read = iso9660_read }; *name = "iso9660"; return (fsig_init(fp, &ops)); } xen-4.9.2/tools/libfsimage/iso9660/iso9660.h0000664000175000017500000001275713256712137016342 0ustar smbsmb/* * ISO 9660 filesystem backend for GRUB (GRand Unified Bootloader) * including Rock Ridge Extensions support * * Copyright (C) 1998, 1999 Kousuke Takai * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * References: * linux/fs/isofs/rock.[ch] * mkisofs-1.11.1/diag/isoinfo.c * mkisofs-1.11.1/iso9660.h * (all are written by Eric Youngdale) */ #ifndef _ISO9660_H_ #define _ISO9660_H_ #define ISO_SECTOR_BITS (11) #define ISO_SECTOR_SIZE (1< #include "fsimage.h" #define FSIMAGE_PLUGIN_VERSION 1 typedef struct fsi_plugin fsi_plugin_t; typedef struct fsi_plugin_ops { int fpo_version; int (*fpo_mount)(fsi_t *, const char *, const char *); int (*fpo_umount)(fsi_t *); fsi_file_t *(*fpo_open)(fsi_t *, const char *); ssize_t (*fpo_read)(fsi_file_t *, void *, size_t); ssize_t (*fpo_pread)(fsi_file_t *, void *, size_t, uint64_t); int (*fpo_close)(fsi_file_t *); } fsi_plugin_ops_t; typedef fsi_plugin_ops_t * (*fsi_plugin_init_t)(int, fsi_plugin_t *, const char **); void fsip_fs_set_data(fsi_t *, void *); fsi_file_t *fsip_file_alloc(fsi_t *, void *); void fsip_file_free(fsi_file_t *); fsi_t *fsip_fs(fsi_file_t *); uint64_t fsip_fs_offset(fsi_t *); void *fsip_fs_data(fsi_t *); void *fsip_file_data(fsi_file_t *); #ifdef __cplusplus }; #endif #endif /* _FSIMAGE_PLUGIN_H */ xen-4.9.2/tools/libfsimage/common/fsimage_priv.h0000664000175000017500000000333013256712137020032 0ustar smbsmb/* * 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. * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FSIMAGE_PRIV_H #define _FSIMAGE_PRIV_H #ifdef __cplusplus extern C { #endif #include #include "fsimage.h" #include "fsimage_plugin.h" struct fsi_plugin { const char *fp_name; void *fp_dlh; fsi_plugin_ops_t *fp_ops; struct fsi_plugin *fp_next; void *fp_data; }; struct fsi { int f_fd; uint64_t f_off; void *f_data; fsi_plugin_t *f_plugin; char *f_bootstring; }; struct fsi_file { fsi_t *ff_fsi; void *ff_data; }; int find_plugin(fsi_t *, const char *, const char *); #ifdef __cplusplus }; #endif #endif /* _FSIMAGE_PRIV_H */ xen-4.9.2/tools/libfsimage/common/mapfile-GNU0000664000175000017500000000115713256712137017202 0ustar smbsmbVERSION { libfsimage.so.1.0 { global: fsi_open_fsimage; fsi_close_fsimage; fsi_file_exists; fsi_open_file; fsi_close_file; fsi_read_file; fsi_pread_file; fsi_bootstring_alloc; fsi_bootstring_free; fsi_fs_bootstring; fsip_fs_set_data; fsip_file_alloc; fsip_file_free; fsip_fs; fsip_fs_offset; fsip_fs_data; fsip_file_data; fsig_init; fsig_devread; fsig_substring; fsig_log2; fsig_fs_buf; fsig_file_alloc; fsig_file_buf; fsig_filepos; fsig_filemax; fsig_int1; fsig_int2; fsig_errnum; fsig_disk_read_junk; local: *; }; } xen-4.9.2/tools/libfsimage/common/Makefile0000664000175000017500000000244513256712137016654 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. include $(XEN_ROOT)/tools/libfsimage/Rules.mk MAJOR = 1.0 MINOR = 0 LDFLAGS-$(CONFIG_SunOS) = -Wl,-M -Wl,mapfile-SunOS LDFLAGS-$(CONFIG_Linux) = -Wl,mapfile-GNU LDFLAGS = $(LDFLAGS-y) CFLAGS += $(PTHREAD_CFLAGS) LDFLAGS += $(PTHREAD_LDFLAGS) LIB_SRCS-y = fsimage.c fsimage_plugin.c fsimage_grub.c PIC_OBJS := $(patsubst %.c,%.opic,$(LIB_SRCS-y)) LIB = libfsimage.so libfsimage.so.$(MAJOR) libfsimage.so.$(MAJOR).$(MINOR) .PHONY: all all: $(LIB) .PHONY: install install: all $(INSTALL_DIR) $(DESTDIR)$(libdir) $(INSTALL_DIR) $(DESTDIR)$(includedir) $(INSTALL_PROG) libfsimage.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir) ln -sf libfsimage.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libfsimage.so.$(MAJOR) ln -sf libfsimage.so.$(MAJOR) $(DESTDIR)$(libdir)/libfsimage.so $(INSTALL_DATA) fsimage.h $(DESTDIR)$(includedir) $(INSTALL_DATA) fsimage_plugin.h $(DESTDIR)$(includedir) $(INSTALL_DATA) fsimage_grub.h $(DESTDIR)$(includedir) clean distclean:: rm -f $(LIB) libfsimage.so: libfsimage.so.$(MAJOR) ln -sf $< $@ libfsimage.so.$(MAJOR): libfsimage.so.$(MAJOR).$(MINOR) ln -sf $< $@ libfsimage.so.$(MAJOR).$(MINOR): $(PIC_OBJS) $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libfsimage.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(PTHREAD_LIBS) $(APPEND_LDFLAGS) -include $(DEPS) xen-4.9.2/tools/libfsimage/common/mapfile-SunOS0000664000175000017500000000107513256712137017557 0ustar smbsmblibfsimage.so.1.0 { global: fsi_open_fsimage; fsi_close_fsimage; fsi_file_exists; fsi_open_file; fsi_close_file; fsi_read_file; fsi_pread_file; fsi_bootstring_alloc; fsi_bootstring_free; fsi_fs_bootstring; fsip_fs_set_data; fsip_file_alloc; fsip_file_free; fsip_fs; fsip_fs_data; fsip_fs_offset; fsip_file_data; fsig_init; fsig_devread; fsig_substring; fsig_log2; fsig_fs_buf; fsig_file_alloc; fsig_file_buf; fsig_filepos; fsig_filemax; fsig_int1; fsig_int2; fsig_errnum; fsig_disk_read_junk; local: *; }; xen-4.9.2/tools/libfsimage/common/fsimage_grub.h0000664000175000017500000000604413256712137020016 0ustar smbsmb/* * 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. * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FSIMAGE_GRUB_H #define _FSIMAGE_GRUB_H #ifdef __cplusplus extern C { #endif #include #include #include #include #include "fsimage.h" #include "fsimage_plugin.h" typedef struct fsig_plugin_ops { int fpo_version; int (*fpo_mount)(fsi_file_t *, const char *); int (*fpo_dir)(fsi_file_t *, char *); int (*fpo_read)(fsi_file_t *, char *, int); } fsig_plugin_ops_t; #define STAGE1_5 #define FSYS_BUFLEN 0x40000 #define SECTOR_BITS 9 #define SECTOR_SIZE 0x200 #define FSYS_BUF (fsig_file_buf(ffi)) #define filepos (*fsig_filepos(ffi)) #define filemax (*fsig_filemax(ffi)) #define devread fsig_devread #define substring fsig_substring #define errnum (*fsig_errnum(ffi)) #define disk_read_func (*fsig_disk_read_junk()) #define disk_read_hook (*fsig_disk_read_junk()) #define print_possibilities 0 #define noisy_printf(fmt...) #define grub_memset memset #define grub_memmove memmove #define grub_log2 fsig_log2 extern char **fsig_disk_read_junk(void); unsigned long fsig_log2(unsigned long); #define ERR_FSYS_CORRUPT 1 #define ERR_OUTSIDE_PART 1 #define ERR_SYMLINK_LOOP 1 #define ERR_FILELENGTH 1 #define ERR_BAD_FILETYPE 1 #define ERR_FILE_NOT_FOUND 1 #define ERR_BAD_ARGUMENT 1 #define ERR_FILESYSTEM_NOT_FOUND 1 #define ERR_NO_BOOTPATH 1 #define ERR_DEV_VALUES 1 #define ERR_WONT_FIT 1 #define ERR_READ 1 #define ERR_NEWER_VERSION 1 fsi_plugin_ops_t *fsig_init(fsi_plugin_t *, fsig_plugin_ops_t *); int fsig_devread(fsi_file_t *, unsigned int, unsigned int, unsigned int, char *); int fsig_substring(const char *, const char *); void *fsig_fs_buf(fsi_t *); fsi_file_t *fsig_file_alloc(fsi_t *); void *fsig_file_buf(fsi_file_t *); uint64_t *fsig_filepos(fsi_file_t *); uint64_t *fsig_filemax(fsi_file_t *); int *fsig_int1(fsi_file_t *); int *fsig_int2(fsi_file_t *); int *fsig_errnum(fsi_file_t *); #ifdef __cplusplus }; #endif #endif /* _FSIMAGE_GRUB_H */ xen-4.9.2/tools/libfsimage/common/fsimage_grub.c0000664000175000017500000001530513256712137020011 0ustar smbsmb/* * 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. * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef __sun__ #define _XOPEN_SOURCE 500 #endif #include #include #include #include "fsimage_grub.h" #include "fsimage_priv.h" static char *disk_read_junk; typedef struct fsig_data { char fd_buf[FSYS_BUFLEN]; } fsig_data_t; typedef struct fsig_file_data { char ffd_buf[FSYS_BUFLEN]; uint64_t ffd_curpos; uint64_t ffd_filepos; uint64_t ffd_filemax; int ffd_int1; int ffd_int2; int ffd_errnum; } fsig_file_data_t; fsi_file_t * fsig_file_alloc(fsi_t *fsi) { fsi_file_t *ffi; fsig_file_data_t *data = malloc(sizeof (fsig_file_data_t)); if (data == NULL) return (NULL); bzero(data, sizeof (fsig_file_data_t)); bcopy(fsig_fs_buf(fsi), data->ffd_buf, FSYS_BUFLEN); if ((ffi = fsip_file_alloc(fsi, data)) == NULL) { free(data); return (NULL); } return (ffi); } void * fsig_fs_buf(fsi_t *fsi) { fsig_data_t *data = fsip_fs_data(fsi); return ((void *)data->fd_buf); } void * fsig_file_buf(fsi_file_t *ffi) { fsig_file_data_t *data = fsip_file_data(ffi); return ((void *)data->ffd_buf); } uint64_t * fsig_filepos(fsi_file_t *ffi) { fsig_file_data_t *data = fsip_file_data(ffi); return (&data->ffd_filepos); } uint64_t * fsig_filemax(fsi_file_t *ffi) { fsig_file_data_t *data = fsip_file_data(ffi); return (&data->ffd_filemax); } int * fsig_int1(fsi_file_t *ffi) { fsig_file_data_t *data = fsip_file_data(ffi); return (&data->ffd_int1); } int * fsig_int2(fsi_file_t *ffi) { fsig_file_data_t *data = fsip_file_data(ffi); return (&data->ffd_int2); } int * fsig_errnum(fsi_file_t *ffi) { fsig_file_data_t *data = fsip_file_data(ffi); return (&data->ffd_errnum); } char ** fsig_disk_read_junk(void) { return (&disk_read_junk); } #if defined(__i386__) || defined(__x86_64__) #ifdef __amd64 #define BSF "bsfq" #else #define BSF "bsfl" #endif unsigned long fsig_log2 (unsigned long word) { __asm__ (BSF " %1,%0" : "=r" (word) : "r" (word)); return word; } #else /* Unoptimized */ unsigned long fsig_log2 (unsigned long word) { unsigned long result = 0; while (!(word & 1UL)) { result++; word >>= 1; } return result; } #endif int fsig_devread(fsi_file_t *ffi, unsigned int sector, unsigned int offset, unsigned int bufsize, char *buf) { off_t off; ssize_t ret; int n, r; char tmp[SECTOR_SIZE]; off = ffi->ff_fsi->f_off + ((off_t)sector * SECTOR_SIZE) + offset; /* * Make reads from a raw disk sector-aligned. This is a requirement * for NetBSD. Split the read up into to three parts to meet this * requirement. */ n = (off & (SECTOR_SIZE - 1)); if (n > 0) { r = SECTOR_SIZE - n; if (r > bufsize) r = bufsize; ret = pread(ffi->ff_fsi->f_fd, tmp, SECTOR_SIZE, off - n); if (ret < n + r) return (0); memcpy(buf, tmp + n, r); buf += r; bufsize -= r; off += r; } n = (bufsize & ~(SECTOR_SIZE - 1)); if (n > 0) { ret = pread(ffi->ff_fsi->f_fd, buf, n, off); if (ret < n) return (0); buf += n; bufsize -= n; off += n; } if (bufsize > 0) { ret = pread(ffi->ff_fsi->f_fd, tmp, SECTOR_SIZE, off); if (ret < bufsize) return (0); memcpy(buf, tmp, bufsize); } return (1); } int fsig_substring(const char *s1, const char *s2) { while (*s1 == *s2) { if (*s1 == '\0') return (0); s1++; s2++; } if (*s1 == '\0') return (-1); return (1); } static int fsig_mount(fsi_t *fsi, const char *path, const char *options) { fsig_plugin_ops_t *ops = fsi->f_plugin->fp_data; fsi_file_t *ffi; fsi->f_data = malloc(sizeof (fsig_data_t)); if (fsi->f_data == NULL) return (-1); if ((ffi = fsig_file_alloc(fsi)) == NULL) { free(fsi->f_data); fsi->f_data = NULL; return (-1); } bzero(fsi->f_data, sizeof (fsig_data_t)); if (!ops->fpo_mount(ffi, options)) { fsip_file_free(ffi); fsi_bootstring_free(fsi); free(fsi->f_data); fsi->f_data = NULL; return (-1); } bcopy(fsig_file_buf(ffi), fsig_fs_buf(fsi), FSYS_BUFLEN); fsip_file_free(ffi); return (0); } static int fsig_umount(fsi_t *fsi) { fsi_bootstring_free(fsi); free(fsi->f_data); return (0); } static fsi_file_t * fsig_open(fsi_t *fsi, const char *name) { fsig_plugin_ops_t *ops = fsi->f_plugin->fp_data; char *path = strdup(name); fsi_file_t *ffi = NULL; if (path == NULL || (ffi = fsig_file_alloc(fsi)) == NULL) goto out; if (ops->fpo_dir(ffi, path) == 0) { fsip_file_free(ffi); ffi = NULL; errno = ENOENT; } out: free(path); return (ffi); } static ssize_t fsig_pread(fsi_file_t *ffi, void *buf, size_t nbytes, uint64_t off) { fsig_plugin_ops_t *ops = ffi->ff_fsi->f_plugin->fp_data; fsig_file_data_t *data = fsip_file_data(ffi); data->ffd_filepos = off; if (data->ffd_filepos >= data->ffd_filemax) return (0); /* FIXME: check */ if (data->ffd_filepos + nbytes > data->ffd_filemax) nbytes = data->ffd_filemax - data->ffd_filepos; errnum = 0; return (ops->fpo_read(ffi, buf, nbytes)); } static ssize_t fsig_read(fsi_file_t *ffi, void *buf, size_t nbytes) { fsig_file_data_t *data = fsip_file_data(ffi); ssize_t ret; ret = fsig_pread(ffi, buf, nbytes, data->ffd_curpos); data->ffd_curpos = data->ffd_filepos; return (ret); } static int fsig_close(fsi_file_t *ffi) { free(ffi->ff_data); fsip_file_free(ffi); return (0); } static fsi_plugin_ops_t fsig_grub_ops = { .fpo_version = FSIMAGE_PLUGIN_VERSION, .fpo_mount = fsig_mount, .fpo_umount = fsig_umount, .fpo_open = fsig_open, .fpo_read = fsig_read, .fpo_pread = fsig_pread, .fpo_close = fsig_close }; fsi_plugin_ops_t * fsig_init(fsi_plugin_t *plugin, fsig_plugin_ops_t *ops) { if (ops->fpo_version > FSIMAGE_PLUGIN_VERSION) return (NULL); plugin->fp_data = ops; return (&fsig_grub_ops); } xen-4.9.2/tools/libfsimage/common/fsimage.h0000664000175000017500000000355513256712137017003 0ustar smbsmb/* * 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. * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FSIMAGE_H #define _FSIMAGE_H #ifdef __cplusplus extern C { #endif #include #include #include typedef struct fsi fsi_t; typedef struct fsi_file fsi_file_t; fsi_t *fsi_open_fsimage(const char *, uint64_t, const char *); void fsi_close_fsimage(fsi_t *); int fsi_file_exists(fsi_t *, const char *); fsi_file_t *fsi_open_file(fsi_t *, const char *); int fsi_close_file(fsi_file_t *); ssize_t fsi_read_file(fsi_file_t *, void *, size_t); ssize_t fsi_pread_file(fsi_file_t *, void *, size_t, uint64_t); char *fsi_bootstring_alloc(fsi_t *, size_t); void fsi_bootstring_free(fsi_t *); char *fsi_fs_bootstring(fsi_t *); #ifdef __cplusplus }; #endif #endif /* _FSIMAGE_H */ xen-4.9.2/tools/libfsimage/common/fsimage_plugin.c0000664000175000017500000000750513256712137020353 0ustar smbsmb/* * 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. * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include "fsimage_plugin.h" #include "fsimage_priv.h" static fsi_plugin_t *plugins; void fsip_fs_set_data(fsi_t *fsi, void *data) { fsi->f_data = data; } fsi_file_t * fsip_file_alloc(fsi_t *fsi, void *data) { fsi_file_t *ffi = malloc(sizeof (fsi_file_t)); if (ffi == NULL) return (NULL); bzero(ffi, sizeof (fsi_file_t)); ffi->ff_fsi = fsi; ffi->ff_data = data; return (ffi); } void fsip_file_free(fsi_file_t *ffi) { free(ffi); } fsi_t * fsip_fs(fsi_file_t *ffi) { return (ffi->ff_fsi); } uint64_t fsip_fs_offset(fsi_t *fsi) { return (fsi->f_off); } void * fsip_fs_data(fsi_t *fsi) { return (fsi->f_data); } void * fsip_file_data(fsi_file_t *ffi) { return (ffi->ff_data); } static int init_plugin(const char *lib) { fsi_plugin_init_t init; fsi_plugin_t *fp = malloc(sizeof (fsi_plugin_t)); if (fp == NULL) return (-1); bzero(fp, sizeof (fsi_plugin_t)); if ((fp->fp_dlh = dlopen(lib, RTLD_LAZY | RTLD_LOCAL)) == NULL) { free(fp); return (0); } init = dlsym(fp->fp_dlh, "fsi_init_plugin"); if (init == NULL) goto fail; fp->fp_ops = init(FSIMAGE_PLUGIN_VERSION, fp, &fp->fp_name); if (fp->fp_ops == NULL || fp->fp_ops->fpo_version > FSIMAGE_PLUGIN_VERSION) goto fail; fp->fp_next = plugins; plugins = fp; return (0); fail: (void) dlclose(fp->fp_dlh); free(fp); return (-1); } static int load_plugins(void) { const char *fsdir = getenv("FSIMAGE_FSDIR"); struct dirent *dp = NULL; DIR *dir = NULL; char *tmp = NULL; size_t name_max; int err; int ret = -1; if (fsdir == NULL) fsdir = FSIMAGE_FSDIR; if ((name_max = pathconf(fsdir, _PC_NAME_MAX)) == -1) goto fail; if ((tmp = malloc(name_max + 1)) == NULL) goto fail; if ((dir = opendir(fsdir)) == NULL) goto fail; for (;;) { errno = 0; dp = readdir(dir); if (dp == NULL && errno != 0) goto fail; if (dp == NULL) break; if (strcmp(dp->d_name, ".") == 0) continue; if (strcmp(dp->d_name, "..") == 0) continue; (void) snprintf(tmp, name_max, "%s/%s/fsimage.so", fsdir, dp->d_name); if (init_plugin(tmp) != 0) goto fail; } ret = 0; fail: err = errno; if (dir != NULL) (void) closedir(dir); free(tmp); free(dp); errno = err; return (ret); } int find_plugin(fsi_t *fsi, const char *path, const char *options) { fsi_plugin_t *fp; int ret = 0; if (plugins == NULL && (ret = load_plugins()) != 0) goto out; for (fp = plugins; fp != NULL; fp = fp->fp_next) { fsi->f_plugin = fp; if (fp->fp_ops->fpo_mount(fsi, path, options) == 0) goto out; } ret = -1; errno = ENOTSUP; out: return (ret); } xen-4.9.2/tools/libfsimage/common/fsimage.c0000664000175000017500000000742713256712137017000 0ustar smbsmb/* * 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. * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include "fsimage_plugin.h" #include "fsimage_priv.h" static pthread_mutex_t fsi_lock = PTHREAD_MUTEX_INITIALIZER; fsi_t *fsi_open_fsimage(const char *path, uint64_t off, const char *options) { fsi_t *fsi = NULL; int fd; int err; if ((fd = open(path, O_RDONLY)) == -1) goto fail; if ((fsi = malloc(sizeof(*fsi))) == NULL) goto fail; fsi->f_fd = fd; fsi->f_off = off; fsi->f_data = NULL; fsi->f_bootstring = NULL; pthread_mutex_lock(&fsi_lock); err = find_plugin(fsi, path, options); pthread_mutex_unlock(&fsi_lock); if (err != 0) goto fail; return (fsi); fail: err = errno; if (fd != -1) (void) close(fd); free(fsi); errno = err; return (NULL); } void fsi_close_fsimage(fsi_t *fsi) { pthread_mutex_lock(&fsi_lock); fsi->f_plugin->fp_ops->fpo_umount(fsi); (void) close(fsi->f_fd); free(fsi); pthread_mutex_unlock(&fsi_lock); } int fsi_file_exists(fsi_t *fsi, const char *path) { fsi_file_t *ffi; if ((ffi = fsi_open_file(fsi, path)) == NULL) return (0); fsi_close_file(ffi); return (1); } fsi_file_t *fsi_open_file(fsi_t *fsi, const char *path) { fsi_plugin_ops_t *ops; fsi_file_t *ffi; pthread_mutex_lock(&fsi_lock); ops = fsi->f_plugin->fp_ops; ffi = ops->fpo_open(fsi, path); pthread_mutex_unlock(&fsi_lock); return (ffi); } int fsi_close_file(fsi_file_t *ffi) { fsi_plugin_ops_t *ops; int err; pthread_mutex_lock(&fsi_lock); ops = ffi->ff_fsi->f_plugin->fp_ops; err = ops->fpo_close(ffi); pthread_mutex_unlock(&fsi_lock); return (err); } ssize_t fsi_read_file(fsi_file_t *ffi, void *buf, size_t nbytes) { fsi_plugin_ops_t *ops; ssize_t ret; pthread_mutex_lock(&fsi_lock); ops = ffi->ff_fsi->f_plugin->fp_ops; ret = ops->fpo_read(ffi, buf, nbytes); pthread_mutex_unlock(&fsi_lock); return (ret); } ssize_t fsi_pread_file(fsi_file_t *ffi, void *buf, size_t nbytes, uint64_t off) { fsi_plugin_ops_t *ops; ssize_t ret; pthread_mutex_lock(&fsi_lock); ops = ffi->ff_fsi->f_plugin->fp_ops; ret = ops->fpo_pread(ffi, buf, nbytes, off); pthread_mutex_unlock(&fsi_lock); return (ret); } char * fsi_bootstring_alloc(fsi_t *fsi, size_t len) { fsi->f_bootstring = malloc(len); if (fsi->f_bootstring == NULL) return (NULL); bzero(fsi->f_bootstring, len); return (fsi->f_bootstring); } void fsi_bootstring_free(fsi_t *fsi) { if (fsi->f_bootstring != NULL) { free(fsi->f_bootstring); fsi->f_bootstring = NULL; } } char * fsi_fs_bootstring(fsi_t *fsi) { return (fsi->f_bootstring); } xen-4.9.2/tools/libfsimage/fat/0000775000175000017500000000000013256712137014471 5ustar smbsmbxen-4.9.2/tools/libfsimage/fat/Makefile0000664000175000017500000000025613256712137016134 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. LIB_SRCS-y = fsys_fat.c FS = fat .PHONY: all all: fs-all .PHONY: install install: fs-install include $(XEN_ROOT)/tools/libfsimage/Rules.mk xen-4.9.2/tools/libfsimage/fat/fat.h0000664000175000017500000000630313256712137015416 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 2001 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* * Defines for the FAT BIOS Parameter Block (embedded in the first block * of the partition. */ typedef __signed__ char __s8; typedef unsigned char __u8; typedef __signed__ short __s16; typedef unsigned short __u16; typedef __signed__ int __s32; typedef unsigned int __u32; /* Note that some shorts are not aligned, and must therefore * be declared as array of two bytes. */ struct fat_bpb { __s8 ignored[3]; /* Boot strap short or near jump */ __s8 system_id[8]; /* Name - can be used to special case partition manager volumes */ __u8 bytes_per_sect[2]; /* bytes per logical sector */ __u8 sects_per_clust;/* sectors/cluster */ __u8 reserved_sects[2]; /* reserved sectors */ __u8 num_fats; /* number of FATs */ __u8 dir_entries[2]; /* root directory entries */ __u8 short_sectors[2]; /* number of sectors */ __u8 media; /* media code (unused) */ __u16 fat_length; /* sectors/FAT */ __u16 secs_track; /* sectors per track */ __u16 heads; /* number of heads */ __u32 hidden; /* hidden sectors (unused) */ __u32 long_sectors; /* number of sectors (if short_sectors == 0) */ /* The following fields are only used by FAT32 */ __u32 fat32_length; /* sectors/FAT */ __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ __u8 version[2]; /* major, minor filesystem version */ __u32 root_cluster; /* first cluster in root directory */ __u16 info_sector; /* filesystem info sector */ __u16 backup_boot; /* backup boot sector */ __u16 reserved2[6]; /* Unused */ }; #define FAT_CVT_U16(bytarr) (* (__u16*)(bytarr)) /* * Defines how to differentiate a 12-bit and 16-bit FAT. */ #define FAT_MAX_12BIT_CLUST 4087 /* 4085 + 2 */ /* * Defines for the file "attribute" byte */ #define FAT_ATTRIB_OK_MASK 0x37 #define FAT_ATTRIB_NOT_OK_MASK 0xC8 #define FAT_ATTRIB_DIR 0x10 #define FAT_ATTRIB_LONGNAME 0x0F /* * Defines for FAT directory entries */ #define FAT_DIRENTRY_LENGTH 32 #define FAT_DIRENTRY_ATTRIB(entry) \ (*((__u8 *) (entry+11))) #define FAT_DIRENTRY_VALID(entry) \ ( ((*((__u8 *) entry)) != 0) \ && ((*((__u8 *) entry)) != 0xE5) \ && !(FAT_DIRENTRY_ATTRIB(entry) & FAT_ATTRIB_NOT_OK_MASK) ) #define FAT_DIRENTRY_FIRST_CLUSTER(entry) \ ((*((__u16 *) (entry+26)))+(*((__u16 *) (entry+20)) << 16)) #define FAT_DIRENTRY_FILELENGTH(entry) \ (*((__u32 *) (entry+28))) #define FAT_LONGDIR_ID(entry) \ (*((__u8 *) (entry))) #define FAT_LONGDIR_ALIASCHECKSUM(entry) \ (*((__u8 *) (entry+13))) xen-4.9.2/tools/libfsimage/fat/fsys_fat.c0000664000175000017500000003001213256712137016447 0ustar smbsmb/* * GRUB -- GRand Unified Bootloader * Copyright (C) 2000,2001,2005 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #include #include #include "fat.h" struct fat_superblock { int fat_offset; int fat_length; int fat_size; int root_offset; int root_max; int data_offset; int num_sectors; int num_clust; int clust_eof_marker; int sects_per_clust; int sectsize_bits; int clustsize_bits; int root_cluster; int cached_fat; int file_cluster; int current_cluster_num; int current_cluster; }; /* pointer(s) into filesystem info buffer for DOS stuff */ #define FAT_SUPER ( (struct fat_superblock *) \ ( FSYS_BUF + 32256) )/* 512 bytes long */ #define FAT_BUF ( FSYS_BUF + 30208 ) /* 4 sector FAT buffer */ #define NAME_BUF ( FSYS_BUF + 29184 ) /* Filename buffer (833 bytes) */ #define FAT_CACHE_SIZE 2048 #define log2 grub_log2 static int fat_mount (fsi_file_t *ffi, const char *options) { struct fat_bpb bpb; __u32 magic, first_fat; /* Read bpb */ if (! devread (ffi, 0, 0, sizeof (bpb), (char *) &bpb)) return 0; /* Check if the number of sectors per cluster is zero here, to avoid zero division. */ if (bpb.sects_per_clust == 0) return 0; FAT_SUPER->sectsize_bits = log2 (FAT_CVT_U16 (bpb.bytes_per_sect)); FAT_SUPER->clustsize_bits = FAT_SUPER->sectsize_bits + log2 (bpb.sects_per_clust); /* Fill in info about super block */ FAT_SUPER->num_sectors = FAT_CVT_U16 (bpb.short_sectors) ? FAT_CVT_U16 (bpb.short_sectors) : bpb.long_sectors; /* FAT offset and length */ FAT_SUPER->fat_offset = FAT_CVT_U16 (bpb.reserved_sects); FAT_SUPER->fat_length = bpb.fat_length ? bpb.fat_length : bpb.fat32_length; /* Rootdir offset and length for FAT12/16 */ FAT_SUPER->root_offset = FAT_SUPER->fat_offset + bpb.num_fats * FAT_SUPER->fat_length; FAT_SUPER->root_max = FAT_DIRENTRY_LENGTH * FAT_CVT_U16(bpb.dir_entries); /* Data offset and number of clusters */ FAT_SUPER->data_offset = FAT_SUPER->root_offset + ((FAT_SUPER->root_max - 1) >> FAT_SUPER->sectsize_bits) + 1; FAT_SUPER->num_clust = 2 + ((FAT_SUPER->num_sectors - FAT_SUPER->data_offset) / bpb.sects_per_clust); FAT_SUPER->sects_per_clust = bpb.sects_per_clust; if (!bpb.fat_length) { /* This is a FAT32 */ if (FAT_CVT_U16(bpb.dir_entries)) return 0; if (bpb.flags & 0x0080) { /* FAT mirroring is disabled, get active FAT */ int active_fat = bpb.flags & 0x000f; if (active_fat >= bpb.num_fats) return 0; FAT_SUPER->fat_offset += active_fat * FAT_SUPER->fat_length; } FAT_SUPER->fat_size = 8; FAT_SUPER->root_cluster = bpb.root_cluster; /* Yes the following is correct. FAT32 should be called FAT28 :) */ FAT_SUPER->clust_eof_marker = 0xffffff8; } else { if (!FAT_SUPER->root_max) return 0; FAT_SUPER->root_cluster = -1; if (FAT_SUPER->num_clust > FAT_MAX_12BIT_CLUST) { FAT_SUPER->fat_size = 4; FAT_SUPER->clust_eof_marker = 0xfff8; } else { FAT_SUPER->fat_size = 3; FAT_SUPER->clust_eof_marker = 0xff8; } } /* Now do some sanity checks */ if (FAT_CVT_U16(bpb.bytes_per_sect) != (1 << FAT_SUPER->sectsize_bits) || FAT_CVT_U16(bpb.bytes_per_sect) != SECTOR_SIZE || bpb.sects_per_clust != (1 << (FAT_SUPER->clustsize_bits - FAT_SUPER->sectsize_bits)) || FAT_SUPER->num_clust <= 2 || (FAT_SUPER->fat_size * FAT_SUPER->num_clust / (2 * SECTOR_SIZE) > FAT_SUPER->fat_length)) return 0; /* kbs: Media check on first FAT entry [ported from PUPA] */ if (!devread(ffi, FAT_SUPER->fat_offset, 0, sizeof(first_fat), (char *)&first_fat)) return 0; if (FAT_SUPER->fat_size == 8) { first_fat &= 0x0fffffff; magic = 0x0fffff00; } else if (FAT_SUPER->fat_size == 4) { first_fat &= 0x0000ffff; magic = 0xff00; } else { first_fat &= 0x00000fff; magic = 0x0f00; } /* Ignore the 3rd bit, because some BIOSes assigns 0xF0 to the media descriptor, even if it is a so-called superfloppy (e.g. an USB key). The check may be too strict for this kind of stupid BIOSes, as they overwrite the media descriptor. */ if ((first_fat | 0x8) != (magic | bpb.media | 0x8)) return 0; FAT_SUPER->cached_fat = - 2 * FAT_CACHE_SIZE; return 1; } static int fat_read (fsi_file_t *ffi, char *buf, int len) { int logical_clust; int offset; int ret = 0; int size; if (FAT_SUPER->file_cluster < 0) { /* root directory for fat16 */ size = FAT_SUPER->root_max - filepos; if (size > len) size = len; if (!devread(ffi, FAT_SUPER->root_offset, filepos, size, buf)) return 0; filepos += size; return size; } logical_clust = filepos >> FAT_SUPER->clustsize_bits; offset = (filepos & ((1 << FAT_SUPER->clustsize_bits) - 1)); if (logical_clust < FAT_SUPER->current_cluster_num) { FAT_SUPER->current_cluster_num = 0; FAT_SUPER->current_cluster = FAT_SUPER->file_cluster; } while (len > 0) { int sector; while (logical_clust > FAT_SUPER->current_cluster_num) { /* calculate next cluster */ int fat_entry = FAT_SUPER->current_cluster * FAT_SUPER->fat_size; int next_cluster; int cached_pos = (fat_entry - FAT_SUPER->cached_fat); if (cached_pos < 0 || (cached_pos + FAT_SUPER->fat_size) > 2*FAT_CACHE_SIZE) { FAT_SUPER->cached_fat = (fat_entry & ~(2*SECTOR_SIZE - 1)); cached_pos = (fat_entry - FAT_SUPER->cached_fat); sector = FAT_SUPER->fat_offset + FAT_SUPER->cached_fat / (2*SECTOR_SIZE); if (!devread (ffi, sector, 0, FAT_CACHE_SIZE, (char*) FAT_BUF)) return 0; } next_cluster = ((__u16 *) (FAT_BUF + (cached_pos >> 1)))[0]; if (FAT_SUPER->fat_size == 3) { if (cached_pos & 1) next_cluster >>= 4; next_cluster &= 0xFFF; } else if (FAT_SUPER->fat_size > 4) next_cluster |= ((__u16 *) (FAT_BUF + (cached_pos >> 1)))[1] << 16; if (next_cluster >= FAT_SUPER->clust_eof_marker) return ret; if (next_cluster < 2 || next_cluster >= FAT_SUPER->num_clust) { errnum = ERR_FSYS_CORRUPT; return 0; } FAT_SUPER->current_cluster = next_cluster; FAT_SUPER->current_cluster_num++; } sector = FAT_SUPER->data_offset + ((FAT_SUPER->current_cluster - 2) << (FAT_SUPER->clustsize_bits - FAT_SUPER->sectsize_bits)); size = (1 << FAT_SUPER->clustsize_bits) - offset; if (size > len) size = len; disk_read_func = disk_read_hook; devread(ffi, sector, offset, size, buf); disk_read_func = NULL; len -= size; buf += size; ret += size; filepos += size; logical_clust++; offset = 0; } return errnum ? 0 : ret; } static int fat_dir (fsi_file_t *ffi, char *dirname) { char *rest, ch, dir_buf[FAT_DIRENTRY_LENGTH]; char *filename = (char *) NAME_BUF; int attrib = FAT_ATTRIB_DIR; #ifndef STAGE1_5 int do_possibilities = 0; #endif /* XXX I18N: * the positions 2,4,6 etc are high bytes of a 16 bit unicode char */ static unsigned char longdir_pos[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; int slot = -2; int alias_checksum = -1; FAT_SUPER->file_cluster = FAT_SUPER->root_cluster; filepos = 0; FAT_SUPER->current_cluster_num = INT_MAX; /* main loop to find desired directory entry */ loop: /* if we have a real file (and we're not just printing possibilities), then this is where we want to exit */ if (!*dirname || isspace ((uint8_t)*dirname)) { if (attrib & FAT_ATTRIB_DIR) { errnum = ERR_BAD_FILETYPE; return 0; } return 1; } /* continue with the file/directory name interpretation */ while (*dirname == '/') dirname++; if (!(attrib & FAT_ATTRIB_DIR)) { errnum = ERR_BAD_FILETYPE; return 0; } /* Directories don't have a file size */ filemax = INT_MAX; for (rest = dirname; (ch = *rest) && !isspace ((uint8_t)ch) && ch != '/'; rest++); *rest = 0; # ifndef STAGE1_5 if (print_possibilities && ch != '/') do_possibilities = 1; # endif while (1) { if (fat_read (ffi, dir_buf, FAT_DIRENTRY_LENGTH) != FAT_DIRENTRY_LENGTH || dir_buf[0] == 0) { if (!errnum) { # ifndef STAGE1_5 if (print_possibilities < 0) { #if 0 putchar ('\n'); #endif return 1; } # endif /* STAGE1_5 */ errnum = ERR_FILE_NOT_FOUND; *rest = ch; } return 0; } if (FAT_DIRENTRY_ATTRIB (dir_buf) == FAT_ATTRIB_LONGNAME) { /* This is a long filename. The filename is build from back * to front and may span multiple entries. To bind these * entries together they all contain the same checksum over * the short alias. * * The id field tells if this is the first entry (the last * part) of the long filename, and also at which offset this * belongs. * * We just write the part of the long filename this entry * describes and continue with the next dir entry. */ int i, offset; unsigned char id = FAT_LONGDIR_ID(dir_buf); if ((id & 0x40)) { id &= 0x3f; slot = id; filename[slot * 13] = 0; alias_checksum = FAT_LONGDIR_ALIASCHECKSUM(dir_buf); } if (id != slot || slot == 0 || alias_checksum != FAT_LONGDIR_ALIASCHECKSUM(dir_buf)) { alias_checksum = -1; continue; } slot--; offset = slot * 13; for (i=0; i < 13; i++) filename[offset+i] = dir_buf[longdir_pos[i]]; continue; } if (!FAT_DIRENTRY_VALID (dir_buf)) continue; if (alias_checksum != -1 && slot == 0) { int i; unsigned char sum; slot = -2; for (sum = 0, i = 0; i< 11; i++) sum = ((sum >> 1) | (sum << 7)) + dir_buf[i]; if (sum == alias_checksum) { # ifndef STAGE1_5 if (do_possibilities) goto print_filename; # endif /* STAGE1_5 */ if (substring (dirname, filename) == 0) break; } } /* XXX convert to 8.3 filename format here */ { int i, j, c; for (i = 0; i < 8 && (c = filename[i] = tolower ((uint8_t)dir_buf[i])) && !isspace ((uint8_t)c); i++); filename[i++] = '.'; for (j = 0; j < 3 && (c = filename[i + j] = tolower ((uint8_t)dir_buf[8 + j])) && !isspace ((uint8_t)c); j++); if (j == 0) i--; filename[i + j] = 0; } # ifndef STAGE1_5 if (do_possibilities) { print_filename: if (substring (dirname, filename) <= 0) { if (print_possibilities > 0) print_possibilities = -print_possibilities; print_a_completion (filename); } continue; } # endif /* STAGE1_5 */ if (substring (dirname, filename) == 0) break; } *(dirname = rest) = ch; attrib = FAT_DIRENTRY_ATTRIB (dir_buf); filemax = FAT_DIRENTRY_FILELENGTH (dir_buf); filepos = 0; FAT_SUPER->file_cluster = FAT_DIRENTRY_FIRST_CLUSTER (dir_buf); FAT_SUPER->current_cluster_num = INT_MAX; /* go back to main loop at top of function */ goto loop; } fsi_plugin_ops_t * fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) { static fsig_plugin_ops_t ops = { FSIMAGE_PLUGIN_VERSION, .fpo_mount = fat_mount, .fpo_dir = fat_dir, .fpo_read = fat_read }; *name = "fat"; return (fsig_init(fp, &ops)); } xen-4.9.2/tools/fuzz/0000775000175000017500000000000013256712137012613 5ustar smbsmbxen-4.9.2/tools/fuzz/libelf/0000775000175000017500000000000013256712137014050 5ustar smbsmbxen-4.9.2/tools/fuzz/libelf/libelf-fuzzer.c0000664000175000017500000000111313256712137016770 0ustar smbsmb#include #include #include #include #include int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { struct elf_binary elf_buf, *elf; struct elf_dom_parms parms; elf = &elf_buf; memset(elf, 0, sizeof(*elf)); if ( elf_init(elf, (const char *)data, size) < 0 ) return -1; elf_parse_binary(elf); elf_xen_parse(elf, &parms); return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/fuzz/libelf/afl-libelf-fuzzer.c0000664000175000017500000000161613256712137017540 0ustar smbsmb#include #include #include #include extern int LLVMFuzzerTestOneInput(const uint8_t *data_p, size_t size); #define INPUT_SIZE 4096 static uint8_t input[INPUT_SIZE]; int main(int argc, char **argv) { size_t size; FILE *fp; setbuf(stdout, NULL); if ( argc != 2 ) { printf("Expecting only one argument\n"); exit(-1); } fp = fopen(argv[1], "rb"); if ( fp == NULL ) { perror("fopen"); exit(-1); } size = fread(input, 1, INPUT_SIZE, fp); if ( ferror(fp) ) { perror("fread"); exit(-1); } if ( !feof(fp) ) { printf("Input too large\n"); exit(-1); } fclose(fp); return LLVMFuzzerTestOneInput(input, size); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/fuzz/libelf/Makefile0000664000175000017500000000157613256712137015521 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk # libelf fuzz target vpath %.c ../../../xen/common/libelf CFLAGS += -I../../../xen/common/libelf ELF_SRCS-y += libelf-tools.c libelf-loader.c libelf-dominfo.c ELF_LIB_OBJS := $(patsubst %.c,%.o,$(ELF_SRCS-y)) $(patsubst %.c,%.o,$(ELF_SRCS-y)): CFLAGS += -Wno-pointer-sign $(ELF_LIB_OBJS): CFLAGS += -DFUZZ_NO_LIBXC $(CFLAGS_xeninclude) libelf-fuzzer.o: CFLAGS += $(CFLAGS_xeninclude) libelf.a: libelf-fuzzer.o $(ELF_LIB_OBJS) $(AR) rc $@ $^ .PHONY: libelf-fuzzer-all libelf-fuzzer-all: libelf.a libelf-fuzzer.o afl-libelf-fuzzer: afl-libelf-fuzzer.o libelf-fuzzer.o $(ELF_LIB_OBJS) $(CC) $(CFLAGS) $^ -o $@ # Common targets .PHONY: all all: libelf-fuzzer-all .PHONY: distclean distclean: clean .PHONY: clean clean: rm -f *.o .*.d *.a *-libelf-fuzzer .PHONY: install install: all .PHONY: afl afl: afl-libelf-fuzzer xen-4.9.2/tools/fuzz/README.oss-fuzz0000664000175000017500000000303013256712137015266 0ustar smbsmb# OVERVIEW This directory provides fuzzing targets to be run inside Google oss-fuzz infrastructure. See also https://github.com/google/oss-fuzz. # HOW IT WORKS We need to provide the source code and the rune to produce objects or archives (artefacts) from source code. These items ideally should live inside xen.git so that they can be kept up to date. The artefacts contain all the code we wish to fuzz and a function called LLVMFuzzerTestOneInput. LLVMFuzzerTestOneInput is the entry point to the code we wish to fuzz. Note that we don't produce executable programs because we don't have libFuzzEngine locally. libFuzzEngine is maintained by oss-fuzz. We also provide build script to oss-fuzz. The build script will inherit the correct compiler settings and be run in a pre-setup environment, which has libFuzzEngine installed. The build script is responsible for calling the correct Xen build rune to produce the artefacts, then link them against libFuzzEngine to produce executables, which will run in oss-fuzz infrastructure. Please refer to official oss-fuzz documents for the most up-to-date descriptions for all moving parts. # HOW TO IMPROVE THE FUZZING TARGETS Feel free to modify each fuzzing targets at will. Make sure they build by invoking make as you would build tools. To actually test the new code, you would need to run the target in standalone mode, please refer to oss-fuzz documents on how to do that. It is highly recommended that you run the new target for a while to weed out error in plumbing code to avoid false positives. xen-4.9.2/tools/fuzz/README.afl0000664000175000017500000000232413256712137014235 0ustar smbsmb# OVERVIEW Some fuzzing targets have American Fuzzy Lop (AFL) support. See also http://lcamtuf.coredump.cx/afl/ # HOW IT WORKS AFL provides a customised toolchain to build an executable, which in turn is launched by the fuzzer. # HOW TO USE IT Use the x86 instruction emulator fuzzer as an example. 1. download and compile AFL in $AFLPATH. 2. run the following commands to build: $ cd tools/fuzz/x86_instruction_emulator $ make distclean If you have a new enough version of Clang/LLVM and have configured AFL's llvm_mode, make use of afl-clang-fast: $ make CC=$AFLPATH/afl-clang-fast afl # produces afl-harness If not, use the default afl-gcc: $ make CC=$AFLPATH/afl-gcc afl # produces afl-harness 3. provide initial test case (fuzzer dependent, see afl-*.c): $ mkdir testcase_dir $ dd if=/dev/urandom of=testcase_dir/rand.bin \ bs=`./afl-harness --min-input-size` count=1 3a. use a tmpfs for findings_dir (Perf improvement and reduced disk load) $ mkdir findings_dir $ mount -t tmpfs -o size=512M tmpfs findings_dir 4. run the fuzzer with AFL: $ $AFLPATH/afl-fuzz -t 1000 -i testcase_dir -o findings_dir -- ./afl-harness Please see AFL documentation for more information. xen-4.9.2/tools/fuzz/Makefile0000664000175000017500000000032513256712137014253 0ustar smbsmbXEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk SUBDIRS-y := SUBDIRS-y += libelf SUBDIRS-y += x86_instruction_emulator .PHONY: all clean distclean install all clean distclean install: %: subdirs-% xen-4.9.2/tools/fuzz/x86_instruction_emulator/0000775000175000017500000000000013256712137017611 5ustar smbsmbxen-4.9.2/tools/fuzz/x86_instruction_emulator/afl-harness.c0000664000175000017500000000514213256712137022162 0ustar smbsmb#include #include #include #include #include #include extern int LLVMFuzzerInitialize(int *argc, char ***argv); extern int LLVMFuzzerTestOneInput(const uint8_t *data_p, size_t size); extern unsigned int fuzz_minimal_input_size(void); #define INPUT_SIZE 4096 static uint8_t input[INPUT_SIZE]; int main(int argc, char **argv) { size_t size; FILE *fp = NULL; setbuf(stdin, NULL); setbuf(stdout, NULL); while ( 1 ) { enum { OPT_MIN_SIZE, }; static const struct option lopts[] = { { "min-input-size", no_argument, NULL, OPT_MIN_SIZE }, { 0, 0, 0, 0 } }; int c = getopt_long_only(argc, argv, "", lopts, NULL); if ( c == -1 ) break; switch ( c ) { case OPT_MIN_SIZE: printf("%u\n", fuzz_minimal_input_size()); exit(0); break; case '?': usage: printf("Usage: %s $FILE | [--min-input-size]\n", argv[0]); exit(-1); break; default: printf("Bad getopt return %d (%c)\n", c, c); exit(-1); break; } } if ( optind == argc ) /* No positional parameters. Use stdin. */ fp = stdin; else if ( optind != (argc - 1) ) goto usage; if ( LLVMFuzzerInitialize(&argc, &argv) ) exit(-1); #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); while ( __AFL_LOOP(1000) ) #endif { if ( fp != stdin ) /* If not using stdin, open the provided file. */ { fp = fopen(argv[optind], "rb"); if ( fp == NULL ) { perror("fopen"); exit(-1); } } #ifdef __AFL_HAVE_MANUAL_CONTROL else { /* * This will ensure we're dealing with a clean stream * state after the afl-fuzz process messes with the open * file handle. */ fseek(fp, 0, SEEK_SET); } #endif size = fread(input, 1, INPUT_SIZE, fp); if ( ferror(fp) ) { perror("fread"); exit(-1); } if ( !feof(fp) ) { printf("Input too large\n"); exit(-1); } if ( fp != stdin ) { fclose(fp); fp = NULL; } LLVMFuzzerTestOneInput(input, size); } return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/fuzz/x86_instruction_emulator/fuzz-emul.c0000664000175000017500000005174713256712137021731 0ustar smbsmb#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "x86_emulate.h" #define MSR_INDEX_MAX 16 #define SEG_NUM x86_seg_none /* Layout of data expected as fuzzing input. */ struct fuzz_corpus { unsigned long cr[5]; uint64_t msr[MSR_INDEX_MAX]; struct cpu_user_regs regs; struct segment_register segments[SEG_NUM]; unsigned long options; unsigned char data[4096]; } input; #define DATA_OFFSET offsetof(struct fuzz_corpus, data) /* * Internal state of the fuzzing harness. Calculated initially from the input * corpus, and later mutates by the emulation callbacks. */ struct fuzz_state { /* Fuzzer's input data. */ struct fuzz_corpus *corpus; /* Real amount of data backing corpus->data[]. */ size_t data_num; /* Amount of corpus->data[] consumed thus far. */ size_t data_index; /* Emulation ops, some of which are disabled based on corpus->options. */ struct x86_emulate_ops ops; }; /* * Randomly return success or failure when processing data. If * `exception` is false, this function turns _EXCEPTION to _OKAY. */ static int maybe_fail(struct x86_emulate_ctxt *ctxt, const char *why, bool exception) { struct fuzz_state *s = ctxt->data; const struct fuzz_corpus *c = s->corpus; int rc; if ( s->data_index >= s->data_num ) rc = X86EMUL_EXCEPTION; else { /* Randomly returns value: * 50% okay * 25% unhandlable * 25% exception */ if ( c->data[s->data_index] > 0xc0 ) rc = X86EMUL_EXCEPTION; else if ( c->data[s->data_index] > 0x80 ) rc = X86EMUL_UNHANDLEABLE; else rc = X86EMUL_OKAY; s->data_index++; } if ( rc == X86EMUL_EXCEPTION && !exception ) rc = X86EMUL_OKAY; printf("maybe_fail %s: %d\n", why, rc); if ( rc == X86EMUL_EXCEPTION ) /* Fake up a pagefault. */ x86_emul_pagefault(0, 0, ctxt); return rc; } static int data_read(struct x86_emulate_ctxt *ctxt, enum x86_segment seg, const char *why, void *dst, unsigned int bytes) { struct fuzz_state *s = ctxt->data; const struct fuzz_corpus *c = s->corpus; unsigned int i; int rc; if ( s->data_index + bytes > s->data_num ) { /* * Fake up a segment limit violation. System segment limit volations * are reported by X86EMUL_EXCEPTION alone, so the emulator can fill * in the correct context. */ if ( !is_x86_system_segment(seg) ) x86_emul_hw_exception(13, 0, ctxt); rc = X86EMUL_EXCEPTION; } else rc = maybe_fail(ctxt, why, true); if ( rc == X86EMUL_OKAY ) { memcpy(dst, &c->data[s->data_index], bytes); s->data_index += bytes; printf("%s: ", why); for ( i = 0; i < bytes; i++ ) printf(" %02x", *(unsigned char *)(dst + i)); printf("\n"); } return rc; } static int fuzz_read( enum x86_segment seg, unsigned long offset, void *p_data, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { /* Reads expected for all user and system segments. */ if ( is_x86_user_segment(seg) ) assert(ctxt->addr_size == 64 || !(offset >> 32)); else if ( seg == x86_seg_tr ) /* * The TSS is special in that accesses below the segment base are * possible, as the Interrupt Redirection Bitmap starts 32 bytes * ahead of the I/O Bitmap, regardless of the value of the latter. */ assert((long)offset < 0 ? (long)offset > -32 : !(offset >> 17)); else assert(is_x86_system_segment(seg) && (ctxt->lma ? offset <= 0x10007 : !(offset >> 16))); return data_read(ctxt, seg, "read", p_data, bytes); } static int fuzz_read_io( unsigned int port, unsigned int bytes, unsigned long *val, struct x86_emulate_ctxt *ctxt) { return data_read(ctxt, x86_seg_none, "read_io", val, bytes); } static int fuzz_insn_fetch( enum x86_segment seg, unsigned long offset, void *p_data, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { assert(seg == x86_seg_cs); /* Minimal segment limit checking, until full one is being put in place. */ if ( ctxt->addr_size < 64 && (offset >> 32) ) { x86_emul_hw_exception(13, 0, ctxt); return X86EMUL_EXCEPTION; } /* * Zero-length instruction fetches are made at the destination of jumps, * to perform segmentation checks. No data needs returning. */ if ( bytes == 0 ) { assert(p_data == NULL); return maybe_fail(ctxt, "insn_fetch", true); } return data_read(ctxt, seg, "insn_fetch", p_data, bytes); } static int _fuzz_rep_read(struct x86_emulate_ctxt *ctxt, const char *why, unsigned long *reps) { int rc; unsigned long bytes_read = 0; rc = data_read(ctxt, x86_seg_none, why, &bytes_read, sizeof(bytes_read)); if ( bytes_read <= *reps ) *reps = bytes_read; switch ( rc ) { case X86EMUL_UNHANDLEABLE: /* No work is done in this case */ *reps = 0; break; case X86EMUL_EXCEPTION: case X86EMUL_RETRY: /* Halve the amount in this case */ *reps /= 2; break; } return rc; } static int _fuzz_rep_write(struct x86_emulate_ctxt *ctxt, const char *why, unsigned long *reps) { int rc = maybe_fail(ctxt, why, true); switch ( rc ) { case X86EMUL_UNHANDLEABLE: /* No work is done in this case */ *reps = 0; break; case X86EMUL_EXCEPTION: case X86EMUL_RETRY: /* Halve the amount in this case */ *reps /= 2; break; } return rc; } static int fuzz_rep_ins( uint16_t src_port, enum x86_segment dst_seg, unsigned long dst_offset, unsigned int bytes_per_rep, unsigned long *reps, struct x86_emulate_ctxt *ctxt) { assert(dst_seg == x86_seg_es); assert(ctxt->addr_size == 64 || !(dst_offset >> 32)); return _fuzz_rep_read(ctxt, "rep_ins", reps); } static int fuzz_rep_movs( enum x86_segment src_seg, unsigned long src_offset, enum x86_segment dst_seg, unsigned long dst_offset, unsigned int bytes_per_rep, unsigned long *reps, struct x86_emulate_ctxt *ctxt) { assert(is_x86_user_segment(src_seg)); assert(dst_seg == x86_seg_es); assert(ctxt->addr_size == 64 || !((src_offset | dst_offset) >> 32)); return _fuzz_rep_read(ctxt, "rep_movs", reps); } static int fuzz_rep_outs( enum x86_segment src_seg, unsigned long src_offset, uint16_t dst_port, unsigned int bytes_per_rep, unsigned long *reps, struct x86_emulate_ctxt *ctxt) { assert(is_x86_user_segment(src_seg)); assert(ctxt->addr_size == 64 || !(src_offset >> 32)); return _fuzz_rep_write(ctxt, "rep_outs", reps); } static int fuzz_rep_stos( void *p_data, enum x86_segment seg, unsigned long offset, unsigned int bytes_per_rep, unsigned long *reps, struct x86_emulate_ctxt *ctxt) { /* * STOS itself may only have an %es segment, but the stos() hook is reused * for CLZERO. */ assert(is_x86_user_segment(seg)); assert(ctxt->addr_size == 64 || !(offset >> 32)); return _fuzz_rep_write(ctxt, "rep_stos", reps); } static int fuzz_write( enum x86_segment seg, unsigned long offset, void *p_data, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { /* Writes not expected for any system segments. */ assert(is_x86_user_segment(seg)); assert(ctxt->addr_size == 64 || !(offset >> 32)); return maybe_fail(ctxt, "write", true); } static int fuzz_cmpxchg( enum x86_segment seg, unsigned long offset, void *old, void *new, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { /* * Cmpxchg expected for user segments, and setting accessed/busy bits in * GDT/LDT enties, but not expected for any IDT or TR accesses. */ if ( is_x86_user_segment(seg) ) assert(ctxt->addr_size == 64 || !(offset >> 32)); else assert((seg == x86_seg_gdtr || seg == x86_seg_ldtr) && !(offset >> 16)); return maybe_fail(ctxt, "cmpxchg", true); } static int fuzz_invlpg( enum x86_segment seg, unsigned long offset, struct x86_emulate_ctxt *ctxt) { /* invlpg(), unlike all other hooks, may be called with x86_seg_none. */ assert(is_x86_user_segment(seg) || seg == x86_seg_none); assert(ctxt->addr_size == 64 || !(offset >> 32)); return maybe_fail(ctxt, "invlpg", false); } static int fuzz_wbinvd( struct x86_emulate_ctxt *ctxt) { return maybe_fail(ctxt, "wbinvd", true); } static int fuzz_write_io( unsigned int port, unsigned int bytes, unsigned long val, struct x86_emulate_ctxt *ctxt) { return maybe_fail(ctxt, "write_io", true); } static int fuzz_read_segment( enum x86_segment seg, struct segment_register *reg, struct x86_emulate_ctxt *ctxt) { const struct fuzz_state *s = ctxt->data; const struct fuzz_corpus *c = s->corpus; assert(is_x86_user_segment(seg) || is_x86_system_segment(seg)); *reg = c->segments[seg]; return X86EMUL_OKAY; } static int fuzz_write_segment( enum x86_segment seg, const struct segment_register *reg, struct x86_emulate_ctxt *ctxt) { struct fuzz_state *s = ctxt->data; struct fuzz_corpus *c = s->corpus; int rc; assert(is_x86_user_segment(seg) || is_x86_system_segment(seg)); rc = maybe_fail(ctxt, "write_segment", true); if ( rc == X86EMUL_OKAY ) c->segments[seg] = *reg; return rc; } static int fuzz_read_cr( unsigned int reg, unsigned long *val, struct x86_emulate_ctxt *ctxt) { const struct fuzz_state *s = ctxt->data; const struct fuzz_corpus *c = s->corpus; if ( reg >= ARRAY_SIZE(c->cr) ) return X86EMUL_UNHANDLEABLE; *val = c->cr[reg]; return X86EMUL_OKAY; } static int fuzz_write_cr( unsigned int reg, unsigned long val, struct x86_emulate_ctxt *ctxt) { struct fuzz_state *s = ctxt->data; struct fuzz_corpus *c = s->corpus; int rc; if ( reg >= ARRAY_SIZE(c->cr) ) return X86EMUL_UNHANDLEABLE; rc = maybe_fail(ctxt, "write_cr", true); if ( rc != X86EMUL_OKAY ) return rc; c->cr[reg] = val; return X86EMUL_OKAY; } enum { MSRI_IA32_SYSENTER_CS, MSRI_IA32_SYSENTER_ESP, MSRI_IA32_SYSENTER_EIP, MSRI_EFER, MSRI_STAR, MSRI_LSTAR, MSRI_CSTAR, MSRI_SYSCALL_MASK, MSRI_IA32_DEBUGCTLMSR, }; static const unsigned int msr_index[MSR_INDEX_MAX] = { [MSRI_IA32_SYSENTER_CS] = MSR_IA32_SYSENTER_CS, [MSRI_IA32_SYSENTER_ESP] = MSR_IA32_SYSENTER_ESP, [MSRI_IA32_SYSENTER_EIP] = MSR_IA32_SYSENTER_EIP, [MSRI_EFER] = MSR_EFER, [MSRI_STAR] = MSR_STAR, [MSRI_LSTAR] = MSR_LSTAR, [MSRI_CSTAR] = MSR_CSTAR, [MSRI_SYSCALL_MASK] = MSR_SYSCALL_MASK, [MSRI_IA32_DEBUGCTLMSR] = MSR_IA32_DEBUGCTLMSR, }; static int fuzz_read_msr( unsigned int reg, uint64_t *val, struct x86_emulate_ctxt *ctxt) { const struct fuzz_state *s = ctxt->data; const struct fuzz_corpus *c = s->corpus; unsigned int idx; switch ( reg ) { case MSR_TSC_AUX: case MSR_IA32_TSC: /* * TSC should return monotonically increasing values, TSC_AUX * should preferably return consistent values, but returning * random values is fine in fuzzer. */ return data_read(ctxt, x86_seg_none, "read_msr", val, sizeof(*val)); case MSR_EFER: *val = c->msr[MSRI_EFER]; *val &= ~EFER_LMA; if ( (*val & EFER_LME) && (c->cr[4] & X86_CR4_PAE) && (c->cr[0] & X86_CR0_PG) ) { printf("Setting EFER_LMA\n"); *val |= EFER_LMA; } return X86EMUL_OKAY; } for ( idx = 0; idx < MSR_INDEX_MAX; idx++ ) { if ( msr_index[idx] == reg ) { *val = c->msr[idx]; return X86EMUL_OKAY; } } x86_emul_hw_exception(13, 0, ctxt); return X86EMUL_EXCEPTION; } static int fuzz_write_msr( unsigned int reg, uint64_t val, struct x86_emulate_ctxt *ctxt) { struct fuzz_state *s = ctxt->data; struct fuzz_corpus *c = s->corpus; unsigned int idx; int rc; rc = maybe_fail(ctxt, "write_msr", true); if ( rc != X86EMUL_OKAY ) return rc; switch ( reg ) { case MSR_TSC_AUX: case MSR_IA32_TSC: return X86EMUL_OKAY; } for ( idx = 0; idx < MSR_INDEX_MAX; idx++ ) { if ( msr_index[idx] == reg ) { c->msr[idx] = val; return X86EMUL_OKAY; } } x86_emul_hw_exception(13, 0, ctxt); return X86EMUL_EXCEPTION; } #define SET(h) .h = fuzz_##h static const struct x86_emulate_ops all_fuzzer_ops = { SET(read), SET(insn_fetch), SET(write), SET(cmpxchg), SET(rep_ins), SET(rep_outs), SET(rep_movs), SET(rep_stos), SET(read_segment), SET(write_segment), SET(read_io), SET(write_io), SET(read_cr), SET(write_cr), SET(read_msr), SET(write_msr), SET(wbinvd), SET(invlpg), .get_fpu = emul_test_get_fpu, .put_fpu = emul_test_put_fpu, .cpuid = emul_test_cpuid, }; #undef SET static void setup_fpu_exception_handler(void) { /* FIXME - just disable exceptions for now */ unsigned long a; asm volatile ( "fnclex"); a = 0x37f; /* FCW_DEFAULT in Xen */ asm volatile ( "fldcw %0" :: "m" (a)); a = 0x1f80; /* MXCSR_DEFAULT in Xen */ asm volatile ( "ldmxcsr %0" :: "m" (a) ); } static void dump_state(struct x86_emulate_ctxt *ctxt) { struct fuzz_state *s = ctxt->data; const struct fuzz_corpus *c = s->corpus; struct cpu_user_regs *regs = ctxt->regs; uint64_t val = 0; printf(" -- State -- \n"); printf("addr / sp size: %d / %d\n", ctxt->addr_size, ctxt->sp_size); printf(" cr0: %lx\n", c->cr[0]); printf(" cr3: %lx\n", c->cr[3]); printf(" cr4: %lx\n", c->cr[4]); printf(" rip: %"PRIx64"\n", regs->rip); fuzz_read_msr(MSR_EFER, &val, ctxt); printf("EFER: %"PRIx64"\n", val); } static bool long_mode_active(struct x86_emulate_ctxt *ctxt) { uint64_t val; if ( fuzz_read_msr(MSR_EFER, &val, ctxt) != X86EMUL_OKAY ) return false; return val & EFER_LMA; } static bool in_longmode(struct x86_emulate_ctxt *ctxt) { const struct fuzz_state *s = ctxt->data; const struct fuzz_corpus *c = s->corpus; return long_mode_active(ctxt) && c->segments[x86_seg_cs].attr.fields.l; } static void set_sizes(struct x86_emulate_ctxt *ctxt) { struct fuzz_state *s = ctxt->data; const struct fuzz_corpus *c = s->corpus; ctxt->lma = long_mode_active(ctxt); if ( in_longmode(ctxt) ) ctxt->addr_size = ctxt->sp_size = 64; else { ctxt->addr_size = c->segments[x86_seg_cs].attr.fields.db ? 32 : 16; ctxt->sp_size = c->segments[x86_seg_ss].attr.fields.db ? 32 : 16; } } #define CANONICALIZE(x) \ do { \ uint64_t _y = (x); \ if ( _y & (1ULL << 47) ) \ _y |= (~0ULL) << 48; \ else \ _y &= (1ULL << 48)-1; \ printf("Canonicalized %" PRIx64 " to %" PRIx64 "\n", x, _y); \ (x) = _y; \ } while( 0 ) /* Expects bitmap and regs to be defined */ #define CANONICALIZE_MAYBE(reg) \ if ( !(bitmap & (1 << CANONICALIZE_##reg)) ) \ CANONICALIZE(regs->reg); \ enum { HOOK_read, HOOK_insn_fetch, HOOK_write, HOOK_cmpxchg, HOOK_rep_ins, HOOK_rep_outs, HOOK_rep_movs, HOOK_rep_stos, HOOK_read_segment, HOOK_write_segment, HOOK_read_io, HOOK_write_io, HOOK_read_cr, HOOK_write_cr, HOOK_read_dr, HOOK_write_dr, HOOK_read_msr, HOOK_write_msr, HOOK_wbinvd, HOOK_cpuid, HOOK_inject_hw_exception, HOOK_inject_sw_interrupt, HOOK_get_fpu, HOOK_put_fpu, HOOK_invlpg, HOOK_vmfunc, CANONICALIZE_rip, CANONICALIZE_rsp, CANONICALIZE_rbp }; /* Expects bitmap to be defined */ #define MAYBE_DISABLE_HOOK(h) \ if ( bitmap & (1 << HOOK_##h) ) \ { \ s->ops.h = NULL; \ printf("Disabling hook "#h"\n"); \ } static void disable_hooks(struct x86_emulate_ctxt *ctxt) { struct fuzz_state *s = ctxt->data; const struct fuzz_corpus *c = s->corpus; unsigned long bitmap = c->options; /* See also sanitize_input, some hooks can't be disabled. */ MAYBE_DISABLE_HOOK(read); MAYBE_DISABLE_HOOK(insn_fetch); MAYBE_DISABLE_HOOK(write); MAYBE_DISABLE_HOOK(cmpxchg); MAYBE_DISABLE_HOOK(rep_ins); MAYBE_DISABLE_HOOK(rep_outs); MAYBE_DISABLE_HOOK(rep_movs); MAYBE_DISABLE_HOOK(rep_stos); MAYBE_DISABLE_HOOK(read_segment); MAYBE_DISABLE_HOOK(write_segment); MAYBE_DISABLE_HOOK(read_io); MAYBE_DISABLE_HOOK(write_io); MAYBE_DISABLE_HOOK(read_cr); MAYBE_DISABLE_HOOK(write_cr); MAYBE_DISABLE_HOOK(read_msr); MAYBE_DISABLE_HOOK(write_msr); MAYBE_DISABLE_HOOK(wbinvd); MAYBE_DISABLE_HOOK(cpuid); MAYBE_DISABLE_HOOK(get_fpu); MAYBE_DISABLE_HOOK(invlpg); } /* * Constrain input to architecturally-possible states where * the emulator relies on these * * In general we want the emulator to be as absolutely robust as * possible; which means that we want to minimize the number of things * it assumes about the input state. Tesing this means minimizing and * removing as much of the input constraints as possible. * * So we only add constraints that (in general) have been proven to * cause crashes in the emulator. * * For future reference: other constraints which might be necessary at * some point: * * - EFER.LMA => !EFLAGS.NT * - In VM86 mode, force segment... * - ...access rights to 0xf3 * - ...limits to 0xffff * - ...bases to below 1Mb, 16-byte aligned * - ...selectors to (base >> 4) */ static void sanitize_input(struct x86_emulate_ctxt *ctxt) { struct fuzz_state *s = ctxt->data; struct fuzz_corpus *c = s->corpus; struct cpu_user_regs *regs = &c->regs; unsigned long bitmap = c->options; /* Some hooks can't be disabled. */ c->options &= ~((1<error_code = 0; regs->entry_vector = 0; CANONICALIZE_MAYBE(rip); CANONICALIZE_MAYBE(rsp); CANONICALIZE_MAYBE(rbp); /* * CR0.PG can't be set if CR0.PE isn't set. Set is more interesting, so * set PE if PG is set. */ if ( c->cr[0] & X86_CR0_PG ) c->cr[0] |= X86_CR0_PE; /* EFLAGS.VM not available in long mode */ if ( long_mode_active(ctxt) ) regs->rflags &= ~X86_EFLAGS_VM; /* EFLAGS.VM implies 16-bit mode */ if ( regs->rflags & X86_EFLAGS_VM ) { c->segments[x86_seg_cs].attr.fields.db = 0; c->segments[x86_seg_ss].attr.fields.db = 0; } } int LLVMFuzzerInitialize(int *argc, char ***argv) { if ( !emul_test_init() ) { printf("Warning: Stack could not be made executable (%d).\n", errno); return 1; } return 0; } int LLVMFuzzerTestOneInput(const uint8_t *data_p, size_t size) { struct fuzz_state state = { .ops = all_fuzzer_ops, }; struct x86_emulate_ctxt ctxt = { .data = &state, .regs = &input.regs, .addr_size = 8 * sizeof(void *), .sp_size = 8 * sizeof(void *), }; int rc; /* Reset all global state variables */ memset(&input, 0, sizeof(input)); if ( size <= DATA_OFFSET ) { printf("Input too small\n"); return 1; } if ( size > sizeof(input) ) { printf("Input too large\n"); return 1; } memcpy(&input, data_p, size); state.corpus = &input; state.data_num = size - DATA_OFFSET; sanitize_input(&ctxt); disable_hooks(&ctxt); do { /* FIXME: Until we actually implement SIGFPE handling properly */ setup_fpu_exception_handler(); set_sizes(&ctxt); dump_state(&ctxt); rc = x86_emulate(&ctxt, &state.ops); printf("Emulation result: %d\n", rc); } while ( rc == X86EMUL_OKAY ); return 0; } unsigned int fuzz_minimal_input_size(void) { return DATA_OFFSET + 1; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/fuzz/x86_instruction_emulator/Makefile0000664000175000017500000000216413256712137021254 0ustar smbsmbXEN_ROOT=$(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk .PHONY: x86-insn-fuzz-all ifeq ($(CONFIG_X86_64),y) x86-insn-fuzz-all: x86-insn-fuzzer.a fuzz-emul.o afl else x86-insn-fuzz-all: endif x86_emulate: [ -L $@ ] || ln -sf $(XEN_ROOT)/xen/arch/x86/$@ x86_emulate/%: x86_emulate ; asm: [ -L $@ ] || ln -sf $(XEN_ROOT)/xen/include/asm-x86 $@ asm/%: asm ; x86_emulate.c x86_emulate.h: %: [ -L $* ] || ln -sf $(XEN_ROOT)/tools/tests/x86_emulator/$* CFLAGS += $(CFLAGS_xeninclude) -D__XEN_TOOLS__ -I. x86.h := asm/x86-vendors.h asm/x86-defns.h asm/msr-index.h x86_emulate.h := x86_emulate.h x86_emulate/x86_emulate.h $(x86.h) x86_emulate.o: x86_emulate.c x86_emulate/x86_emulate.c $(x86_emulate.h) fuzz-emul.o: $(x86_emulate.h) x86-insn-fuzzer.a: fuzz-emul.o x86_emulate.o $(AR) rc $@ $^ afl-harness: afl-harness.o fuzz-emul.o x86_emulate.o $(CC) $(CFLAGS) $^ -o $@ # Common targets .PHONY: all all: x86-insn-fuzz-all .PHONY: distclean distclean: clean rm -f x86_emulate x86_emulate.c x86_emulate.h asm .PHONY: clean clean: rm -f *.a *.o .*.d afl-harness .PHONY: install install: all .PHONY: afl afl: afl-harness xen-4.9.2/tools/firmware/0000775000175000017500000000000013256712137013431 5ustar smbsmbxen-4.9.2/tools/firmware/Rules.mk0000664000175000017500000000062013256712137015052 0ustar smbsmb# Firmware is a 32-bit target override XEN_TARGET_ARCH = x86_32 # User-supplied CFLAGS are not useful here. CFLAGS = EXTRA_CFLAGS_XEN_TOOLS = include $(XEN_ROOT)/tools/Rules.mk ifneq ($(debug),y) CFLAGS += -DNDEBUG endif CFLAGS += -Werror $(call cc-options-add,CFLAGS,CC,$(EMBEDDED_EXTRA_CFLAGS)) # Extra CFLAGS suitable for an embedded type of environment. CFLAGS += -fno-builtin -msoft-float xen-4.9.2/tools/firmware/etherboot/0000775000175000017500000000000013256712137015424 5ustar smbsmbxen-4.9.2/tools/firmware/etherboot/Makefile0000664000175000017500000000222113256712137017061 0ustar smbsmb override XEN_TARGET_ARCH = x86_32 XEN_ROOT = $(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk include Config ifeq ($(GIT_HTTP),y) IPXE_GIT_URL ?= http://git.ipxe.org/ipxe.git else IPXE_GIT_URL ?= git://git.ipxe.org/ipxe.git endif IPXE_GIT_TAG := 827dd1bfee67daa683935ce65316f7e0f057fe1c IPXE_TARBALL_URL ?= $(XEN_EXTFILES_URL)/ipxe-git-$(IPXE_GIT_TAG).tar.gz D=ipxe T=ipxe.tar.gz ROMS = $(addprefix $D/src/bin/, $(addsuffix .rom, $(ETHERBOOT_NICS))) .NOTPARALLEL: .PHONY: all all: $(ROMS) %.rom: $D/src/arch/i386/Makefile $(MAKE) -C $D/src bin/$(*F).rom $T: if ! $(FETCHER) _$T $(IPXE_TARBALL_URL); then \ $(GIT) clone $(IPXE_GIT_URL) $D.git; \ (cd $D.git && $(GIT) archive --format=tar --prefix=$D/ \ $(IPXE_GIT_TAG) | gzip >../_$T); \ rm -rf $D.git; \ fi mv _$T $T $D/src/arch/i386/Makefile: $T Config rm -rf $D gzip -dc $T | tar xf - for i in $$(cat patches/series) ; do \ patch -d $D -p1 --quiet >$@ $D/src/bin/NIC: $D/src/arch/i386/Makefile $(MAKE) -C $D/src bin/NIC .PHONY: clean clean: rm -rf $D $D.git *~ _$T $T .PHONY: distclean distclean: clean rm -rf $T xen-4.9.2/tools/firmware/etherboot/Config0000664000175000017500000000025113256712137016552 0ustar smbsmb CFLAGS += -UPXE_DHCP_STRICT CFLAGS += -DPXE_DHCP_STRICT CFLAGS += -UNO_POST_PROMPT CFLAGS += -DNO_POST_PROMPT CFLAGS += -UCONSOLE_SERIAL CFLAGS += -DCONSOLE_SERIAL=1 xen-4.9.2/tools/firmware/etherboot/README0000664000175000017500000000332713256712137016311 0ustar smbsmb This directory builds gPXE option ROMs from the gPXE source tarball. We provide this tarball in our source so that the option ROMs can be reconstructed from source, allowing easier debugging and fulfilling the terms of the GPL. We make a few non-standard settings, most to do with timeouts and when to give up, and for stricter DHCP spec compliance. ---------------------------------------- Since gPXE upstream do not appear to distribute tarballs, we generated one ourselves with the following runes (to extract the tree as on 25th September 2008): git clone git://git.etherboot.org/scm/gpxe.git cd gpxe git archive --format=tar --prefix=gpxe/ c24bc349ead939d90b5784dbff3cd9fdb9d83ba8 | gzip >../gpxe-git-snapshot.tar.gz ---------------------------------------- Previously, an image from Rom-o-matic.net was provided here, fetched from the following URL: http://rom-o-matic.net/5.4.3/build.php?version=5.4.3&F=ignore&nic=rtl8139%3Artl8139+--+%5B0x10ec%2C0x8139%5D&ofmt=Binary+ROM+Image%28.zrom%29&arch=i386&ASK_BOOT=-1&BOOT_FIRST=BOOT_NIC&BOOT_SECOND=BOOT_NOTHING&BOOT_THIRD=BOOT_NOTHING&BOOT_INDEX=0&STATIC_CLIENT_IP=&STATIC_SUBNET_MASK=&STATIC_SERVER_IP=&STATIC_GATEWAY_IP=&STATIC_BOOTFILE=&EXIT_ON_FILE_LOAD_ERROR=on&DHCP_CLIENT_ID=&DHCP_CLIENT_ID_LEN=&DHCP_CLIENT_ID_TYPE=&DHCP_USER_CLASS=&DHCP_USER_CLASS_LEN=&ALLOW_ONLY_ENCAPSULATED=on&DEFAULT_BOOTFILE=&CONGESTED=on&BACKOFF_LIMIT=7&TIMEOUT=180&TRY_FLOPPY_FIRST=0&EXIT_IF_NO_OFFER=on&TAGGED_IMAGE=on&ELF_IMAGE=on&PXE_IMAGE=on&DOWNLOAD_PROTO_TFTP=on&COMCONSOLE=0x3F8&CONSPEED=9600&COMPARM=0x03&PXE_EXPORT=on&CONFIG_PCI=on&CONFIG_ISA=on&BUILD_ID=&PCBIOS=on&PXE_DHCP_STRICT=on&A=Get+ROM The mkhex script in tools/firmware/hvmloader will make the header file from the downloaded image. xen-4.9.2/tools/firmware/etherboot/patches/0000775000175000017500000000000013256712137017053 5ustar smbsmbxen-4.9.2/tools/firmware/etherboot/patches/boot_prompt_option.patch0000664000175000017500000000152413256712137024032 0ustar smbsmb--- a/src/arch/x86/prefix/romprefix.S 2016-10-10 13:09:18.126031400 +0100 +++ b/src/arch/x86/prefix/romprefix.S 2016-10-10 13:11:22.930680278 +0100 @@ -468,6 +468,7 @@ testb $PCI_FUNC_MASK, init_pci_busdevfn jnz no_shell .endif +#ifndef NO_POST_PROMPT /* Prompt for POST-time shell */ movw $init_message_prompt, %si xorw %di, %di @@ -495,6 +496,7 @@ pushw %cs call exec +#endif no_shell: movb $( '\n' ), %al xorw %di, %di call print_character @@ -636,6 +638,7 @@ init_message_int19: .asciz " INT19" .size init_message_int19, . - init_message_int19 +#ifndef NO_POST_PROMPT init_message_prompt: .asciz "\nPress Ctrl-B to configure " .size init_message_prompt, . - init_message_prompt @@ -645,6 +648,7 @@ init_message_done: .asciz "\n\n" .size init_message_done, . - init_message_done +#endif /* PCI bus:dev.fn * xen-4.9.2/tools/firmware/etherboot/patches/series0000664000175000017500000000003113256712137020262 0ustar smbsmbboot_prompt_option.patch xen-4.9.2/tools/firmware/Makefile0000664000175000017500000000567113256712137015102 0ustar smbsmbXEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk # hvmloader is a 32-bit protected mode binary. TARGET := hvmloader/hvmloader INST_DIR := $(DESTDIR)$(XENFIRMWAREDIR) SUBDIRS-y := SUBDIRS-$(CONFIG_OVMF) += ovmf-dir SUBDIRS-$(CONFIG_SEABIOS) += seabios-dir SUBDIRS-$(CONFIG_ROMBIOS) += rombios SUBDIRS-$(CONFIG_ROMBIOS) += vgabios SUBDIRS-$(CONFIG_ROMBIOS) += etherboot SUBDIRS-y += hvmloader LD32BIT-$(CONFIG_FreeBSD) := LD32BIT_FLAG=-melf_i386_fbsd ovmf-dir: GIT=$(GIT) $(XEN_ROOT)/scripts/git-checkout.sh $(OVMF_UPSTREAM_URL) $(OVMF_UPSTREAM_REVISION) ovmf-dir cp ovmf-makefile ovmf-dir/Makefile; seabios-dir: GIT=$(GIT) $(XEN_ROOT)/scripts/git-checkout.sh $(SEABIOS_UPSTREAM_URL) $(SEABIOS_UPSTREAM_REVISION) seabios-dir cp seabios-config seabios-dir/.config; $(MAKE) -C seabios-dir olddefconfig .PHONY: all all: $(SUBDIRS-y) ifeq ($(CONFIG_ROMBIOS),y) @set -e; if [ $$((`( bcc -v 2>&1 | grep version || echo 0.0.0 ) | cut -d' ' -f 3 | awk -F. '{ printf "0x%02x%02x%02x", $$1, $$2, $$3}'`)) -lt $$((0x00100e)) ] ; then \ echo "==========================================================================="; \ echo "Require dev86 rpm or bin86 & bcc debs version >= 0.16.14 to build firmware!"; \ echo "(visit http://www.debath.co.uk/dev86/ for more information)"; \ echo "==========================================================================="; \ false ; \ fi endif $(MAKE) $(LD32BIT-y) CC=$(CC) PYTHON=$(PYTHON) subdirs-$@ .PHONY: install install: all [ -d $(INST_DIR) ] || $(INSTALL_DIR) $(INST_DIR) [ ! -e $(TARGET) ] || $(INSTALL_DATA) $(TARGET) $(INST_DIR) ifeq ($(CONFIG_SEABIOS),y) $(INSTALL_DATA) seabios-dir/out/bios.bin $(INST_DIR)/seabios.bin endif ifeq ($(CONFIG_OVMF),y) $(INSTALL_DATA) ovmf-dir/ovmf.bin $(INST_DIR)/ovmf.bin endif .PHONY: clean clean: subdirs-clean .PHONY: distclean distclean: subdirs-distclean subdir-distclean-etherboot: .phony $(MAKE) -C etherboot distclean subdir-distclean-ovmf-dir: .phony rm -rf ovmf-dir ovmf-dir-remote subdir-distclean-seabios-dir: .phony rm -rf seabios-dir seabios-dir-remote .PHONY: ovmf-dir-force-update ovmf-dir-force-update: ovmf-dir set -ex; \ if [ "$(OVMF_UPSTREAM_REVISION)" ]; then \ cd ovmf-dir-remote; \ $(GIT) fetch origin; \ $(GIT) reset --hard $(OVMF_UPSTREAM_REVISION); \ fi subdir-clean-ovmf-dir: set -e; if test -d ovmf-dir/.; then \ $(MAKE) -C ovmf-dir clean; \ fi .PHONY: seabios-dir-force-update seabios-dir-force-update: seabios-dir set -ex; \ if [ "$(SEABIOS_UPSTREAM_REVISION)" ]; then \ cd seabios-dir-remote; \ $(GIT) fetch origin; \ $(GIT) reset --hard $(SEABIOS_UPSTREAM_REVISION); \ fi subdir-clean-seabios-dir: set -e; if test -d seabios-dir/.; then \ $(MAKE) -C seabios-dir clean; \ fi subtree-force-update: ifeq ($(CONFIG_SEABIOS),y) $(MAKE) seabios-dir-force-update endif ifeq ($(CONFIG_OVMF),y) $(MAKE) ovmf-dir-force-update endif subtree-force-update-all: $(MAKE) seabios-dir-force-update $(MAKE) ovmf-dir-force-update xen-4.9.2/tools/firmware/rombios/0000775000175000017500000000000013256712137015103 5ustar smbsmbxen-4.9.2/tools/firmware/rombios/32bit/0000775000175000017500000000000013256712137016026 5ustar smbsmbxen-4.9.2/tools/firmware/rombios/32bit/util.h0000664000175000017500000000242213256712137017154 0ustar smbsmb#ifndef UTIL_H #define UTIL_H #include void outb(uint16_t addr, uint8_t val); void outw(uint16_t addr, uint16_t val); void outl(uint16_t addr, uint32_t val); uint8_t inb(uint16_t addr); uint16_t inw(uint16_t addr); uint32_t inl(uint16_t addr); void mssleep(uint32_t time); char *itoa(char *a, unsigned int i); int strcmp(const char *cs, const char *ct); int strncmp(const char *s1, const char *s2, uint32_t n); void *memcpy(void *dest, const void *src, unsigned n); void *memmove(void *dest, const void *src, unsigned n); char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, unsigned n); unsigned strlen(const char *s); void * memset(void *s, int c, unsigned n); int memcmp(const void *s1, const void *s2, unsigned n); void cpuid(uint32_t idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); void byte_to_hex(char *digits, uint8_t byte); void uuid_to_string(char *dest, uint8_t *uuid); int printf(const char *fmt, ...); static inline uint8_t mmio_readb(uint8_t *addr) { return *(volatile uint8_t *)addr; } static inline uint16_t mmio_readw(uint16_t *addr) { return *(volatile uint16_t *)addr; } static inline uint32_t mmio_readl(uint32_t *addr) { return *(volatile uint32_t *)addr; } struct acpi_20_rsdp *find_rsdp(void); #endif xen-4.9.2/tools/firmware/rombios/32bit/mkhex0000664000175000017500000000160613256712137017070 0ustar smbsmb#!/bin/sh # # mkhex: Generate C embeddable hexdumps # # Leendert van Doorn, leendert@watson.ibm.com # Copyright (c) 2005, International Business Machines Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # this program; If not, see . # echo "unsigned $1[] = {" od -v -t x $2 | sed 's/^[0-9]* */0x/' | sed 's/ */, 0x/g' | sed 's/$/,/' | sed 's/0x,//' | sed 's/^[0-9]*,//' echo "};" xen-4.9.2/tools/firmware/rombios/32bit/pmm.c0000664000175000017500000003660413256712137016774 0ustar smbsmb/* * pmm.c - POST(Power On Self Test) Memory Manager * according to the specification described in * http://www.phoenix.com/NR/rdonlyres/873A00CF-33AC-4775-B77E-08E7B9754993/0/specspmm101.pdf * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (C) 2009 FUJITSU LIMITED * * Author: Kouya Shimura */ /* * Algorithm: * * This is not a fast storage allocator but simple one. There is no * segregated management by block size and it does nothing special for * avoiding the fragmentation. * * The allocation algorithm is a first-fit. All memory blocks are * managed by linear single linked list in order of the address. * (i.e. There is no backward pointer) It searches the first available * equal or larger block from the head (lowest address) of memory * heap. The larger block is splitted into two blocks unless one side * becomes too small. * * For de-allocation, the specified block is just marked as available * and it does nothing else. Thus, the fragmentation will occur. The * collection of continuous available blocks are done on the search * phase of another block allocation. * * The following is an abstract of this algorithm. The actual code * looks complicated on account of alignment and checking the handle. * * static memblk_t * * alloc(heap_t *heap, uint32_t size) * { * static memblk_t *mb; * for_each_memblk(heap, mb) // search memory blocks * if (memblk_is_avail(mb)) * { * collect_avail_memblks(heap, mb); * if (size <= memblk_bufsize(mb)) * { * split_memblk(mb, size); * set_inuse(mb); * return mb; * } * } * return NULL; * } */ #include #include #include "config.h" #include "e820.h" #include "util.h" #define DEBUG_PMM 0 #define __stringify(a) #a #define stringify(a) __stringify(a) #define ASSERT(_expr, _action) \ if (!(_expr)) { \ printf("ASSERTION FAIL: %s %s:%d %s()\n", \ stringify(_expr), __FILE__, __LINE__, __func__); \ _action; \ } else #if DEBUG_PMM # define PMM_DEBUG(format, p...) printf("PMM " format, ##p) #else # define PMM_DEBUG(format, p...) #endif struct pmmAllocArgs { uint16_t function; uint32_t length; uint32_t handle; uint16_t flags; } __attribute__ ((packed)); struct pmmFindArgs { uint16_t function; uint32_t handle; } __attribute__ ((packed)); struct pmmDeallocateArgs { uint16_t function; uint32_t buffer; } __attribute__ ((packed)); #define PMM_FUNCTION_ALLOCATE 0 #define PMM_FUNCTION_FIND 1 #define PMM_FUNCTION_DEALLOC 2 #define PARAGRAPH_LENGTH 16 // unit of length #define PMM_HANDLE_ANONYMOUS 0xffffffff #define PMM_FLAGS_MEMORY_TYPE_MASK 0x0003 #define PMM_FLAGS_MEMORY_INVALID 0 #define PMM_FLAGS_MEMORY_CONVENTIONAL 1 // 0 to 1MB #define PMM_FLAGS_MEMORY_EXTENDED 2 // 1MB to 4GB #define PMM_FLAGS_MEMORY_ANY 3 // whichever is available #define PMM_FLAGS_ALIGINMENT 0x0004 /* Error code */ #define PMM_ENOMEM (0) // Out of memory, duplicate handle #define PMM_EINVAL (-1) // Invalid argument #define ALIGN_UP(addr, size) (((addr)+((size)-1))&(~((size)-1))) #define ALIGN_DOWN(addr, size) ((addr)&(~((size)-1))) typedef struct memblk { uint32_t magic; // inuse or available struct memblk *next; // points the very next of this memblk uint32_t handle; // identifier of this block uint32_t __fill; // for 16byte alignment, not used uint8_t buffer[0]; } memblk_t; typedef struct heap { memblk_t *head; // start address of heap memblk_t *end; // end address of heap } heap_t; #define HEAP_NOT_INITIALIZED (memblk_t *)-1 #define HEAP_ALIGNMENT 16 /* * PMM handles two memory heaps, the caller chooses either. * * - conventional memroy (below 1MB) * In HVM, the area is fixed. 0x00010000-0x0007FFFF * (LOWHEAP_SIZE bytes from LOWHEAP_PHYSICAL_ADDRESS) * * - extended memory (start at 1MB, below 4GB) * In HVM, the area starts at memory address 0x00100000. * The end address is variable. We read low RAM address from e820 table. * * The following struct must be located in the data segment since bss * in 32bitbios doesn't be relocated. */ static struct { heap_t heap; // conventional memory heap_t ext_heap; // extended memory } pmm_data = { {HEAP_NOT_INITIALIZED, NULL}, {NULL, NULL} }; /* These values are private use, not a spec in PMM */ #define MEMBLK_MAGIC_INUSE 0x2A4D4D50 // 'PMM*' #define MEMBLK_MAGIC_AVAIL 0x5F4D4D50 // 'PMM_' #define memblk_is_inuse(_mb) ((_mb)->magic == MEMBLK_MAGIC_INUSE) #define memblk_is_avail(_mb) ((_mb)->magic == MEMBLK_MAGIC_AVAIL) static void set_inuse(memblk_t *mb, uint32_t handle) { mb->magic = MEMBLK_MAGIC_INUSE; mb->handle = handle; } static void set_avail(memblk_t *mb) { mb->magic = MEMBLK_MAGIC_AVAIL; mb->handle = PMM_HANDLE_ANONYMOUS; } #define MEMBLK_HEADER_SIZE ((int)(&((memblk_t *)0)->buffer)) #define MIN_MEMBLK_SIZE (MEMBLK_HEADER_SIZE + PARAGRAPH_LENGTH) #define memblk_size(_mb) ((void *)((_mb)->next) - (void *)(_mb)) #define memblk_buffer(_mb) ((uint32_t)(&(_mb)->buffer)) #define memblk_bufsize(_mb) (memblk_size(_mb) - MEMBLK_HEADER_SIZE) #define buffer_memblk(_buf) (memblk_t *)((_buf) - MEMBLK_HEADER_SIZE) #define memblk_loop_mbondition(_h, _mb) \ (((_mb) < (_h)->end) && (/* avoid infinite loop */ (_mb) < (_mb)->next)) #define for_each_memblk(_h, _mb) \ for ((_mb) = (_h)->head; \ memblk_loop_mbondition(_h, _mb); \ (_mb) = (_mb)->next) #define for_remain_memblk(_h, _mb) \ for (; \ memblk_loop_mbondition(_h, _mb); \ (_mb) = (_mb)->next) /* * <-size-> * +==================+======+ +========+========+======+ * | avail | | | avail | avail | | * | memblk |memblk|... | memblk | memblk |memblk|... * +==================+======+ => +========+========+======+ * ^ | ^ | ^ | ^ | ^ | ^ * | |next | |next| |next | |next | |next| * | \________________/ \____/ \______/ \______/ \____/ * | ^ * | | * mb +- sb(return value) */ static memblk_t * split_memblk(memblk_t *mb, uint32_t size) { memblk_t *sb = (void *)memblk_buffer(mb) + size; /* Only split if the remaining fragment is big enough. */ if ( (memblk_bufsize(mb) - size) < MIN_MEMBLK_SIZE) return mb; sb->next = mb->next; set_avail(sb); mb->next = sb; return sb; } /* * +======+======+======+======+ +=================+======+ * |avail |avail |avail |inuse | | avail |inuse | * |memblk|memblk|memblk|memblk|... | memblk |memblk|... * +======+======+======+======+ => +=================+======+ * ^ | ^ | ^ | ^ | ^ | ^ | ^ * | |next| |next| |next| |next| |next | |next| * | \____/ \____/ \____/ \____/ \_______________/ \____/ * | * mb */ static void collect_avail_memblks(heap_t *heap, memblk_t *mb) { memblk_t *nb = mb->next; for_remain_memblk ( heap, nb ) if ( memblk_is_inuse(nb) ) break; mb->next = nb; } static void pmm_init_heap(heap_t *heap, uint32_t from_addr, uint32_t to_addr) { memblk_t *mb = (memblk_t *)ALIGN_UP(from_addr, HEAP_ALIGNMENT); mb->next = (memblk_t *)ALIGN_DOWN(to_addr, HEAP_ALIGNMENT); set_avail(mb); heap->head = mb; heap->end = mb->next; } static void pmm_initalize(void) { int i, e820_nr = *E820_NR; struct e820entry *e820 = E820; /* Extended memory: RAM below 4GB, 0x100000-0xXXXXXXXX */ for ( i = 0; i < e820_nr; i++ ) { if ( (e820[i].type == E820_RAM) && (e820[i].addr >= 0x00100000) ) { pmm_init_heap(&pmm_data.ext_heap, e820[i].addr, e820[i].addr + e820[i].size); break; } } /* convectional memory: RAM below 1MB, 0x10000-0x7FFFF */ pmm_init_heap(&pmm_data.heap, LOWHEAP_PHYSICAL_ADDRESS, LOWHEAP_PHYSICAL_ADDRESS+LOWHEAP_SIZE); } static uint32_t pmm_max_avail_length(heap_t *heap) { memblk_t *mb; uint32_t size, max = 0; for_each_memblk ( heap, mb ) { if ( !memblk_is_avail(mb) ) continue; collect_avail_memblks(heap, mb); size = memblk_bufsize(mb); if ( size > max ) max = size; } return (max / PARAGRAPH_LENGTH); } static memblk_t * first_fit(heap_t *heap, uint32_t size, uint32_t handle, uint32_t flags) { memblk_t *mb; int32_t align = 0; if ( flags & PMM_FLAGS_ALIGINMENT ) align = ((size ^ (size - 1)) >> 1) + 1; for_each_memblk ( heap, mb ) { if ( memblk_is_avail(mb) ) { collect_avail_memblks(heap, mb); if ( align ) { uint32_t addr = memblk_buffer(mb); uint32_t offset = ALIGN_UP(addr, align) - addr; if ( offset > 0 ) { ASSERT(offset >= MEMBLK_HEADER_SIZE, continue); if ( (offset + size) > memblk_bufsize(mb) ) continue; mb = split_memblk(mb, offset - MEMBLK_HEADER_SIZE); return mb; } } if ( size <= memblk_bufsize(mb) ) return mb; } else { ASSERT(memblk_is_inuse(mb), return NULL); /* Duplication check for handle. */ if ( (handle != PMM_HANDLE_ANONYMOUS) && (mb->handle == handle) ) return NULL; } } return NULL; } static memblk_t * pmm_find_handle(heap_t *heap, uint32_t handle) { memblk_t *mb; if ( handle == PMM_HANDLE_ANONYMOUS ) return NULL; for_each_memblk ( heap, mb ) if ( mb->handle == handle ) return mb; return NULL; } /* * allocate a memory block of the specified type and size, and returns * the address of the memory block. * * A client-specified identifier to be associated with the allocated * memory block. A handle of 0xFFFFFFFF indicates that no identifier * should be associated with the block. Such a memory block is known * as an "anonymous" memory block and cannot be found using the * pmmFind function. If a specified handle for a requested memory * block is already used in a currently allocated memory block, the * error value of 0x00000000 is returned * * If length is 0x00000000, no memory is allocated and the value * returned is the size of the largest memory block available for the * memory type specified in the flags parameter. The alignment bit in * the flags register is ignored when calculating the largest memory * block available. * * If a specified handle for a requested memory block is already used * in a currently allocated memory block, the error value of * 0x00000000 is returned. * * A return value of 0x00000000 indicates that an error occurred and * no memory has been allocated. */ static uint32_t pmmAllocate(uint32_t length, uint32_t handle, uint16_t flags) { heap_t *heap; memblk_t *mb; uint32_t size; switch ( flags & PMM_FLAGS_MEMORY_TYPE_MASK ) { case PMM_FLAGS_MEMORY_CONVENTIONAL: heap = &pmm_data.heap; break; case PMM_FLAGS_MEMORY_EXTENDED: case PMM_FLAGS_MEMORY_ANY: /* XXX: ignore conventional memory for now */ heap = &pmm_data.ext_heap; break; default: return PMM_EINVAL; } /* return the largest memory block available */ if ( length == 0 ) return pmm_max_avail_length(heap); size = length * PARAGRAPH_LENGTH; mb = first_fit(heap, size, handle, flags); if ( mb == NULL ) return PMM_ENOMEM; /* duplication check for handle */ if ( handle != PMM_HANDLE_ANONYMOUS ) { memblk_t *nb = mb->next; for_remain_memblk(heap, nb) if (nb->handle == handle) return PMM_ENOMEM; } split_memblk(mb, size); set_inuse(mb, handle); return memblk_buffer(mb); } /* * returns the address of the memory block associated with the * specified handle. * * A return value of 0x00000000 indicates that the handle does not * correspond to a currently allocated memory block. */ static uint32_t pmmFind(uint32_t handle) { memblk_t *mb; if ( handle == PMM_HANDLE_ANONYMOUS ) return 0; mb = pmm_find_handle(&pmm_data.heap, handle); if ( mb == NULL ) mb = pmm_find_handle(&pmm_data.ext_heap, handle); return mb ? memblk_buffer(mb) : 0; } /* * frees the specified memory block that was previously allocated by * pmmAllocate. * * If the memory block was deallocated correctly, the return value is * 0x00000000. If there was an error, the return value is non-zero. */ static uint32_t pmmDeallocate(uint32_t buffer) { memblk_t *mb = buffer_memblk(buffer); if ( !memblk_is_inuse(mb) ) return PMM_EINVAL; set_avail(mb); return 0; } union pmm_args { uint16_t function; struct pmmAllocArgs alloc; struct pmmFindArgs find; struct pmmDeallocateArgs dealloc; } __attribute__ ((packed)); /* * entry function of all PMM services. * * Values returned to the caller are placed in the DX:AX register * pair. The flags and all registers, other than DX and AX, are * preserved across calls to PMM services. */ uint32_t pmm(void *argp) { union pmm_args *ap = argp; uint32_t ret = PMM_EINVAL; if ( pmm_data.heap.head == HEAP_NOT_INITIALIZED ) pmm_initalize(); switch ( ap->function ) { case PMM_FUNCTION_ALLOCATE: ret = pmmAllocate(ap->alloc.length, ap->alloc.handle, ap->alloc.flags); PMM_DEBUG("Alloc length=%x handle=%x flags=%x ret=%x\n", ap->alloc.length, ap->alloc.handle, ap->alloc.flags, ret); break; case PMM_FUNCTION_FIND: ret = pmmFind(ap->find.handle); PMM_DEBUG("Find handle=%x ret=%x\n", ap->find.handle, ret); break; case PMM_FUNCTION_DEALLOC: ret = pmmDeallocate(ap->dealloc.buffer); PMM_DEBUG("Dealloc buffer=%x ret=%x\n", ap->dealloc.buffer, ret); break; default: PMM_DEBUG("Invalid function:%d\n", ap->function); break; } return ret; } xen-4.9.2/tools/firmware/rombios/32bit/tcgbios/0000775000175000017500000000000013256712137017460 5ustar smbsmbxen-4.9.2/tools/firmware/rombios/32bit/tcgbios/tcgbios.h0000664000175000017500000001627513256712137021276 0ustar smbsmb#ifndef TCGBIOS_H #define TCGBIOS_H /* TCPA ACPI definitions */ #define TCPA_ACPI_CLASS_CLIENT 0 #define TCPA_ACPI_CLASS_SERVER 1 /* Define for section 12.3 */ #define TCG_PC_OK 0x0 #define TCG_PC_TPMERROR 0x1 #define TCG_PC_LOGOVERFLOW 0x2 #define TCG_PC_UNSUPPORTED 0x3 #define TPM_ALG_SHA 0x4 #define TCG_MAGIC 0x41504354L #define TCG_VERSION_MAJOR 1 #define TCG_VERSION_MINOR 2 #define TPM_OK 0x0 #define TPM_RET_BASE 0x1 #define TCG_GENERAL_ERROR (TPM_RET_BASE + 0x0) #define TCG_TPM_IS_LOCKED (TPM_RET_BASE + 0x1) #define TCG_NO_RESPONSE (TPM_RET_BASE + 0x2) #define TCG_INVALID_RESPONSE (TPM_RET_BASE + 0x3) #define TCG_INVALID_ACCESS_REQUEST (TPM_RET_BASE + 0x4) #define TCG_FIRMWARE_ERROR (TPM_RET_BASE + 0x5) #define TCG_INTEGRITY_CHECK_FAILED (TPM_RET_BASE + 0x6) #define TCG_INVALID_DEVICE_ID (TPM_RET_BASE + 0x7) #define TCG_INVALID_VENDOR_ID (TPM_RET_BASE + 0x8) #define TCG_UNABLE_TO_OPEN (TPM_RET_BASE + 0x9) #define TCG_UNABLE_TO_CLOSE (TPM_RET_BASE + 0xa) #define TCG_RESPONSE_TIMEOUT (TPM_RET_BASE + 0xb) #define TCG_INVALID_COM_REQUEST (TPM_RET_BASE + 0xc) #define TCG_INVALID_ADR_REQUEST (TPM_RET_BASE + 0xd) #define TCG_WRITE_BYTE_ERROR (TPM_RET_BASE + 0xe) #define TCG_READ_BYTE_ERROR (TPM_RET_BASE + 0xf) #define TCG_BLOCK_WRITE_TIMEOUT (TPM_RET_BASE + 0x10) #define TCG_CHAR_WRITE_TIMEOUT (TPM_RET_BASE + 0x11) #define TCG_CHAR_READ_TIMEOUT (TPM_RET_BASE + 0x12) #define TCG_BLOCK_READ_TIMEOUT (TPM_RET_BASE + 0x13) #define TCG_TRANSFER_ABORT (TPM_RET_BASE + 0x14) #define TCG_INVALID_DRV_FUNCTION (TPM_RET_BASE + 0x15) #define TCG_OUTPUT_BUFFER_TOO_SHORT (TPM_RET_BASE + 0x16) #define TCG_FATAL_COM_ERROR (TPM_RET_BASE + 0x17) #define TCG_INVALID_INPUT_PARA (TPM_RET_BASE + 0x18) #define TCG_TCG_COMMAND_ERROR (TPM_RET_BASE + 0x19) #define TCG_INTERFACE_SHUTDOWN (TPM_RET_BASE + 0x20) //define TCG_PC_UNSUPPORTED (TPM_RET_BASE + 0x21) #define TCG_PC_TPM_NOT_PRESENT (TPM_RET_BASE + 0x22) #define TCG_PC_TPM_DEACTIVATED (TPM_RET_BASE + 0x23) #define TPM_INVALID_ADR_REQUEST TCG_INVALID_ADR_REQUEST #define TPM_IS_LOCKED TCG_TPM_IS_LOCKED #define TPM_INVALID_DEVICE_ID TCG_INVALID_DEVICE_ID #define TPM_INVALID_VENDOR_ID TCG_INVALID_VENDOR_ID //define TPM_RESERVED_REG_INVALID #define TPM_FIRMWARE_ERROR TCG_FIRMWARE_ERROR #define TPM_UNABLE_TO_OPEN TCG_UNABLE_TO_OPEN #define TPM_UNABLE_TO_CLOSE TCG_UNABLE_TO_CLOSE #define TPM_INVALID_RESPONSE TCG_INVALID_RESPONSE #define TPM_RESPONSE_TIMEOUT TCG_RESPONSE_TIMEOUT #define TPM_INVALID_ACCESS_REQUEST TCG_INVALID_ACCESS_REQUEST #define TPM_TRANSFER_ABORT TCG_TRANSFER_ABORT #define TPM_GENERAL_ERROR TCG_GENERAL_ERROR #define TPM_ST_CLEAR 0x0 #define TPM_ST_STATE 0x1 #define TPM_ST_DEACTIVATED 0x2 /* event types: 10.4.1 / table 11 */ #define EV_POST_CODE 1 #define EV_SEPARATOR 4 #define EV_ACTION 5 #define EV_EVENT_TAG 6 #define EV_COMPACT_HASH 12 #define EV_IPL 13 #define EV_IPL_PARTITION_DATA 14 // MA Driver defines #define CODE_MAInitTPM 0x01 #define CODE_MAHashAllExtendTPM 0x02 #define CODE_MAPhysicalPresenceTPM 0x03 /* vendor specific ones */ #define CODE_MAIsTPMPresent 0x80 #define CODE_MAHashAll 0x81 #define CODE_MATransmit 0x82 /* indices for commands to be sent via proprietary _TCG_SendCommand function */ #define IDX_CMD_TPM_Startup_0x01 0 #define IDX_CMD_TSC_PhysicalPresence_0x20 1 #define IDX_CMD_TSC_PhysicalPresence_0x08 2 #define IDX_CMD_TSC_PhysicalPresence_0x100 3 #define IDX_CMD_TSC_PhysicalPresence_0x10 4 #define IDX_CMD_TPM_PhysicalEnable 5 #define IDX_CMD_TPM_PhysicalSetDeactivated_0x00 6 #define IDX_CMD_TPM_SHA1Start 7 /* hardware registers for TPM TIS */ #define TPM_ACCESS 0x0 #define TPM_INT_ENABLE 0x8 #define TPM_INT_VECTOR 0xc #define TPM_INT_STATUS 0x10 #define TPM_INTF_CAPABILITY 0x14 #define TPM_STS 0x18 #define TPM_DATA_FIFO 0x24 #define TPM_DID_VID 0xf00 #define TPM_RID 0xf04 /* address of locality 0 (TIS) */ #define TPM_TIS_BASE_ADDRESS 0xfed40000 #define STATUS_FLAG_SHUTDOWN (1 << 0) /* Input and Output blocks for the TCG BIOS commands */ struct hleei_short { uint16_t ipblength; uint16_t reserved; uint32_t hashdataptr; uint32_t hashdatalen; uint32_t pcrindex; uint32_t logdataptr; uint32_t logdatalen; } __attribute__((packed)); struct hleei_long { uint16_t ipblength; uint16_t reserved; uint32_t hashdataptr; uint32_t hashdatalen; uint32_t pcrindex; uint32_t reserved2; uint32_t logdataptr; uint32_t logdatalen; } __attribute__((packed)); struct hleeo { uint16_t opblength; uint16_t reserved; uint32_t eventnumber; uint8_t hashvalue[20]; } __attribute__((packed)); struct pttti { uint16_t ipblength; uint16_t reserved; uint16_t opblength; uint16_t reserved2; uint8_t tpmoperandin[0]; } __attribute__((packed)); struct pttto { uint16_t opblength; uint16_t reserved; uint8_t tpmoperandout[0]; }; struct hlei { uint16_t ipblength; uint16_t reserved; uint32_t hashdataptr; uint32_t hashdatalen; uint32_t pcrindex; uint32_t logeventtype; uint32_t logdataptr; uint32_t logdatalen; } __attribute__((packed)); struct hleo { uint16_t opblength; uint16_t reserved; uint32_t eventnumber; } __attribute__((packed)); struct hai { uint16_t ipblength; uint16_t reserved; uint32_t hashdataptr; uint32_t hashdatalen; uint32_t algorithmid; } __attribute__((packed)); struct ti { uint16_t ipblength; uint16_t reserved; uint16_t opblength; uint16_t reserved2; uint8_t tssoperandin[0]; } __attribute__((packed)); struct to { uint16_t opblength; uint16_t reserved; uint8_t tssoperandout[0]; } __attribute__((packed)); struct pcpes { uint32_t pcrindex; uint32_t eventtype; uint8_t digest[20]; uint32_t eventdatasize; uint32_t event; } __attribute__((packed)); struct acpi_20_tcpa_client { uint32_t laml; uint64_t lasa; } __attribute__((packed)); struct acpi_20_tcpa_server { uint16_t reserved; uint32_t laml; uint64_t lasa; /* more here */ } __attribute__((packed)); struct acpi_20_tcpa_clisrv { struct acpi_header header; uint16_t platform_class; union { struct acpi_20_tcpa_client client; struct acpi_20_tcpa_server server; } u; } __attribute__((packed)); #endif xen-4.9.2/tools/firmware/rombios/32bit/tcgbios/tpm_drivers.c0000664000175000017500000001166313256712137022171 0ustar smbsmb/* * Implementation of the TCG BIOS extension according to the specification * described in * https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementationforBIOS_1-20_1-00.pdf * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (C) IBM Corporation, 2006 * * Author: Stefan Berger */ #include "rombios_compat.h" #include "util.h" #include "tpm_drivers.h" #include "tcgbios.h" #define STS_VALID (1 << 7) /* 0x80 */ #define STS_COMMAND_READY (1 << 6) /* 0x40 */ #define STS_TPM_GO (1 << 5) /* 0x20 */ #define STS_DATA_AVAILABLE (1 << 4) /* 0x10 */ #define STS_EXPECT (1 << 3) /* 0x08 */ #define STS_RESPONSE_RETRY (1 << 1) /* 0x02 */ #define ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */ #define ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */ #define ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */ #define ACCESS_SEIZE (1 << 3) /* 0x08 */ #define ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */ #define ACCESS_REQUEST_USE (1 << 1) /* 0x02 */ #define ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */ static uint32_t tis_wait_sts(uint8_t *addr, uint32_t time, uint8_t mask, uint8_t expect) { uint32_t rc = 0; while (time > 0) { uint8_t sts = mmio_readb(&addr[TPM_STS]); if ((sts & mask) == expect) { rc = 1; break; } mssleep(1); time--; } return rc; } static uint32_t tis_activate(uint32_t baseaddr) { uint32_t rc = 1; uint8_t *tis_addr = (uint8_t*)baseaddr; uint8_t acc; /* request access to locality */ tis_addr[TPM_ACCESS] = ACCESS_REQUEST_USE; acc = mmio_readb(&tis_addr[TPM_ACCESS]); if ((acc & ACCESS_ACTIVE_LOCALITY) != 0) { tis_addr[TPM_STS] = STS_COMMAND_READY; rc = tis_wait_sts(tis_addr, 100, STS_COMMAND_READY, STS_COMMAND_READY); } return rc; } static uint32_t tis_ready(uint32_t baseaddr) { uint32_t rc = 0; uint8_t *tis_addr = (uint8_t*)baseaddr; tis_addr[TPM_STS] = STS_COMMAND_READY; rc = tis_wait_sts(tis_addr, 100, STS_COMMAND_READY, STS_COMMAND_READY); return rc; } static uint32_t tis_senddata(uint32_t baseaddr, unsigned char *data, uint32_t len) { uint32_t rc = 0; uint8_t *tis_addr = (uint8_t*)baseaddr; uint32_t offset = 0; uint32_t end = 0; do { uint16_t burst = 0; uint32_t ctr = 0; while (burst == 0 && ctr < 2000) { burst = mmio_readw((uint16_t *)&tis_addr[TPM_STS+1]); if (burst == 0) { mssleep(1); ctr++; } } if (burst == 0) { rc = TCG_RESPONSE_TIMEOUT; break; } while (1) { tis_addr[TPM_DATA_FIFO] = data[offset]; offset++; burst--; if (burst == 0 || offset == len) { break; } } if (offset == len) { end = 1; } } while (end == 0); return rc; } static uint32_t tis_readresp(uint32_t baseaddr, unsigned char *buffer, uint32_t len) { uint32_t rc = 0; uint32_t offset = 0; uint8_t *tis_addr = (uint8_t*)baseaddr; uint32_t sts; while (offset < len) { buffer[offset] = mmio_readb(&tis_addr[TPM_DATA_FIFO]); offset++; sts = mmio_readb(&tis_addr[TPM_STS]); /* data left ? */ if ((sts & STS_DATA_AVAILABLE) == 0) { break; } } return rc; } static uint32_t tis_waitdatavalid(uint32_t baseaddr) { uint8_t *tis_addr = (uint8_t*)baseaddr; uint32_t rc = 0; if (tis_wait_sts(tis_addr, 1000, STS_VALID, STS_VALID) == 0) { rc = TCG_NO_RESPONSE; } return rc; } static uint32_t tis_waitrespready(uint32_t baseaddr, uint32_t timeout) { uint32_t rc = 0; uint8_t *tis_addr = (uint8_t*)baseaddr; tis_addr[TPM_STS] = STS_TPM_GO; if (tis_wait_sts(tis_addr, timeout, STS_DATA_AVAILABLE, STS_DATA_AVAILABLE) == 0) { rc = TCG_NO_RESPONSE; } return rc; } /* if device is not there, return '0', '1' otherwise */ static uint32_t tis_probe(uint32_t baseaddr) { uint32_t rc = 0; uint8_t *tis_addr = (uint8_t*)baseaddr; uint32_t didvid = mmio_readl((uint32_t *)&tis_addr[TPM_DID_VID]); if ((didvid != 0) && (didvid != 0xffffffff)) { rc = 1; } return rc; } struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { { .baseaddr = TPM_TIS_BASE_ADDRESS, .activate = tis_activate, .ready = tis_ready, .senddata = tis_senddata, .readresp = tis_readresp, .waitdatavalid = tis_waitdatavalid, .waitrespready = tis_waitrespready, .probe = tis_probe, }, }; xen-4.9.2/tools/firmware/rombios/32bit/tcgbios/Makefile0000664000175000017500000000071413256712137021122 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../../../.. include $(XEN_ROOT)/tools/firmware/Rules.mk TARGET = tcgbiosext.o CFLAGS += $(CFLAGS_xeninclude) -I.. -I../.. -I../../../../libacpi $(call cc-option-add,CFLAGS,CC,-fno-pic) $(call cc-option-add,CFLAGS,CC,-fno-PIE) .PHONY: all all: $(TARGET) .PHONY: clean clean: rm -rf *.o $(TARGET) $(DEPS) .PHONY: distclean distclean: clean $(TARGET): tcgbios.o tpm_drivers.o $(LD) $(LDFLAGS_DIRECT) -r $^ -o $@ -include $(DEPS) xen-4.9.2/tools/firmware/rombios/32bit/tcgbios/tcgbios.c0000664000175000017500000010777213256712137021274 0ustar smbsmb/* * Implementation of the TCG BIOS extension according to the specification * described in * https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementationforBIOS_1-20_1-00.pdf * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (C) IBM Corporation, 2006 * * Author: Stefan Berger */ #include "rombios_compat.h" #include "tpm_drivers.h" #include "util.h" #include "tcgbios.h" /* local structure and variables */ struct ptti_cust { uint16_t ipblength; uint16_t reserved; uint16_t opblength; uint16_t reserved2; uint8_t tpmoperandin[18]; } __attribute__((packed)); struct ptti_cust CMD_TPM_Startup_0x01_IPB = { 0x8+0xc, 0x00, 4+10, 0x00, { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x99, 0x00, 0x01 }, }; struct ptti_cust CMD_TSC_PhysicalPresence_0x20_IPB = { 0x8+0xc, 0x00, 4+10, 0x00, { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x0a, 0x00, 0x20 }, }; struct ptti_cust CMD_TSC_PhysicalPresence_0x08_IPB = { 0x8+0xc, 0x00, 4+10, 0x00, { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x0a, 0x00, 0x08 }, }; struct ptti_cust CMD_TSC_PhysicalPresence_0x100_IPB = { 0x8+0xc, 0x00, 4+10, 0x00, { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x0a, 0x01, 0x00 }, }; struct ptti_cust CMD_TSC_PhysicalPresence_0x10_IPB = { 0x8+0xc, 0x00, 4+10, 0x00, { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x0a, 0x00, 0x10 }, }; struct ptti_cust CMD_TPM_PhysicalEnable_IPB = { 0x8+0xa, 0x00, 4+10, 0x00, { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x6f }, }; struct ptti_cust CMD_TPM_PhysicalSetDeactivated_0x00_IPB = { 0x8+0xb, 0x00, 4+10, 0x00, { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x72, 0x00 } }; struct ptti_cust CMD_TPM_SHA1Start_IPB = { 0x8+0xa, 0x00, 4+10, 0x00, { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xa0 }, }; struct ptti_cust CMD_TPM_GetCap_Version_IPB = { 0x8+0x12, 0x00, 4+18, 0x00, {0x00, 0xc1, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 }, }; struct ptti_cust *TCG_CommandList[] = { &CMD_TPM_Startup_0x01_IPB, &CMD_TSC_PhysicalPresence_0x20_IPB, &CMD_TSC_PhysicalPresence_0x08_IPB, &CMD_TSC_PhysicalPresence_0x100_IPB, &CMD_TSC_PhysicalPresence_0x10_IPB, &CMD_TPM_PhysicalEnable_IPB, &CMD_TPM_PhysicalSetDeactivated_0x00_IPB, &CMD_TPM_SHA1Start_IPB, }; /* local function prototypes */ static void sha1(const unsigned char *data, uint32_t length, unsigned char *hash); static uint32_t TCG_ShutdownPreBootInterface(uint32_t ebx); static uint32_t HashAll32(struct hai *hai, unsigned char *hash, uint32_t magic, uint32_t ecx, uint32_t edx); static uint32_t HashLogExtendEvent32(struct hleei_short *hleei_s, struct hleeo *hleeo, uint32_t magic, uint32_t ecx, uint32_t edx); static uint32_t HashLogEvent32(struct hlei *hlei, struct hleo *hleo, uint32_t ebx, uint32_t ecx, uint32_t edx); static uint32_t PassThroughToTPM32(struct pttti *pttti, struct pttto *pttto, uint32_t magic, uint32_t ecx, uint32_t edx); static uint32_t MA_Transmit(unsigned char *cmdbuffer, unsigned char *respbuffer, uint32_t respbufferlen); static unsigned char *tcpa_get_lasa_last_ptr(void); static unsigned char *tcpa_get_lasa_base_ptr(void); static void tcpa_reset_acpi_log(void); static uint32_t tcpa_get_laml(void); extern struct tpm_driver tpm_drivers[]; /* utility functions */ static inline uint32_t bswap(uint32_t a) { return ( ( a >> 24 ) & 0x000000ff) | ( ( a >> 8 ) & 0x0000ff00) | ( ( a << 8 ) & 0x00ff0000) | ( ( a << 24 ) & 0xff000000); } /******************************************************** Extensions for TCG-enabled BIOS *******************************************************/ typedef struct { struct acpi_20_tcpa_clisrv *tcpa_ptr; unsigned char *lasa_last_ptr; uint16_t entry_count; uint16_t flags; } tcpa_acpi_t; static tcpa_acpi_t tcpa_acpi; /* low level driver implementation */ static int tpm_driver_to_use = TPM_INVALID_DRIVER; static uint32_t MA_IsTPMPresent(void) { uint32_t rc = 0; unsigned int i; for (i = 0; i < TPM_NUM_DRIVERS; i++) { struct tpm_driver *td = &tpm_drivers[i]; if (td->probe(td->baseaddr) != 0) { tpm_driver_to_use = i; rc = 1; break; } } return rc; } static uint32_t MA_InitTPM(uint16_t startupcode) { uint32_t rc = 0; /* low-level initialize the TPM */ unsigned char command[sizeof(CMD_TPM_Startup_0x01_IPB.tpmoperandin)]; unsigned char response[10]; uint32_t response_size = sizeof(response); memcpy(command, CMD_TPM_Startup_0x01_IPB.tpmoperandin, sizeof(CMD_TPM_Startup_0x01_IPB.tpmoperandin)); command[10] = (startupcode >> 8) & 0xff; command[11] = (startupcode >> 0) & 0xff; rc = MA_Transmit(command, response, response_size); return rc; } static uint32_t MA_Transmit(unsigned char *cmdbuffer, unsigned char *respbuffer, uint32_t respbufferlen) { uint32_t rc = 0; uint32_t irc; struct tpm_driver *td; if (tpm_driver_to_use == TPM_INVALID_DRIVER) return TCG_FATAL_COM_ERROR; td = &tpm_drivers[tpm_driver_to_use]; if (rc == 0) { irc = td->activate(td->baseaddr); if (irc == 0) { /* tpm could not be activated */ rc = TCG_FATAL_COM_ERROR; } } if (rc == 0) { uint32_t *tmp = (uint32_t *)&cmdbuffer[2]; uint32_t len = bswap(*tmp); irc = td->senddata(td->baseaddr, cmdbuffer, len); if (irc != 0) { rc = TCG_FATAL_COM_ERROR; } } if (rc == 0) { irc = td->waitdatavalid(td->baseaddr); if (irc != 0) { rc = TCG_FATAL_COM_ERROR; } } if (rc == 0) { irc = td->waitrespready(td->baseaddr, 2000); if (irc != 0) { rc = TCG_FATAL_COM_ERROR; } } if (rc == 0) { irc = td->readresp(td->baseaddr, respbuffer, respbufferlen); if (irc != 0) { rc = TCG_FATAL_COM_ERROR; } } if (rc == 0) { irc = td->ready(td->baseaddr); } return rc; } static uint8_t acpi_validate_entry(struct acpi_header *hdr) { uint8_t sum = 0; unsigned int length = hdr->length; unsigned int ctr; unsigned char *addr = (unsigned char *)hdr; for (ctr = 0; ctr < length; ctr++) sum += addr[ctr]; return sum; } /* initialize the TCPA ACPI subsystem; find the ACPI tables and determine where the TCPA table is. */ void tcpa_acpi_init(void) { struct acpi_20_rsdt *rsdt; struct acpi_20_tcpa_clisrv *tcpa = (void *)0; struct acpi_20_rsdp *rsdp; uint32_t length; uint16_t off; int found = 0; if (MA_IsTPMPresent() == 0) return; rsdp = find_rsdp(); if (rsdp) { uint32_t ctr = 0; /* get RSDT from RSDP */ rsdt = (struct acpi_20_rsdt *)rsdp->rsdt_address; length = rsdt->header.length; off = 36; while ((off + 3) < length) { /* try all pointers to structures */ tcpa = (struct acpi_20_tcpa_clisrv *)rsdt->entry[ctr]; /* valid TCPA ACPI table ? */ if (ACPI_2_0_TCPA_SIGNATURE == tcpa->header.signature && acpi_validate_entry(&tcpa->header) == 0) { found = 1; break; } off += 4; ctr++; } } if (found == 0) { printf("TCPA ACPI was NOT found!\n"); tcpa = 0; } tcpa_acpi.tcpa_ptr = tcpa; tcpa_acpi.lasa_last_ptr = 0; tcpa_acpi.entry_count = 0; tcpa_acpi.flags = 0; tcpa_reset_acpi_log(); } /* clear the ACPI log */ static void tcpa_reset_acpi_log(void) { unsigned char *lasa = tcpa_get_lasa_base_ptr(); if (lasa) memset(lasa, 0x0, tcpa_get_laml()); } /* * Extend the ACPI log with the given entry by copying the * entry data into the log. * Input * Pointer to the structure to be copied into the log * * Output: * lower 16 bits of return code contain entry number * if entry number is '0', then upper 16 bits contain error code. */ uint32_t tcpa_extend_acpi_log(uint32_t entry_ptr) { uint32_t res = 0; unsigned char *lasa_last = tcpa_get_lasa_last_ptr(); unsigned char *lasa_base = tcpa_get_lasa_base_ptr(); uint32_t size = 0; uint16_t entry_count = tcpa_acpi.entry_count; struct pcpes *pcpes = (struct pcpes *)entry_ptr; if (lasa_last == 0) { lasa_last = lasa_base; } else { struct pcpes *pcpes = (struct pcpes *)lasa_last; /* skip the last entry in the log */ size = pcpes->eventdatasize; size += 32; lasa_last += size; } if (lasa_last == 0) { res = ((uint32_t)TCG_PC_LOGOVERFLOW << 16); } if (res == 0) { uint32_t laml = tcpa_get_laml(); size = pcpes->eventdatasize; size += 32; if ((lasa_last + size - lasa_base) > laml) { res = (TCG_PC_LOGOVERFLOW << 16); } } if (res == 0) { /* copy the log entry into the ACPI log */ memcpy((char *)lasa_last, (char *)entry_ptr, size); /* * update the pointers and entry counter that were modified * due to the new entry in the log */ tcpa_acpi.lasa_last_ptr = lasa_last; entry_count++; tcpa_acpi.entry_count = entry_count; res = entry_count; } return res; } static unsigned char *tcpa_get_lasa_last_ptr(void) { return tcpa_acpi.lasa_last_ptr; } static unsigned char *tcpa_get_lasa_base_ptr(void) { unsigned char *lasa = 0; struct acpi_20_tcpa_clisrv *tcpa = tcpa_acpi.tcpa_ptr; if (tcpa != 0) { uint32_t class = tcpa->platform_class; if (class == TCPA_ACPI_CLASS_CLIENT) { /* client type */ lasa = (unsigned char *)(long)tcpa->u.client.lasa; } else if (class == TCPA_ACPI_CLASS_SERVER) { /* server type */ lasa = (unsigned char *)(long)tcpa->u.server.lasa; } } return lasa; } static uint32_t tcpa_get_laml(void) { uint32_t laml = 0; struct acpi_20_tcpa_clisrv *tcpa = tcpa_acpi.tcpa_ptr; if (tcpa != 0) { uint32_t class = tcpa->platform_class; if (class == TCPA_ACPI_CLASS_CLIENT) { /* client type */ laml = tcpa->u.client.laml; } else if (class == TCPA_ACPI_CLASS_SERVER) { laml = tcpa->u.server.laml; } } return laml; } /* * Add a measurement to the log; the data at data_seg:data/length are * appended to the TCG_PCClientPCREventStruct * * Input parameters: * pcrIndex : which PCR to extend * event_type : type of event; specs 10.4.1 * event_id : (unused) * data : pointer to the data (i.e., string) to be added to the log * length : length of the data */ static uint32_t tcpa_add_measurement_to_log(uint32_t pcrIndex, uint32_t event_type, uint32_t event_id, const char *data_ptr, uint32_t length) { uint32_t rc = 0; struct hleei_short hleei; struct hleeo hleeo; uint8_t _pcpes[32+400]; struct pcpes *pcpes = (struct pcpes *)_pcpes; uint8_t *data = (uint8_t *)data_ptr; if (length < sizeof(_pcpes)-32) { memset(pcpes, 0x0, 32); pcpes->pcrindex = pcrIndex; pcpes->eventtype = event_type; pcpes->eventdatasize = length; memcpy(&_pcpes[32], data, length); hleei.ipblength = 0x18; hleei.reserved = 0x0; hleei.hashdataptr = (uint32_t)&_pcpes[32]; hleei.hashdatalen = length; hleei.pcrindex = pcrIndex; hleei.logdataptr = (uint32_t)_pcpes; hleei.logdatalen = length + 32; rc = HashLogExtendEvent32(&hleei, &hleeo, TCG_MAGIC, 0x0, 0x0); } else { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_GENERAL_ERROR << 16)); } return rc; } static uint16_t tcpa_add_pcpes_to_log(struct pcpes *pcpes) { uint32_t rc = 0; struct hleei_short hleei; struct hleeo hleeo; hleei.ipblength = 0x18; hleei.reserved = 0x0; hleei.hashdataptr = 0; hleei.hashdatalen = 0; hleei.pcrindex = pcpes->pcrindex; hleei.logdataptr = (uint32_t)pcpes; hleei.logdatalen = sizeof(pcpes); rc = HashLogExtendEvent32(&hleei, &hleeo, TCG_MAGIC, 0x0, 0x0); return rc; } /* * Add a measurement to the log; further description of the data * that are to be hashed are NOT appended to the TCG_PCClientPCREventStruc. * Input parameters: * pcrIndex : PCR to extend * event_type : type of event; specs 10.4.1 * ptr : 32 bit pointer to the data to be hashed * length : length of the data to be hashed * * Returns lower 16 bit of return code of TCG_HashLogExtendEvent. '0' means * success, otherwise an error is indicated. */ static uint16_t tcpa_add_measurement_to_log_simple(uint32_t pcrIndex, uint16_t event_type, uint8_t *ptr, uint32_t length) { uint32_t rc = 0; struct hleei_short hleei; struct hleeo hleeo; struct pcpes pcpes; memset(&pcpes, 0x0, sizeof(pcpes)); pcpes.pcrindex = pcrIndex; pcpes.eventtype = event_type; /* specs: 10.4.1, EV_IPL eventfield should not contain the code.*/ pcpes.eventdatasize = 0; hleei.ipblength = 0x18; hleei.reserved = 0x0; hleei.hashdataptr = (uint32_t)ptr; hleei.hashdatalen = length; hleei.pcrindex = pcrIndex; hleei.logdataptr = (uint32_t)&pcpes; hleei.logdatalen = 32; rc = HashLogExtendEvent32(&hleei, &hleeo, TCG_MAGIC, 0x0, 0x0); return rc; } /* table of event types according to 10.4.1 / table 11 */ static const char ev_action[][23] = { /* 0 */ "Calling INT 19h", "Returned INT 19h", "Returned via INT 18h", "", "", /* 5 */ "", "", "", "", "", /* 10 */ "", "", "", "", "Start Option ROM Scan" }; static char evt_separator[] = {0xff,0xff,0xff,0xff}; static char wake_event_1[] = "Wake Event 1"; /* * Add a measurement to the list of measurements * pcrIndex : PCR to be extended * event_type : type of event; specs 10.4.1 * data : additional parameter; used as parameter for 10.4.3 * 'action index' */ static void tcpa_add_measurement(uint32_t pcrIndex, uint16_t event_type, uint32_t data) { const char *string; switch (event_type) { case EV_SEPARATOR: tcpa_add_measurement_to_log_simple(pcrIndex, event_type, (uint8_t *)evt_separator, 4); break; case EV_ACTION: string = ev_action[data /* event_id */]; tcpa_add_measurement_to_log(pcrIndex, event_type, data, string, strlen(string)); break; } } /* * Add measurement to log about call of int 19h */ void tcpa_calling_int19h() { tcpa_add_measurement(4, EV_ACTION, 0); } /* * Add measurement to log about retuning from int 19h */ void tcpa_returned_int19h() { tcpa_add_measurement(4, EV_ACTION, 1); } /* * Add event separators for PCRs 0 to 7; specs 8.2.3 */ void tcpa_add_event_separators() { uint32_t pcrIndex = 0; while (pcrIndex <= 7) { tcpa_add_measurement(pcrIndex, EV_SEPARATOR, 0); pcrIndex ++; } } /* * Add a wake event to the log */ void tcpa_wake_event() { tcpa_add_measurement_to_log(6, EV_ACTION, 10, wake_event_1, strlen(wake_event_1)); } /* * Add a measurement regarding the boot device (CDRom, Floppy, HDD) to * the list of measurements. */ void tcpa_add_bootdevice(uint32_t bootcd, uint32_t bootdrv) { char *string; if (bootcd == 0) { if (bootdrv == 0) { string = "Booting BCV device 00h (Floppy)"; } else if (bootdrv == 0x80) { string = "Booting BCV device 80h (HDD)"; } else { string = "Booting unknown device"; } } else { string = "Booting from CD ROM device"; } tcpa_add_measurement_to_log(4, 5, 0, string, strlen(string)); } /* * Add measurement to the log about option rom scan * 10.4.3 : action 14 */ void tcpa_start_option_rom_scan() { tcpa_add_measurement(2, EV_ACTION, 14); } /* * Add measurement to the log about an option rom */ void tcpa_option_rom(uint32_t seg) { uint32_t len = read_byte(seg, 2) << 9; uint8_t *addr = (uint8_t *)ADDR_FROM_SEG_OFF(seg,0); char append[32]; /* TCG_PCClientTaggedEventStruct and OptionROMExecuteStructure; specs 10.4.2.1 */ struct hai hai; /* HashAll Input Block; specs 12.10 */ memset(append, 0x0, sizeof(append)); append[0] = 7; /* Option ROM Execute */ append[4] = 24;/* size of OptionROMExecute Structure */ /* leave the rest to '0' */ /* 12.10 table 21 */ hai.ipblength = 0x10; hai.reserved = 0; hai.hashdataptr = (uint32_t)addr; hai.hashdatalen = len; hai.algorithmid = TPM_ALG_SHA; HashAll32(&hai, (unsigned char *)append+12, TCG_MAGIC, 0, 0); tcpa_add_measurement_to_log(2, EV_EVENT_TAG, 0, append, 32); } /* * Add a measurement to the log in support of 8.2.5.3 * Creates two log entries * * Input parameter: * bootcd : 0: MBR of hdd, 1: boot image, 2: boot catalog of El Torito * seg : segment where the IPL data are located * off : offset where the IPL data are located * count : length in bytes */ void tcpa_ipl(Bit32u bootcd,Bit32u seg,Bit32u off,Bit32u count) { uint8_t *addr = (uint8_t *)ADDR_FROM_SEG_OFF(seg,off); if (bootcd == 1) { /* specs: 8.2.5.6 El Torito */ tcpa_add_measurement_to_log_simple(4, EV_IPL, addr, count); } else if (bootcd == 2) { /* Boot Catalog */ /* specs: 8.2.5.6 El Torito */ tcpa_add_measurement_to_log_simple(5, EV_IPL_PARTITION_DATA, addr, count); } else { /* specs: 8.2.5.3 */ /* equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum */ tcpa_add_measurement_to_log_simple(4, EV_IPL, addr, 0x1b8); /* equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum */ tcpa_add_measurement_to_log_simple(5, EV_IPL_PARTITION_DATA, addr + 0x1b8, 0x48); } } void tcpa_measure_post(Bit32u from, Bit32u to) { struct pcpes pcpes; /* PCClientPCREventStruc */ int len = to - from; memset(&pcpes, 0x0, sizeof(pcpes)); if (len > 0) { sha1((unsigned char *)from, to-from, (unsigned char *)&pcpes.digest); pcpes.eventtype = EV_POST_CODE; pcpes.eventdatasize = 0; pcpes.pcrindex = 0; tcpa_add_pcpes_to_log(&pcpes); } } static uint32_t SendCommand32(uint32_t idx, struct pttto *pttto, uint32_t size_ptto) { uint32_t rc = 0; struct pttti *pttti = (struct pttti *)TCG_CommandList[idx]; uint8_t _pttto[30]; if (size_ptto > 0 && size_ptto < 14) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INVALID_INPUT_PARA << 16)); } if (rc == 0) { if (size_ptto == 0) { pttto = (struct pttto *)_pttto; size_ptto = sizeof(_pttto); } pttti->opblength = size_ptto; } if (rc == 0) { if (pttti->opblength > size_ptto) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_OUTPUT_BUFFER_TOO_SHORT << 16)); } } if (rc == 0) { rc = PassThroughToTPM32(pttti, pttto, TCG_MAGIC, 0x0, 0x0); } return rc; } uint32_t tcpa_initialize_tpm(uint32_t physpres) { uint32_t rc = 0; uint8_t _pttto[40]; struct pttto *pttto = (struct pttto *)_pttto; uint32_t pttto_size = sizeof(_pttto); if (rc == 0) { rc = SendCommand32(IDX_CMD_TPM_Startup_0x01, pttto, pttto_size); } if (rc == 0 && physpres != 0) { rc = SendCommand32(IDX_CMD_TSC_PhysicalPresence_0x20, pttto, pttto_size); } if (rc == 0 && physpres != 0) { rc = SendCommand32(IDX_CMD_TSC_PhysicalPresence_0x08, pttto, pttto_size); } if (rc == 0 && physpres != 0) { rc = SendCommand32(IDX_CMD_TPM_PhysicalEnable, pttto, pttto_size); } if (rc == 0 && physpres != 0) { rc = SendCommand32(IDX_CMD_TPM_PhysicalSetDeactivated_0x00, pttto, pttto_size); } if (rc == 0) { rc = SendCommand32(IDX_CMD_TSC_PhysicalPresence_0x100, pttto, pttto_size); } if (rc == 0) { rc = SendCommand32(IDX_CMD_TSC_PhysicalPresence_0x10, pttto, pttto_size); } return rc; } static uint16_t TCG_IsShutdownPreBootInterface(void) { return tcpa_acpi.flags & STATUS_FLAG_SHUTDOWN; } static uint32_t _TCG_TPM_Extend(unsigned char *hash, uint32_t pcrindex) { uint32_t rc; uint8_t _pttti[8+34]; uint8_t _pttto[4+30]; struct pttti *pttti = (struct pttti*)&_pttti; struct pttto *pttto = (struct pttto*)&_pttto; pttti->ipblength = 8 + 34; pttti->reserved = 0; pttti->opblength = 4 + 30; pttti->reserved2 = 0; _pttti[8 + 0] = 0x0; _pttti[8 + 1] = 0xc1; *(uint32_t *)&_pttti[8 + 2] = bswap(34); *(uint32_t *)&_pttti[8 + 6] = bswap(0x14); *(uint32_t *)&_pttti[8 + 10]= bswap(pcrindex); memcpy(&_pttti[8+14], hash, 20); rc = PassThroughToTPM32(pttti, pttto, TCG_MAGIC, 0x0, 0x0); /* sanity check of result */ if (_pttto[4] != 0x00 || _pttto[5] != 0xc4) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_FATAL_COM_ERROR << 16)); } if (rc != 0) { /* Invalidate the log since system did not process this extend properly. */ tcpa_reset_acpi_log(); memset(&tcpa_acpi, 0x0, sizeof(tcpa_acpi)); TCG_ShutdownPreBootInterface(0); } return rc; } static uint32_t HashLogExtendEvent32(struct hleei_short *hleei_s, struct hleeo *hleeo, uint32_t magic, uint32_t ecx, uint32_t edx) { uint32_t rc = 0; uint16_t size; struct hlei hlei ; /* HashLogEventInput block */ struct hleo hleo; /* HashLogEventOutput block */ struct hleei_long *hleei_l = (struct hleei_long *)hleei_s; int sh = 0; uint32_t logdataptr; if (TCG_IsShutdownPreBootInterface() != 0) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INTERFACE_SHUTDOWN << 16)); } if (rc == 0) { /* short or long version? */ size = hleei_s->ipblength; if (size == 0x18) { /* short */ sh = 1; } else if (size == 0x1c) { /* long */ sh = 0; } else { /* bad input block */ rc = TCG_PC_TPMERROR | ((uint32_t)(TCG_INVALID_ACCESS_REQUEST << 16)); } } if (rc == 0) { uint32_t hashdataptr; uint32_t hashdatalen; uint32_t pcrindex; uint32_t logeventtype; uint32_t logdatalen; uint32_t eventnumber; uint8_t hash[20]; struct pcpes *pcpes; hashdataptr = hleei_s->hashdataptr; hashdatalen = hleei_s->hashdatalen; pcrindex = hleei_s->pcrindex; if (sh) { logdataptr = hleei_s->logdataptr; logdatalen = hleei_s->logdatalen; } else { logdataptr = hleei_l->logdataptr; logdatalen = hleei_l->logdatalen; } pcpes = (struct pcpes *)logdataptr; logeventtype = pcpes->eventtype; /* fill out HashLogEventInput block 'hlie' */ hlei.ipblength = 0x1c; hlei.reserved = 0; hlei.hashdataptr = hashdataptr; hlei.hashdatalen = hashdatalen; hlei.pcrindex = pcrindex; hlei.logeventtype= logeventtype; hlei.logdataptr = logdataptr; hlei.logdatalen = logdatalen; rc = HashLogEvent32(&hlei, &hleo, TCG_MAGIC, 0x0, 0x0); eventnumber = hleo.eventnumber; hleeo->opblength = 8 + 20; hleeo->reserved = 0; hleeo->eventnumber = eventnumber; memcpy(hash, (unsigned char *)logdataptr + 0x8, 20); _TCG_TPM_Extend(hash, pcrindex); } if (rc != 0) { hleeo->opblength = 4; hleeo->reserved = 0; } return rc; } static uint32_t PassThroughToTPM32(struct pttti *pttti, struct pttto *pttto, uint32_t magic, uint32_t ecx, uint32_t edx) { uint32_t rc = 0; uint8_t *cmd32; uint32_t resbuflen = 0; if (TCG_IsShutdownPreBootInterface() != 0) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INTERFACE_SHUTDOWN << 16)); } if (rc == 0) { if (pttti->ipblength < 0x8 + 10) { rc = TCG_PC_TPMERROR | ((uint32_t)(TCG_INVALID_ACCESS_REQUEST << 16)); } } if (rc == 0) { if (pttti->opblength < 0x4) { rc = TCG_PC_TPMERROR | ((uint32_t)(TCG_INVALID_ACCESS_REQUEST << 16)); } } if (rc == 0) { uint8_t *resbuf32; cmd32 = &pttti->tpmoperandin[0]; resbuflen = pttti->opblength - 4; resbuf32 = &pttto->tpmoperandout[0]; rc = MA_Transmit(cmd32, resbuf32, resbuflen); } if (rc == 0) { pttto->opblength = resbuflen+4; pttto->reserved = 0; } if (rc != 0) { pttto->opblength = 0; pttto->reserved = 0; } return rc; } static uint32_t TCG_ShutdownPreBootInterface(uint32_t ebx) { uint32_t rc = 0; if (TCG_IsShutdownPreBootInterface() == 0) { tcpa_acpi.flags |= STATUS_FLAG_SHUTDOWN; } else { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INTERFACE_SHUTDOWN << 16)); } return rc; } static uint32_t HashLogEvent32(struct hlei *hlei, struct hleo *hleo, uint32_t ebx, uint32_t ecx, uint32_t edx) { uint32_t rc = 0; uint16_t size; uint32_t logdataptr = 0; uint32_t logdatalen; uint32_t hashdataptr; uint32_t hashdatalen; if (TCG_IsShutdownPreBootInterface() != 0) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INTERFACE_SHUTDOWN << 16)); } if (rc == 0) { size = hlei->ipblength; if (size != 0x1c) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INVALID_ACCESS_REQUEST << 16)); } } if (rc == 0) { struct pcpes *pcpes; logdataptr = hlei->logdataptr; logdatalen = hlei->logdatalen; pcpes = (struct pcpes *)logdataptr; if (pcpes->pcrindex != hlei->pcrindex) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INVALID_ACCESS_REQUEST << 16)); } } if (rc == 0) { struct pcpes *pcpes= (struct pcpes *)logdataptr; if (pcpes->eventtype != hlei->logeventtype) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INVALID_ACCESS_REQUEST << 16)); } } if (rc == 0) { uint32_t entry = 0; hashdataptr = hlei->hashdataptr; hashdatalen = hlei->hashdatalen; if ((hashdataptr != 0) | (hashdatalen != 0)) { uint8_t hash[20]; struct hai hai; /* HashAll Input Block */ hai.ipblength = 0x10; hai.reserved = 0x0; hai.hashdataptr = hashdataptr; hai.hashdatalen = hashdatalen; hai.algorithmid = TPM_ALG_SHA; rc = HashAll32(&hai, hash, TCG_MAGIC, 0x0, 0x0); if (rc == 0) { /* hashing was done ok */ memcpy((unsigned char *)logdataptr + 8, hash, 20); } } if (rc == 0) { /* extend the log with this event */ entry = tcpa_extend_acpi_log(logdataptr); if ((uint16_t)entry == 0) { /* upper 16 bits hold error code */ rc = (entry >> 16); } } if (rc == 0) { /* updating the log was fine */ hleo->opblength = 8; hleo->reserved = 0; hleo->eventnumber = entry; } } if (rc != 0) { hleo->opblength = 2; hleo->reserved = 0; } return rc; } static uint32_t HashAll32(struct hai *hai, unsigned char *hash, uint32_t magic, uint32_t ecx, uint32_t edx) { uint32_t rc = 0; if (TCG_IsShutdownPreBootInterface() != 0) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INTERFACE_SHUTDOWN << 16)); } if (rc == 0) { if (hai->ipblength != 0x10) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INVALID_ACCESS_REQUEST << 16)); } } if (rc == 0) { if (hai->algorithmid != TPM_ALG_SHA) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INVALID_ACCESS_REQUEST << 16)); } } if (rc == 0) { uint8_t *hashdataptr32; uint32_t hashdatalen32; hashdataptr32 = (uint8_t *)hai->hashdataptr; hashdatalen32 = hai->hashdatalen; sha1(hashdataptr32, hashdatalen32, hash); } return rc; } static uint32_t TSS32(struct ti *ti, struct to *to, uint32_t ebx, uint32_t ecx, uint32_t edx) { uint32_t rc = 0; if (TCG_IsShutdownPreBootInterface() == 0) { rc = TCG_PC_UNSUPPORTED; } else { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INTERFACE_SHUTDOWN << 16)); } if (rc != 0) { to->opblength = 4; to->reserved = 0; } return rc; } static uint32_t CompactHashLogExtendEvent32(unsigned char *buffer, uint32_t info, uint32_t magic, uint32_t length, uint32_t pcrindex, uint32_t *edx_ptr) { uint32_t rc = 0; struct hleeo hleeo; if (TCG_IsShutdownPreBootInterface() != 0) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INTERFACE_SHUTDOWN << 16)); } if (buffer == 0) { rc = (TCG_PC_TPMERROR | ((uint32_t)TCG_INVALID_INPUT_PARA << 16)); } if (rc == 0) { struct hleei_short hleei; struct pcpes pcpes; uint8_t *logdataptr; uint8_t *hashdataptr; logdataptr = (uint8_t*)&pcpes; hashdataptr = buffer; hleei.ipblength = 0x18; hleei.reserved = 0x0; hleei.hashdataptr = (uint32_t)hashdataptr; hleei.hashdatalen = length; hleei.pcrindex = pcrindex; hleei.logdataptr = (uint32_t)logdataptr; hleei.logdatalen = 32; memset(&pcpes, 0x0, 32); pcpes.pcrindex = pcrindex; pcpes.eventtype = 12; /* EV_COMPACT_HASH */ pcpes.eventdatasize = 4; pcpes.event = info; rc = HashLogExtendEvent32(&hleei, &hleeo, TCG_MAGIC, 0x0, 0x0); } if (rc == 0) { *edx_ptr = hleeo.eventnumber; } return rc; } /******************************************************************* Calculation of SHA1 in SW See: RFC3174, Wikipedia's SHA1 alogrithm description ******************************************************************/ typedef struct _sha1_ctx { uint32_t h[5]; } sha1_ctx; static inline uint32_t rol(uint32_t val, uint16_t rol) { return (val << rol) | (val >> (32 - rol)); } static const uint32_t sha_ko[4] = { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 }; static void sha1_block(uint32_t *w, sha1_ctx *ctx) { uint32_t i; uint32_t a,b,c,d,e,f; uint32_t tmp; uint32_t idx; /* change endianess of given data */ for (i = 0; i < 16; i++) { w[i] = bswap(w[i]); } for (i = 16; i <= 79; i++) { tmp = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; w[i] = rol(tmp,1); } a = ctx->h[0]; b = ctx->h[1]; c = ctx->h[2]; d = ctx->h[3]; e = ctx->h[4]; for (i = 0; i <= 79; i++) { if (i <= 19) { f = (b & c) | ((b ^ 0xffffffff) & d); idx = 0; } else if (i <= 39) { f = b ^ c ^ d; idx = 1; } else if (i <= 59) { f = (b & c) | (b & d) | (c & d); idx = 2; } else { f = b ^ c ^ d; idx = 3; } tmp = rol(a, 5) + f + e + sha_ko[idx] + w[i]; e = d; d = c; c = rol(b, 30); b = a; a = tmp; } ctx->h[0] += a; ctx->h[1] += b; ctx->h[2] += c; ctx->h[3] += d; ctx->h[4] += e; } static void sha1_do(sha1_ctx *ctx, const unsigned char *data32, uint32_t length) { uint32_t offset; uint16_t num; uint32_t bits = 0; uint32_t w[80]; uint32_t tmp; /* treat data in 64-byte chunks */ for (offset = 0; length - offset >= 64; offset += 64) { memcpy(w, data32 + offset, 64); sha1_block((uint32_t *)w, ctx); bits += (64 * 8); } /* last block with less than 64 bytes */ num = length - offset; bits += (num << 3); memset(w, 0x0, 64); memcpy(w, data32 + offset, num); ((uint8_t *)w)[num] = 0x80; if (num >= 56) { /* cannot append number of bits here */ sha1_block((uint32_t *)w, ctx); memset(w, 0x0, 60); } /* write number of bits to end of block */ tmp = bswap(bits); memcpy(&w[15], &tmp, 4); sha1_block(w, ctx); /* need to switch result's endianess */ for (num = 0; num < 5; num++) ctx->h[num] = bswap(ctx->h[num]); } /* sha1 initialization constants */ static const uint32_t sha_const[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 }; static void sha1(const unsigned char *data, uint32_t length, unsigned char *hash) { sha1_ctx ctx; memcpy(&ctx.h[0], sha_const, 20); sha1_do(&ctx, data, length); memcpy(hash, &ctx.h[0], 20); } uint32_t TCGInterruptHandler(pushad_regs_t *regs, uint32_t esds, uint32_t flags_ptr) { uint16_t DS = esds >> 16; uint16_t ES = esds & 0xffff; uint16_t *FLAGS = (uint16_t *)flags_ptr; switch(regs->u.r8.al) { case 0x00: if (MA_IsTPMPresent() == 0) { /* no TPM available */ regs->u.r32.eax = TCG_PC_TPMERROR | ((uint32_t)(TCG_PC_TPM_NOT_PRESENT) << 16); } else { regs->u.r32.eax = MA_InitTPM(TPM_ST_CLEAR); if (regs->u.r32.eax == 0) { regs->u.r32.ebx = TCG_MAGIC; regs->u.r8.ch = TCG_VERSION_MAJOR; regs->u.r8.cl = TCG_VERSION_MINOR; regs->u.r32.edx = 0x0; regs->u.r32.esi = (Bit32u)tcpa_get_lasa_base_ptr(); regs->u.r32.edi = (Bit32u)tcpa_get_lasa_last_ptr(); CLEAR_CF(); } } break; case 0x01: regs->u.r32.eax = HashLogExtendEvent32((struct hleei_short*) ADDR_FROM_SEG_OFF(ES, regs->u.r16.di), (struct hleeo*) ADDR_FROM_SEG_OFF(DS, regs->u.r16.si), regs->u.r32.ebx, regs->u.r32.ecx, regs->u.r32.edx); CLEAR_CF(); break; case 0x02: regs->u.r32.eax = PassThroughToTPM32((struct pttti *) ADDR_FROM_SEG_OFF(ES, regs->u.r16.di), (struct pttto *) ADDR_FROM_SEG_OFF(DS, regs->u.r16.si), regs->u.r32.ebx, regs->u.r32.ecx, regs->u.r32.edx); CLEAR_CF(); break; case 0x03: regs->u.r32.eax = TCG_ShutdownPreBootInterface(regs->u.r32.ebx); CLEAR_CF(); break; case 0x04: regs->u.r32.eax = HashLogEvent32((struct hlei*) ADDR_FROM_SEG_OFF(ES, regs->u.r16.di), (struct hleo*) ADDR_FROM_SEG_OFF(DS, regs->u.r16.si), regs->u.r32.ebx, regs->u.r32.ecx, regs->u.r32.edx); CLEAR_CF(); break; case 0x05: regs->u.r32.eax = HashAll32((struct hai*) ADDR_FROM_SEG_OFF(ES, regs->u.r16.di), (unsigned char *) ADDR_FROM_SEG_OFF(DS, regs->u.r16.si), regs->u.r32.ebx, regs->u.r32.ecx, regs->u.r32.edx); CLEAR_CF(); break; case 0x06: regs->u.r32.eax = TSS32((struct ti*)ADDR_FROM_SEG_OFF(ES, regs->u.r16.di), (struct to*)ADDR_FROM_SEG_OFF(DS, regs->u.r16.si), regs->u.r32.ebx, regs->u.r32.ecx, regs->u.r32.edx); CLEAR_CF(); break; case 0x07: regs->u.r32.eax = CompactHashLogExtendEvent32((unsigned char *) ADDR_FROM_SEG_OFF(ES, regs->u.r16.di), regs->u.r32.esi, regs->u.r32.ebx, regs->u.r32.ecx, regs->u.r32.edx, ®s->u.r32.edx); CLEAR_CF(); break; default: SET_CF(); } return 0; } xen-4.9.2/tools/firmware/rombios/32bit/tcgbios/tpm_drivers.h0000664000175000017500000000106213256712137022166 0ustar smbsmb#ifndef TPM_DRIVER_H /* low level driver implementation */ struct tpm_driver { uint32_t baseaddr; uint32_t (*activate)(uint32_t baseaddr); uint32_t (*ready)(uint32_t baseaddr); uint32_t (*senddata)(uint32_t baseaddr, unsigned char *data, uint32_t len); uint32_t (*readresp)(uint32_t baseaddr, unsigned char *buffer, uint32_t len); uint32_t (*waitdatavalid)(uint32_t baseaddr); uint32_t (*waitrespready)(uint32_t baseaddr, uint32_t timeout); uint32_t (*probe)(uint32_t baseaddr); }; #define TPM_NUM_DRIVERS 1 #define TPM_INVALID_DRIVER -1 #endif xen-4.9.2/tools/firmware/rombios/32bit/Makefile0000664000175000017500000000163313256712137017471 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../../.. include $(XEN_ROOT)/tools/firmware/Rules.mk TARGET = 32bitbios_flat.h CFLAGS += $(CFLAGS_xeninclude) -I.. -I../../../libacpi $(call cc-option-add,CFLAGS,CC,-fno-pic) $(call cc-option-add,CFLAGS,CC,-fno-PIE) SUBDIRS = tcgbios .PHONY: all all: subdirs-all $(MAKE) $(TARGET) .PHONY: clean clean: subdirs-clean rm -rf *.o $(TARGET) $(DEPS) .PHONY: distclean distclean: subdirs-distclean $(TARGET): 32bitbios_all.o sh mkhex highbios_array 32bitbios_all.o > $@ 32bitbios_all.o: 32bitbios.o tcgbios/tcgbiosext.o util.o pmm.o $(LD) $(LDFLAGS_DIRECT) -s -r $^ -o 32bitbios_all.o @nm 32bitbios_all.o | \ egrep '^ +U ' >/dev/null && { \ echo "There are undefined symbols in the BIOS:"; \ nm -u 32bitbios_all.o; \ exit 11; \ } || : -include $(DEPS) xen-4.9.2/tools/firmware/rombios/32bit/util.c0000664000175000017500000002344513256712137017157 0ustar smbsmb/* * util.c: Helper library functions for HVMLoader. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005, International Business Machines Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include #include #include "rombios_compat.h" #include "util.h" static void putchar(char c); #define isdigit(c) ((c) >= '0' && (c) <= '9') void outb(uint16_t addr, uint8_t val) { __asm__ __volatile__ ( "outb %%al, %%dx" :: "d"(addr), "a"(val) ); } void outw(uint16_t addr, uint16_t val) { __asm__ __volatile__ ( "outw %%ax, %%dx" :: "d"(addr), "a"(val) ); } void outl(uint16_t addr, uint32_t val) { __asm__ __volatile__ ( "outl %%eax, %%dx" :: "d"(addr), "a"(val) ); } uint8_t inb(uint16_t addr) { uint8_t val; __asm__ __volatile__ ( "inb %%dx,%%al" : "=a" (val) : "d" (addr) ); return val; } uint16_t inw(uint16_t addr) { uint16_t val; __asm__ __volatile__ ( "inw %%dx,%%ax" : "=a" (val) : "d" (addr) ); return val; } uint32_t inl(uint16_t addr) { uint32_t val; __asm__ __volatile__ ( "inl %%dx,%%eax" : "=a" (val) : "d" (addr) ); return val; } char *itoa(char *a, unsigned int i) { unsigned int _i = i, x = 0; do { x++; _i /= 10; } while ( _i != 0 ); a += x; *a-- = '\0'; do { *a-- = (i % 10) + '0'; i /= 10; } while ( i != 0 ); return a + 1; } int strcmp(const char *cs, const char *ct) { signed char res; while ( ((res = *cs - *ct++) == 0) && (*cs++ != '\0') ) continue; return res; } int strncmp(const char *s1, const char *s2, uint32_t n) { uint32_t ctr; for (ctr = 0; ctr < n; ctr++) if (s1[ctr] != s2[ctr]) return (int)(s1[ctr] - s2[ctr]); return 0; } void *memcpy(void *dest, const void *src, unsigned n) { int t0, t1, t2; __asm__ __volatile__ ( "cld\n" "rep; movsl\n" "testb $2,%b4\n" "je 1f\n" "movsw\n" "1: testb $1,%b4\n" "je 2f\n" "movsb\n" "2:" : "=&c" (t0), "=&D" (t1), "=&S" (t2) : "0" (n/4), "q" (n), "1" ((long) dest), "2" ((long) src) : "memory" ); return dest; } void *memmove(void *dest, const void *src, unsigned n) { if ( (long)dest > (long)src ) { n--; while ( n > 0 ) { ((char *)dest)[n] = ((char *)src)[n]; n--; } } else { memcpy(dest, src, n); } return dest; } char * strcpy(char *dest, const char *src) { char *p = dest; while ( *src ) *p++ = *src++; *p = 0; return dest; } char * strncpy(char *dest, const char *src, unsigned n) { int i = 0; char *p = dest; /* write non-NUL characters from src into dest until we run out of room in dest or encounter a NUL in src */ while ( (i < n) && *src ) { *p++ = *src++; i++; } /* pad remaining bytes of dest with NUL bytes */ while ( i < n ) { *p++ = 0; i++; } return dest; } unsigned strlen(const char *s) { int i = 0; while ( *s++ ) i++; return i; } void * memset(void *s, int c, unsigned n) { uint8_t b = (uint8_t) c; uint8_t *p = (uint8_t *)s; int i; for ( i = 0; i < n; i++ ) *p++ = b; return s; } int memcmp(const void *s1, const void *s2, unsigned n) { unsigned i; uint8_t *p1 = (uint8_t *) s1; uint8_t *p2 = (uint8_t *) s2; for ( i = 0; i < n; i++ ) { if ( p1[i] < p2[i] ) return -1; else if ( p1[i] > p2[i] ) return 1; } return 0; } void cpuid(uint32_t idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { __asm__ __volatile__ ( "cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "0" (idx) ); } /* Write a two-character hex representation of 'byte' to digits[]. Pre-condition: sizeof(digits) >= 2 */ void byte_to_hex(char *digits, uint8_t byte) { uint8_t nybbel = byte >> 4; if ( nybbel > 9 ) digits[0] = 'a' + nybbel-10; else digits[0] = '0' + nybbel; nybbel = byte & 0x0f; if ( nybbel > 9 ) digits[1] = 'a' + nybbel-10; else digits[1] = '0' + nybbel; } /* Convert an array of 16 unsigned bytes to a DCE/OSF formatted UUID string. Pre-condition: sizeof(dest) >= 37 */ void uuid_to_string(char *dest, uint8_t *uuid) { int i = 0; char *p = dest; for ( i = 0; i < 4; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p++ = '-'; for ( i = 4; i < 6; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p++ = '-'; for ( i = 6; i < 8; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p++ = '-'; for ( i = 8; i < 10; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p++ = '-'; for ( i = 10; i < 16; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p = '\0'; } static char *printnum(char *p, unsigned long num, int base) { unsigned long n; if ( (n = num/base) > 0 ) p = printnum(p, n, base); *p++ = "0123456789abcdef"[(int)(num % base)]; *p = '\0'; return p; } static void _doprint(void (*put)(char), const char *fmt, va_list ap) { register char *str, c; int lflag, zflag, nflag; char buffer[17]; unsigned value; int i, slen, pad; for ( ; *fmt != '\0'; fmt++ ) { if ( *fmt != '%' ) { put(*fmt); continue; } pad = zflag = nflag = lflag = 0; c = *++fmt; if ( (c == '-') || isdigit(c) ) { if ( c == '-' ) { nflag = 1; c = *++fmt; } zflag = c == '0'; for ( pad = 0; isdigit(c); c = *++fmt ) pad = (pad * 10) + c - '0'; } if ( c == 'l' ) /* long extension */ { lflag = 1; c = *++fmt; } if ( (c == 'd') || (c == 'u') || (c == 'o') || (c == 'x') ) { if ( lflag ) value = va_arg(ap, unsigned); else value = (unsigned) va_arg(ap, unsigned int); str = buffer; printnum(str, value, c == 'o' ? 8 : (c == 'x' ? 16 : 10)); goto printn; } else if ( (c == 'O') || (c == 'D') || (c == 'X') ) { value = va_arg(ap, unsigned); str = buffer; printnum(str, value, c == 'O' ? 8 : (c == 'X' ? 16 : 10)); printn: slen = strlen(str); for ( i = pad - slen; i > 0; i-- ) put(zflag ? '0' : ' '); while ( *str ) put(*str++); } else if ( c == 's' ) { str = va_arg(ap, char *); slen = strlen(str); if ( nflag == 0 ) for ( i = pad - slen; i > 0; i-- ) put(' '); while ( *str ) put(*str++); if ( nflag ) for ( i = pad - slen; i > 0; i-- ) put(' '); } else if ( c == 'c' ) { put(va_arg(ap, int)); } else { put(*fmt); } } } static void putchar(char c) { outb(0xe9, c); } int printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _doprint(putchar, fmt, ap); va_end(ap); return 0; } void mssleep(uint32_t waittime) { uint32_t i; uint8_t x, y = inb(0x61) & 0x10; /* Poll the DRAM refresh timer: I/O port 61h, bit 4 toggles every 15us. */ waittime *= 67; /* Convert milliseconds to multiples of 15us. */ for ( i = 0; i < waittime; i++ ) { while ( (x = inb(0x61) & 0x10) == y ) continue; y = x; } } /* * Search for the RSDP ACPI table in the memory starting at addr and * ending at addr + len - 1. */ static struct acpi_20_rsdp *__find_rsdp(const void *start, unsigned int len) { char *rsdp = (char *)start; char *end = rsdp + len; /* scan memory in steps of 16 bytes */ while (rsdp < end) { /* check for expected string */ if (!strncmp(rsdp, "RSD PTR ", 8)) return (struct acpi_20_rsdp *)rsdp; rsdp += 0x10; } return 0; } struct acpi_20_rsdp *find_rsdp(void) { struct acpi_20_rsdp *rsdp; uint16_t ebda_seg; ebda_seg = *(uint16_t *)ADDR_FROM_SEG_OFF(0x40, 0xe); rsdp = __find_rsdp((void *)(ebda_seg << 16), 1024); if (!rsdp) rsdp = __find_rsdp((void *)0xE0000, 0x20000); return rsdp; } uint32_t get_s3_waking_vector(void) { struct acpi_20_rsdp *rsdp = find_rsdp(); struct acpi_20_xsdt *xsdt; struct acpi_fadt *fadt; struct acpi_20_facs *facs; uint32_t vector; if (!rsdp) return 0; xsdt = (struct acpi_20_xsdt *)(long)rsdp->xsdt_address; if (!xsdt) return 0; fadt = (struct acpi_fadt *)(long)xsdt->entry[0]; if (!fadt || (fadt->header.signature != ACPI_FADT_SIGNATURE)) return 0; facs = (struct acpi_20_facs *)(long)fadt->x_firmware_ctrl; if (!facs) return 0; vector = facs->x_firmware_waking_vector; if (!vector) vector = facs->firmware_waking_vector; return vector; } xen-4.9.2/tools/firmware/rombios/32bit/32bitbios.c0000664000175000017500000000223413256712137017773 0ustar smbsmb/* * 32bitbios - jumptable for those function reachable from 16bit area * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (C) IBM Corporation, 2006 * * Author: Stefan Berger */ #include "rombios_compat.h" asm ( " .text \n" " movzwl %bx,%eax \n" " jmp *jumptable(,%eax,4) \n" " .data \n" "jumptable: \n" #define X(idx, ret, fn, args...) " .long "#fn"\n" #include "32bitprotos.h" #undef X ); xen-4.9.2/tools/firmware/rombios/32bit/rombios_compat.h0000664000175000017500000000432413256712137021217 0ustar smbsmb#ifndef ROMBIOS_COMPAT #define ROMBIOS_COMPAT /* * Compatibility functions and structures for transitioning between * 16 bit Bochs BIOS and 32 bit BIOS code. */ #define ADDR_FROM_SEG_OFF(seg, off) (void *)((((uint32_t)(seg)) << 4) + (off)) typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; typedef uint8_t Bit8u; typedef uint16_t Bit16u; typedef uint32_t Bit32u; #define SetCF(x) (x)->u.r8.flagsl |= 0x01 #define SetZF(x) (x)->u.r8.flagsl |= 0x40 #define ClearCF(x) (x)->u.r8.flagsl &= 0xfe #define ClearZF(x) (x)->u.r8.flagsl &= 0xbf #define GetCF(x) ((x)->u.r8.flagsl & 0x01) #define SET_CF() *FLAGS |= 0x0001 #define CLEAR_CF() *FLAGS &= 0xfffe #define GET_CF() (*FLAGS & 0x0001) #define SET_ZF() *FLAGS |= 0x0040 #define CLEAR_ZF() *FLAGS &= 0xffbf typedef struct { union { struct { Bit32u edi, esi, ebp, esp; Bit32u ebx, edx, ecx, eax; } r32; struct { Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4; Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8; } r16; struct { Bit32u filler[4]; Bit8u bl, bh; Bit16u filler1; Bit8u dl, dh; Bit16u filler2; Bit8u cl, ch; Bit16u filler3; Bit8u al, ah; Bit16u filler4; } r8; } u; } __attribute__((packed)) pushad_regs_t; static inline Bit32u read_dword(Bit16u seg, Bit16u off) { uint32_t *addr = (uint32_t *)ADDR_FROM_SEG_OFF(seg,off); return *addr; } static inline Bit16u read_word(Bit16u seg, Bit16u off) { uint16_t *addr = (uint16_t *)ADDR_FROM_SEG_OFF(seg,off); return *addr; } static inline Bit8u read_byte(Bit16u seg, Bit16u off) { uint8_t *addr = (uint8_t *)ADDR_FROM_SEG_OFF(seg,off); return *addr; } static inline void write_dword(Bit16u seg, Bit16u off, Bit32u val) { uint32_t *addr = (uint32_t *)ADDR_FROM_SEG_OFF(seg,off); *addr = val; } static inline void write_word(Bit16u seg, Bit16u off, Bit16u val) { uint16_t *addr = (uint16_t *)ADDR_FROM_SEG_OFF(seg,off); *addr = val; } static inline void write_byte(Bit16u seg, Bit16u off, Bit8u val) { uint8_t *addr = (uint8_t *)ADDR_FROM_SEG_OFF(seg,off); *addr = val; } #define X(idx, ret, fn, args...) ret fn (args); #include "32bitprotos.h" #undef X #endif xen-4.9.2/tools/firmware/rombios/32bitprotos.h0000664000175000017500000000137113256712137017450 0ustar smbsmbX(0, Bit32u, TCGInterruptHandler, pushad_regs_t *regs, Bit32u esds, Bit32u flags_ptr) X(1, void, tcpa_acpi_init, void) X(2, Bit32u, tcpa_extend_acpi_log, Bit32u entry_ptr) X(3, void, tcpa_calling_int19h,void) X(4, void, tcpa_returned_int19h, void) X(5, void, tcpa_add_event_separators, void) X(6, void, tcpa_wake_event, void) X(7, void, tcpa_add_bootdevice, Bit32u bootcd, Bit32u bootdrv) X(8, void, tcpa_start_option_rom_scan, void) X(9, void, tcpa_option_rom, Bit32u seg) X(10, void, tcpa_ipl, Bit32u bootcd, Bit32u seg, Bit32u off, Bit32u count) X(11, void, tcpa_measure_post, Bit32u from, Bit32u to) X(12, Bit32u, tcpa_initialize_tpm, Bit32u physpres) X(13, Bit32u, get_s3_waking_vector, void) X(14, Bit32u, pmm, void *argp) xen-4.9.2/tools/firmware/rombios/config.h0000664000175000017500000000220313256712137016516 0ustar smbsmb#ifndef _ROMBIOS_CONFIG_H #define _ROMBIOS_CONFIG_H /* Memory map. */ #define LOWHEAP_PHYSICAL_ADDRESS 0x00010000 #define LOWHEAP_SIZE 0x00070000 #define OPTIONROM_PHYSICAL_ADDRESS 0x000C8000 #define OPTIONROM_PHYSICAL_END 0x000EA000 #define BIOS_INFO_PHYSICAL_ADDRESS 0x000EA000 #define ACPI_PHYSICAL_ADDRESS 0x000EA020 #define E820_PHYSICAL_ADDRESS 0x000EA100 #define SMBIOS_PHYSICAL_ADDRESS 0x000EB000 #define SMBIOS_PHYSICAL_END 0x000F0000 #define ROMBIOS_PHYSICAL_ADDRESS 0x000F0000 /* Offsets from E820_PHYSICAL_ADDRESS. */ #define E820_NR_OFFSET 0x0 #define E820_OFFSET 0x8 #define E820_NR ((uint16_t *)(E820_PHYSICAL_ADDRESS + E820_NR_OFFSET)) #define E820 ((struct e820entry *)(E820_PHYSICAL_ADDRESS + E820_OFFSET)) /* Xen Platform Device */ #define XEN_PF_IOBASE 0x10 #define PFFLAG_ROM_LOCK 1 /* Sets whether ROM memory area is RW or RO */ /* Located at BIOS_INFO_PHYSICAL_ADDRESS. */ struct rombios_info { uint32_t bios32_entry; /* 0 - Entry point for 32-bit BIOS */ }; #define BIOSINFO_OFF_bios32_entry 0 #endif xen-4.9.2/tools/firmware/rombios/apmbios.S0000664000175000017500000001475713256712137016677 0ustar smbsmb// APM BIOS support for the Bochs BIOS // Copyright (C) 2004 Fabrice Bellard // // Debugging extensions, 16-bit interface and extended power options // Copyright (C) 2005 Struan Bartlett // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; If not, see . #if defined(APM_REAL) #define APMSYM(s) apmreal_ ## s #elif defined(APM_PROT16) #define APMSYM(s) apm16_ ## s #elif defined(APM_PROT32) #define APMSYM(s) apm32_ ## s #else #error unsupported APM mode #endif APMSYM(out_str): push eax push ebx mov ebx, eax APMSYM(out_str1): SEG CS mov al, byte ptr [bx] cmp al, #0 je APMSYM(out_str2) outb dx, al inc ebx jmp APMSYM(out_str1) APMSYM(out_str2): pop ebx pop eax ret APMSYM(07_poweroff_str): .ascii "Shutdown" db 0 APMSYM(07_suspend_str): .ascii "Suspend" db 0 APMSYM(07_standby_str): .ascii "Standby" db 0 #if DEBUG_APM APMSYM(put_str): push edx mov dx, #INFO_PORT call APMSYM(out_str) pop edx ret ; print the hex number in eax APMSYM(put_num): push eax push ebx push ecx push edx mov ecx, eax mov bx, #8 mov dx, #INFO_PORT APMSYM(put_num1): mov eax, ecx shr eax, #28 add al, #0x30 cmp al, #0x39 jbe APMSYM(put_num2) add al, #0x27 APMSYM(put_num2): outb dx, al shl ecx, #4 dec bx jne APMSYM(put_num1) pop edx pop ecx pop ebx pop eax ret APMSYM(put_reg): outb dx, al shr eax, #8 outb dx, al shr eax, #8 outb dx, al shr eax, #8 outb dx, al mov eax,ebx call APMSYM(put_num) mov al, #0x3b outb dx,al mov al, #0x20 outb dx,al ret APMSYM(put_regs): push eax push edx push ebx mov dx, #INFO_PORT mov ebx, eax mov eax, #0x3d584145 // 'EAX=' call APMSYM(put_reg) pop ebx push ebx mov eax, #0x3d584245 // 'EBX=' call APMSYM(put_reg) mov ebx, ecx mov eax, #0x3d584345 // 'ECX=' call APMSYM(put_reg) mov ebx, edx mov eax, #0x3d584445 // 'EDX=' call APMSYM(put_reg) mov ebx, esi mov eax, #0x3d495345 // 'ESI=' call APMSYM(put_reg) mov ebx, edi mov eax, #0x3d494445 // 'EDI=' call APMSYM(put_reg) mov al, #0x0a outb dx, al pop ebx pop edx pop eax ret #endif #if defined(APM_PROT32) _apm32_entry: #endif #if defined(APM_PROT16) _apm16_entry: #endif pushf #if defined(APM_REAL) _apmreal_entry: #endif #if DEBUG_APM call APMSYM(put_regs) #endif #if defined(APM_REAL) ;----------------- ; APM installation check APMSYM(00): cmp al, #0x00 jne APMSYM(01) mov ah, #1 // APM major version mov al, #2 // APM minor version mov bh, #0x50 // 'P' mov bl, #0x4d // 'M' // bit 0 : 16 bit interface supported // bit 1 : 32 bit interface supported mov cx, #0x3 jmp APMSYM(ok) ;----------------- ; APM real mode interface connect APMSYM(01): cmp al, #0x01 jne APMSYM(02) jmp APMSYM(ok) ;----------------- ; APM 16 bit protected mode interface connect APMSYM(02): cmp al, #0x02 jne APMSYM(03) mov bx, #_apm16_entry mov ax, #0xf000 // 16 bit code segment base mov si, #0xfff0 // 16 bit code segment size mov cx, #0xf000 // data segment address mov di, #0xfff0 // data segment length jmp APMSYM(ok) ;----------------- ; APM 32 bit protected mode interface connect APMSYM(03): cmp al, #0x03 jne APMSYM(04) mov ax, #0xf000 // 32 bit code segment base mov ebx, #_apm32_entry mov cx, #0xf000 // 16 bit code segment base // 32 bit code segment size (low 16 bits) // 16 bit code segment size (high 16 bits) mov esi, #0xfff0fff0 mov dx, #0xf000 // data segment address mov di, #0xfff0 // data segment length jmp APMSYM(ok) #endif ;----------------- ; APM interface disconnect APMSYM(04): cmp al, #0x04 jne APMSYM(05) jmp APMSYM(ok) ;----------------- ; APM cpu idle APMSYM(05): cmp al, #0x05 jne APMSYM(07) pushf ; XEN sti ; XEN: OS calls us with ints disabled -- better re-enable here! hlt popf ; XEN jmp APMSYM(ok) ;----------------- ; APM Set Power State APMSYM(07): cmp al, #0x07 jne APMSYM(08) cmp bx, #1 jne APMSYM(ok) cmp cx, #3 je APMSYM(07_poweroff) cmp cx, #2 je APMSYM(07_suspend) cmp cx, #1 je APMSYM(07_standby) jne APMSYM(ok) APMSYM(07_poweroff): // send power off event to emulator cli mov dx, #0x8900 mov ax, #APMSYM(07_poweroff_str) call APMSYM(out_str) APMSYM(07_1): hlt jmp APMSYM(07_1) APMSYM(07_suspend): push edx mov dx, #0x8900 mov ax, #APMSYM(07_suspend_str) call APMSYM(out_str) pop edx jmp APMSYM(ok) APMSYM(07_standby): push edx mov dx, #0x8900 mov ax, #APMSYM(07_standby_str) call APMSYM(out_str) pop edx jmp APMSYM(ok) ;----------------- ; APM Enable / Disable APMSYM(08): cmp al, #0x08 jne APMSYM(0a) jmp APMSYM(ok) ;----------------- ; Get Power Status APMSYM(0a): cmp al, #0x0a jne APMSYM(0b) mov bh, #0x01 // on line // mov bh, #0x02 // battery mov bl, #0xff // unknown battery status // mov bl, #0x03 // charging mov ch, #0x80 // no system battery // mov ch, #0x8 // charging mov cl, #0xff // unknown remaining time // mov cl, #50 mov dx, #0xffff // unknown remaining time mov si, #0 // zero battery // mov si, #1 // one battery jmp APMSYM(ok) ;----------------- ; Get PM Event APMSYM(0b): cmp al, #0x0b jne APMSYM(0e) mov ah, #0x80 // no event pending jmp APMSYM(error) ;----------------- ; APM Driver Version APMSYM(0e): cmp al, #0x0e jne APMSYM(0f) mov ah, #1 mov al, #2 jmp APMSYM(ok) ;----------------- ; APM Engage / Disengage APMSYM(0f): cmp al, #0x0f jne APMSYM(10) jmp APMSYM(ok) ;----------------- ; APM Get Capabilities APMSYM(10): cmp al, #0x10 jne APMSYM(unimplemented) mov bl, #0 mov cx, #0 jmp APMSYM(ok) ;----------------- APMSYM(ok): popf clc #if defined(APM_REAL) jmp iret_modify_cf #else retf #endif APMSYM(unimplemented): APMSYM(error): popf stc #if defined(APM_REAL) jmp iret_modify_cf #else retf #endif #undef APM_PROT32 #undef APM_PROT16 #undef APM_REAL #undef APMSYM xen-4.9.2/tools/firmware/rombios/makesym.perl0000775000175000017500000000152213256712137017440 0ustar smbsmb#!/usr/bin/perl # # $Id: makesym.perl,v 1.1 2002/11/24 22:45:40 bdenney Exp $ # # Read output file from as86 (e.g. rombios.txt) and write out a symbol # table suitable for the Bochs debugger. # $WHERE_BEFORE_SYM_TABLE = 0; $WHERE_IN_SYM_TABLE = 1; $WHERE_AFTER_SYM_TABLE = 2; $where = $WHERE_BEFORE_SYM_TABLE; while () { chop; if ($where == WHERE_BEFORE_SYM_TABLE && /^Symbols:/) { $where = $WHERE_IN_SYM_TABLE; } elsif ($where == $WHERE_IN_SYM_TABLE && /^$/) { $where = $WHERE_AFTER_SYM_TABLE; } if ($where == $WHERE_IN_SYM_TABLE) { @F = split (/\s+/); ($name[0], $junk, $addr[0], $junk, $name[1], $junk, $addr[1]) = @F; foreach $col (0,1) { next if length $addr[$col] < 1; $addr[$col] =~ tr/A-Z/a-z/; $addr[$col] = "000f" . $addr[$col]; print "$addr[$col] $name[$col]\n"; } } } xen-4.9.2/tools/firmware/rombios/rombios.c0000664000175000017500000117474213256712137016741 0ustar smbsmb///////////////////////////////////////////////////////////////////////// // $Id: rombios.c,v 1.221 2008/12/07 17:32:29 sshwarts Exp $ ////////////////////////////#///////////////////////////////////////////// // // Copyright (C) 2002 MandrakeSoft S.A. // // MandrakeSoft S.A. // 43, rue d'Aboukir // 75002 Paris - France // http://www.linux-mandrake.com/ // http://www.mandrakesoft.com/ // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; If not, see . // ROM BIOS for use with Bochs/Plex86/QEMU emulation environment #define uint8_t unsigned char #define uint16_t unsigned short #define uint32_t unsigned long #include "config.h" #define HVMASSIST #undef HVMTEST // Xen full virtualization does not handle unaligned IO with page crossing. // Disable 32-bit PIO as a workaround. #undef NO_PIO32 // ROM BIOS compatability entry points: // =================================== // $e05b ; POST Entry Point // $e2c3 ; NMI Handler Entry Point // $e3fe ; INT 13h Fixed Disk Services Entry Point // $e401 ; Fixed Disk Parameter Table // $e6f2 ; INT 19h Boot Load Service Entry Point // $e6f5 ; Configuration Data Table // $e729 ; Baud Rate Generator Table // $e739 ; INT 14h Serial Communications Service Entry Point // $e82e ; INT 16h Keyboard Service Entry Point // $e987 ; INT 09h Keyboard Service Entry Point // $ec59 ; INT 13h Diskette Service Entry Point // $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point // $efc7 ; Diskette Controller Parameter Table // $efd2 ; INT 17h Printer Service Entry Point // $f045 ; INT 10 Functions 0-Fh Entry Point // $f065 ; INT 10h Video Support Service Entry Point // $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh) // $f841 ; INT 12h Memory Size Service Entry Point // $f84d ; INT 11h Equipment List Service Entry Point // $f859 ; INT 15h System Services Entry Point // $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters) // $fe6e ; INT 1Ah Time-of-day Service Entry Point // $fea5 ; INT 08h System Timer ISR Entry Point // $fef3 ; Initial Interrupt Vector Offsets Loaded by POST // $ff53 ; IRET Instruction for Dummy Interrupt Handler // $ff54 ; INT 05h Print Screen Service Entry Point // $fff0 ; Power-up Entry Point // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY // $fffe ; System Model ID // NOTES for ATA/ATAPI driver (cbbochs@free.fr) // Features // - supports up to 4 ATA interfaces // - device/geometry detection // - 16bits/32bits device access // - pchs/lba access // - datain/dataout/packet command support // // NOTES for El-Torito Boot (cbbochs@free.fr) // - CD-ROM booting is only available if ATA/ATAPI Driver is available // - Current code is only able to boot mono-session cds // - Current code can not boot and emulate a hard-disk // the bios will panic otherwise // - Current code also use memory in EBDA segement. // - I used cmos byte 0x3D to store extended information on boot-device // - Code has to be modified modified to handle multiple cdrom drives // - Here are the cdrom boot failure codes: // 1 : no atapi device found // 2 : no atapi cdrom found // 3 : can not read cd - BRVD // 4 : cd is not eltorito (BRVD) // 5 : cd is not eltorito (ISO TAG) // 6 : cd is not eltorito (ELTORITO TAG) // 7 : can not read cd - boot catalog // 8 : boot catalog : bad header // 9 : boot catalog : bad platform // 10 : boot catalog : bad signature // 11 : boot catalog : bootable flag not set // 12 : can not read cd - boot image // // ATA driver // - EBDA segment. // I used memory starting at 0x121 in the segment // - the translation policy is defined in cmos regs 0x39 & 0x3a // // TODO : // // int74 // - needs to be reworked. Uses direct [bp] offsets. (?) // // int13: // - f04 (verify sectors) isn't complete (?) // - f02/03/04 should set current cyl,etc in BDA (?) // - rewrite int13_relocated & clean up int13 entry code // // NOTES: // - NMI access (bit7 of addr written to 70h) // // ATA driver // - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c) // - could send the multiple-sector read/write commands // // El-Torito // - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk" // - Implement remaining int13_cdemu functions (as defined by El-Torito specs) // - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded" // - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13. // This is ok. But DL should be reincremented afterwards. // - Fix all "FIXME ElTorito Various" // - should be able to boot any cdrom instead of the first one // // BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7) #include "rombios.h" #define DEBUG_ATA 0 #define DEBUG_INT13_HD 0 #define DEBUG_INT13_CD 0 #define DEBUG_INT13_ET 0 #define DEBUG_INT13_FL 0 #define DEBUG_INT15 0 #define DEBUG_INT16 0 #define DEBUG_INT1A 0 #define DEBUG_INT74 0 #define DEBUG_APM 0 #define BX_CPU 3 #define BX_USE_PS2_MOUSE 1 #define BX_CALL_INT15_4F 1 #define BX_USE_EBDA 1 #define BX_SUPPORT_FLOPPY 1 #define BX_FLOPPY_ON_CNT 37 /* 2 seconds */ #define BX_PCIBIOS 1 #define BX_APM 1 #define BX_USE_ATADRV 1 #define BX_ELTORITO_BOOT 1 #define BX_TCGBIOS 0 /* main switch for TCG BIOS ext. */ #define BX_PMM 1 /* POST Memory Manager */ #define BX_MAX_ATA_INTERFACES 4 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2) #define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */ #define BX_DEBUG_SERIAL 0 /* output to COM1 */ /* model byte 0xFC = AT */ #define SYS_MODEL_ID 0xFC #define SYS_SUBMODEL_ID 0x00 #define BIOS_REVISION 1 #define BIOS_CONFIG_TABLE 0xe6f5 #ifndef BIOS_BUILD_DATE # define BIOS_BUILD_DATE "06/23/99" #endif #define E820_SEG (Bit16u)(E820_PHYSICAL_ADDRESS >> 4) // 1K of base memory used for Extended Bios Data Area (EBDA) // EBDA is used for PS/2 mouse support, and IDE BIOS, etc. #define EBDA_SEG 0x9FC0 #define EBDA_SIZE 1 // In KiB #define BASE_MEM_IN_K (640 - EBDA_SIZE) /* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */ #define IPL_TABLE_OFFSET 0x0300 /* offset from EBDA */ #define IPL_TABLE_ENTRIES 8 #define IPL_COUNT_OFFSET 0x0380 /* u16: number of valid table entries */ #define IPL_SEQUENCE_OFFSET 0x0382 /* u16: next boot device */ #define IPL_BOOTFIRST_OFFSET 0x0384 /* u16: user selected device */ #define IPL_SIZE 0xff #define IPL_TYPE_FLOPPY 0x01 #define IPL_TYPE_HARDDISK 0x02 #define IPL_TYPE_CDROM 0x03 #define IPL_TYPE_BEV 0x80 // Sanity Checks #if BX_USE_ATADRV && BX_CPU<3 # error The ATA/ATAPI Driver can only to be used with a 386+ cpu #endif #if BX_USE_ATADRV && !BX_USE_EBDA # error ATA/ATAPI Driver can only be used if EBDA is available #endif #if BX_ELTORITO_BOOT && !BX_USE_ATADRV # error El-Torito Boot can only be use if ATA/ATAPI Driver is available #endif #if BX_PCIBIOS && BX_CPU<3 # error PCI BIOS can only be used with 386+ cpu #endif #if BX_APM && BX_CPU<3 # error APM BIOS can only be used with 386+ cpu #endif // define this if you want to make PCIBIOS working on a specific bridge only // undef enables PCIBIOS when at least one PCI device is found // i440FX is emulated by Bochs and QEMU #define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge // #20 is dec 20 // #$20 is hex 20 = 32 // #0x20 is hex 20 = 32 // LDA #$20 // JSR $E820 // LDD .i,S // JSR $C682 // mov al, #$20 // all hex literals should be prefixed with '0x' // grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c // no mov SEG-REG, #value, must mov register into seg-reg // grep -i "mov[ ]*.s" rombios.c // This is for compiling with gcc2 and gcc3 #define ASM_START #asm #define ASM_END #endasm ASM_START .rom .org 0x0000 #if BX_CPU >= 3 use16 386 #else use16 286 #endif MACRO HALT ;; the HALT macro is called with the line number of the HALT call. ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex ;; to print a BX_PANIC message. This will normally halt the simulation ;; with a message such as "BIOS panic at rombios.c, line 4091". ;; However, users can choose to make panics non-fatal and continue. #if BX_VIRTUAL_PORTS mov dx,#PANIC_PORT mov ax,#?1 out dx,ax #else mov dx,#0x80 mov ax,#?1 out dx,al #endif MEND MACRO JMP_AP db 0xea dw ?2 dw ?1 MEND MACRO SET_INT_VECTOR mov ax, ?3 mov ?1*4, ax mov ax, ?2 mov ?1*4+2, ax MEND ASM_END typedef unsigned char Bit8u; typedef unsigned short Bit16u; typedef unsigned short bx_bool; typedef unsigned long Bit32u; void memsetb(seg,offset,value,count); void memcpyb(dseg,doffset,sseg,soffset,count); void memcpyd(dseg,doffset,sseg,soffset,count); // memset of count bytes void memsetb(seg,offset,value,count) Bit16u seg; Bit16u offset; Bit16u value; Bit16u count; { ASM_START push bp mov bp, sp push ax push cx push es push di mov cx, 10[bp] ; count test cx, cx je memsetb_end mov ax, 4[bp] ; segment mov es, ax mov ax, 6[bp] ; offset mov di, ax mov al, 8[bp] ; value cld rep stosb memsetb_end: pop di pop es pop cx pop ax pop bp ASM_END } // memcpy of count bytes void memcpyb(dseg,doffset,sseg,soffset,count) Bit16u dseg; Bit16u doffset; Bit16u sseg; Bit16u soffset; Bit16u count; { ASM_START push bp mov bp, sp push ax push cx push es push di push ds push si mov cx, 12[bp] ; count test cx, cx je memcpyb_end mov ax, 4[bp] ; dsegment mov es, ax mov ax, 6[bp] ; doffset mov di, ax mov ax, 8[bp] ; ssegment mov ds, ax mov ax, 10[bp] ; soffset mov si, ax cld rep movsb memcpyb_end: pop si pop ds pop di pop es pop cx pop ax pop bp ASM_END } // memcpy of count dword void memcpyd(dseg,doffset,sseg,soffset,count) Bit16u dseg; Bit16u doffset; Bit16u sseg; Bit16u soffset; Bit16u count; { ASM_START push bp mov bp, sp push ax push cx push es push di push ds push si mov cx, 12[bp] ; count test cx, cx je memcpyd_end mov ax, 4[bp] ; dsegment mov es, ax mov ax, 6[bp] ; doffset mov di, ax mov ax, 8[bp] ; ssegment mov ds, ax mov ax, 10[bp] ; soffset mov si, ax cld rep movsd memcpyd_end: pop si pop ds pop di pop es pop cx pop ax pop bp ASM_END } // read_dword and write_dword functions static Bit32u read_dword(); static void write_dword(); Bit32u read_dword(seg, offset) Bit16u seg; Bit16u offset; { ASM_START push bp mov bp, sp push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov ax, [bx] add bx, #2 mov dx, [bx] ;; ax = return value (word) ;; dx = return value (word) pop ds pop bx pop bp ASM_END } void write_dword(seg, offset, data) Bit16u seg; Bit16u offset; Bit32u data; { ASM_START push bp mov bp, sp push ax push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov ax, 8[bp] ; data word mov [bx], ax ; write data word add bx, #2 mov ax, 10[bp] ; data word mov [bx], ax ; write data word pop ds pop bx pop ax pop bp ASM_END } // Bit32u (unsigned long) and long helper functions ASM_START ;; and function landl: landul: SEG SS and ax,[di] SEG SS and bx,2[di] ret ;; add function laddl: laddul: SEG SS add ax,[di] SEG SS adc bx,2[di] ret ;; cmp function lcmpl: lcmpul: and eax, #0x0000FFFF shl ebx, #16 or eax, ebx shr ebx, #16 SEG SS cmp eax, dword ptr [di] ret ;; sub function lsubl: lsubul: SEG SS sub ax,[di] SEG SS sbb bx,2[di] ret ;; mul function lmull: lmulul: and eax, #0x0000FFFF shl ebx, #16 or eax, ebx SEG SS mul eax, dword ptr [di] mov ebx, eax shr ebx, #16 ret ;; dec function ldecl: ldecul: SEG SS dec dword ptr [bx] ret ;; or function lorl: lorul: SEG SS or ax,[di] SEG SS or bx,2[di] ret ;; inc function lincl: lincul: SEG SS inc dword ptr [bx] ret ;; tst function ltstl: ltstul: and eax, #0x0000FFFF shl ebx, #16 or eax, ebx shr ebx, #16 test eax, eax ret ;; sr function lsrul: mov cx,di jcxz lsr_exit and eax, #0x0000FFFF shl ebx, #16 or eax, ebx lsr_loop: shr eax, #1 loop lsr_loop mov ebx, eax shr ebx, #16 lsr_exit: ret ;; sl function lsll: lslul: mov cx,di jcxz lsl_exit and eax, #0x0000FFFF shl ebx, #16 or eax, ebx lsl_loop: shl eax, #1 loop lsl_loop mov ebx, eax shr ebx, #16 lsl_exit: ret idiv_: cwd idiv bx ret idiv_u: xor dx,dx div bx ret ldivul: and eax, #0x0000FFFF shl ebx, #16 or eax, ebx xor edx, edx SEG SS mov bx, 2[di] shl ebx, #16 SEG SS mov bx, [di] div ebx mov ebx, eax shr ebx, #16 ret ASM_END // for access to RAM area which is used by interrupt vectors // and BIOS Data Area typedef struct { unsigned char filler1[0x400]; unsigned char filler2[0x6c]; Bit16u ticks_low; Bit16u ticks_high; Bit8u midnight_flag; } bios_data_t; #define BiosData ((bios_data_t *) 0) #if BX_USE_ATADRV typedef struct { Bit16u heads; // # heads Bit16u cylinders; // # cylinders Bit16u spt; // # sectors / track } chs_t; // DPTE definition typedef struct { Bit16u iobase1; Bit16u iobase2; Bit8u prefix; Bit8u unused; Bit8u irq; Bit8u blkcount; Bit8u dma; Bit8u pio; Bit16u options; Bit16u reserved; Bit8u revision; Bit8u checksum; } dpte_t; typedef struct { Bit8u iface; // ISA or PCI Bit16u iobase1; // IO Base 1 Bit16u iobase2; // IO Base 2 Bit8u irq; // IRQ } ata_channel_t; typedef struct { Bit8u type; // Detected type of ata (ata/atapi/none/unknown) Bit8u device; // Detected type of attached devices (hd/cd/none) Bit8u removable; // Removable device flag Bit8u lock; // Locks for removable devices Bit8u mode; // transfer mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA Bit16u blksize; // block size Bit8u translation; // type of translation chs_t lchs; // Logical CHS chs_t pchs; // Physical CHS Bit32u sectors_low; // Total sectors count Bit32u sectors_high; } ata_device_t; typedef struct { // ATA channels info ata_channel_t channels[BX_MAX_ATA_INTERFACES]; // ATA devices info ata_device_t devices[BX_MAX_ATA_DEVICES]; // // map between (bios hd id - 0x80) and ata channels Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES]; // map between (bios cd id - 0xE0) and ata channels Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES]; // Buffer for DPTE table dpte_t dpte; // Count of transferred sectors and bytes Bit16u trsfsectors; Bit32u trsfbytes; } ata_t; #if BX_ELTORITO_BOOT // ElTorito Device Emulation data typedef struct { Bit8u active; Bit8u media; Bit8u emulated_drive; Bit8u controller_index; Bit16u device_spec; Bit32u ilba; Bit16u buffer_segment; Bit16u load_segment; Bit16u sector_count; // Virtual device chs_t vdevice; } cdemu_t; #endif // BX_ELTORITO_BOOT #define X(idx, ret, fn, arg...) ret fn (); #include "32bitprotos.h" #undef X // for access to EBDA area // The EBDA structure should conform to // http://www.frontiernet.net/~fys/rombios.htm document // I made the ata and cdemu structs begin at 0x121 in the EBDA seg // EBDA must be at most 768 bytes; it lives at EBDA_SEG, and the boot // device tables are at EBDA_SEG:IPL_TABLE_OFFSET typedef struct { unsigned char ebda_size; unsigned char cmos_shutdown_status; unsigned char filler1[0x3B]; // FDPT - Can be splitted in data members if needed unsigned char fdpt0[0x10]; unsigned char fdpt1[0x10]; unsigned char filler2[0xC4]; // ATA Driver data ata_t ata; #if BX_ELTORITO_BOOT // El Torito Emulation data cdemu_t cdemu; #endif // BX_ELTORITO_BOOT } ebda_data_t; #define EBDA_CMOS_SHUTDOWN_STATUS_OFFSET 1 #define EbdaData ((ebda_data_t *) 0) // for access to the int13ext structure typedef struct { Bit8u size; Bit8u reserved; Bit16u count; Bit16u offset; Bit16u segment; Bit32u lba1; Bit32u lba2; } int13ext_t; #define Int13Ext ((int13ext_t *) 0) // Disk Physical Table definition typedef struct { Bit16u size; Bit16u infos; Bit32u cylinders; Bit32u heads; Bit32u spt; Bit32u sector_count1; Bit32u sector_count2; Bit16u blksize; Bit16u dpte_offset; Bit16u dpte_segment; Bit16u key; Bit8u dpi_length; Bit8u reserved1; Bit16u reserved2; Bit8u host_bus[4]; Bit8u iface_type[8]; Bit8u iface_path[8]; Bit8u device_path[8]; Bit8u reserved3; Bit8u checksum; } dpt_t; #define Int13DPT ((dpt_t *) 0) #endif // BX_USE_ATADRV typedef struct { union { struct { Bit16u di, si, bp, sp; Bit16u bx, dx, cx, ax; } r16; struct { Bit16u filler[4]; Bit8u bl, bh, dl, dh, cl, ch, al, ah; } r8; } u; } pusha_regs_t; typedef struct { union { struct { Bit32u edi, esi, ebp, esp; Bit32u ebx, edx, ecx, eax; } r32; struct { Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4; Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8; } r16; struct { Bit32u filler[4]; Bit8u bl, bh; Bit16u filler1; Bit8u dl, dh; Bit16u filler2; Bit8u cl, ch; Bit16u filler3; Bit8u al, ah; Bit16u filler4; } r8; } u; } pushad_regs_t; typedef struct { union { struct { Bit16u flags; } r16; struct { Bit8u flagsl; Bit8u flagsh; } r8; } u; } flags_t; #define SetCF(x) x.u.r8.flagsl |= 0x01 #define SetZF(x) x.u.r8.flagsl |= 0x40 #define ClearCF(x) x.u.r8.flagsl &= 0xfe #define ClearZF(x) x.u.r8.flagsl &= 0xbf #define GetCF(x) (x.u.r8.flagsl & 0x01) typedef struct { Bit16u ip; Bit16u cs; flags_t flags; } iret_addr_t; typedef struct { Bit16u type; Bit16u flags; Bit32u vector; Bit32u description; Bit32u reserved; } ipl_entry_t; static Bit8u inb(); static Bit8u inb_cmos(); static void outb(); static void outb_cmos(); static Bit16u inw(); static void outw(); static void init_rtc(); static bx_bool rtc_updating(); static Bit8u read_byte(); static Bit16u read_word(); static void write_byte(); static void write_word(); static void bios_printf(); static Bit8u inhibit_mouse_int_and_events(); static void enable_mouse_int_and_events(); static Bit8u send_to_mouse_ctrl(); static Bit8u get_mouse_data(); static void set_kbd_command_byte(); static void int09_function(); static void int13_harddisk(); static void int13_cdrom(); static void int13_cdemu(); static void int13_eltorito(); static void int13_diskette_function(); static void int14_function(); static void int15_function(); static void int16_function(); static void int17_function(); static void int18_function(); static void int1a_function(); static void int70_function(); static void int74_function(); static Bit16u get_CS(); static Bit16u get_SS(); static unsigned int enqueue_key(); static unsigned int dequeue_key(); static void get_hd_geometry(); static void set_diskette_ret_status(); static void set_diskette_current_cyl(); static void determine_floppy_media(); static bx_bool floppy_drive_exists(); static bx_bool floppy_drive_recal(); static bx_bool floppy_media_known(); static bx_bool floppy_media_sense(); static bx_bool set_enable_a20(); static void debugger_on(); static void debugger_off(); static void keyboard_init(); static void keyboard_panic(); static void shutdown_status_panic(); static void nmi_handler_msg(); static void delay_ticks(); static void delay_ticks_and_check_for_keystroke(); static void interactive_bootkey(); static void print_bios_banner(); static void print_boot_device(); static void print_boot_failure(); static void print_cdromboot_failure(); # if BX_USE_ATADRV // ATA / ATAPI driver void ata_init(); void ata_detect(); void ata_reset(); Bit16u ata_cmd_non_data(); Bit16u ata_cmd_data_in(); Bit16u ata_cmd_data_out(); Bit16u ata_cmd_packet(); Bit16u atapi_get_sense(); Bit16u atapi_is_ready(); Bit16u atapi_is_cdrom(); #endif // BX_USE_ATADRV #if BX_ELTORITO_BOOT void cdemu_init(); Bit8u cdemu_isactive(); Bit8u cdemu_emulated_drive(); Bit16u cdrom_boot(); #endif // BX_ELTORITO_BOOT static char bios_cvs_version_string[] = "$Revision: 1.221 $ $Date: 2008/12/07 17:32:29 $"; #define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team." #if DEBUG_ATA # define BX_DEBUG_ATA(a...) BX_DEBUG(a) #else # define BX_DEBUG_ATA(a...) #endif #if DEBUG_INT13_HD # define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a) #else # define BX_DEBUG_INT13_HD(a...) #endif #if DEBUG_INT13_CD # define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a) #else # define BX_DEBUG_INT13_CD(a...) #endif #if DEBUG_INT13_ET # define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a) #else # define BX_DEBUG_INT13_ET(a...) #endif #if DEBUG_INT13_FL # define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a) #else # define BX_DEBUG_INT13_FL(a...) #endif #if DEBUG_INT15 # define BX_DEBUG_INT15(a...) BX_DEBUG(a) #else # define BX_DEBUG_INT15(a...) #endif #if DEBUG_INT16 # define BX_DEBUG_INT16(a...) BX_DEBUG(a) #else # define BX_DEBUG_INT16(a...) #endif #if DEBUG_INT1A # define BX_DEBUG_INT1A(a...) BX_DEBUG(a) #else # define BX_DEBUG_INT1A(a...) #endif #if DEBUG_INT74 # define BX_DEBUG_INT74(a...) BX_DEBUG(a) #else # define BX_DEBUG_INT74(a...) #endif #define SET_AL(val8) AX = ((AX & 0xff00) | (val8)) #define SET_BL(val8) BX = ((BX & 0xff00) | (val8)) #define SET_CL(val8) CX = ((CX & 0xff00) | (val8)) #define SET_DL(val8) DX = ((DX & 0xff00) | (val8)) #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8)) #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8)) #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8)) #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8)) #define GET_AL() ( AX & 0x00ff ) #define GET_BL() ( BX & 0x00ff ) #define GET_CL() ( CX & 0x00ff ) #define GET_DL() ( DX & 0x00ff ) #define GET_AH() ( AX >> 8 ) #define GET_BH() ( BX >> 8 ) #define GET_CH() ( CX >> 8 ) #define GET_DH() ( DX >> 8 ) #define GET_ELDL() ( ELDX & 0x00ff ) #define GET_ELDH() ( ELDX >> 8 ) #define SET_CF() FLAGS |= 0x0001 #define CLEAR_CF() FLAGS &= 0xfffe #define GET_CF() (FLAGS & 0x0001) #define SET_ZF() FLAGS |= 0x0040 #define CLEAR_ZF() FLAGS &= 0xffbf #define GET_ZF() (FLAGS & 0x0040) #define UNSUPPORTED_FUNCTION 0x86 #define none 0 #define MAX_SCAN_CODE 0x58 static struct { Bit16u normal; Bit16u shift; Bit16u control; Bit16u alt; Bit8u lock_flags; } scan_to_scanascii[MAX_SCAN_CODE + 1] = { { none, none, none, none, none }, { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */ { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */ { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */ { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */ { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */ { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */ { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */ { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */ { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */ { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */ { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */ { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */ { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */ { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */ { 0x0f09, 0x0f00, none, none, none }, /* tab */ { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */ { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */ { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */ { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */ { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */ { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */ { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */ { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */ { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */ { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */ { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */ { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */ { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */ { none, none, none, none, none }, /* L Ctrl */ { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */ { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */ { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */ { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */ { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */ { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */ { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */ { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */ { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */ { 0x273b, 0x273a, none, none, none }, /* ;: */ { 0x2827, 0x2822, none, none, none }, /* '" */ { 0x2960, 0x297e, none, none, none }, /* `~ */ { none, none, none, none, none }, /* L shift */ { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */ { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */ { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */ { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */ { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */ { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */ { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */ { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */ { 0x332c, 0x333c, none, none, none }, /* ,< */ { 0x342e, 0x343e, none, none, none }, /* .> */ { 0x352f, 0x353f, none, none, none }, /* /? */ { none, none, none, none, none }, /* R Shift */ { 0x372a, 0x372a, none, none, none }, /* * */ { none, none, none, none, none }, /* L Alt */ { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */ { none, none, none, none, none }, /* caps lock */ { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */ { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */ { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */ { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */ { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */ { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */ { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */ { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */ { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */ { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */ { none, none, none, none, none }, /* Num Lock */ { none, none, none, none, none }, /* Scroll Lock */ { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */ { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */ { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */ { 0x4a2d, 0x4a2d, none, none, none }, /* - */ { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */ { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */ { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */ { 0x4e2b, 0x4e2b, none, none, none }, /* + */ { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */ { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */ { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */ { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */ { 0x5300, 0x532e, none, none, 0x20 }, /* Del */ { none, none, none, none, none }, { none, none, none, none, none }, { 0x565c, 0x567c, none, none, none }, /* \| */ { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */ { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */ }; Bit8u inb(port) Bit16u port; { ASM_START push bp mov bp, sp push dx mov dx, 4[bp] in al, dx pop dx pop bp ASM_END } #if BX_USE_ATADRV Bit16u inw(port) Bit16u port; { ASM_START push bp mov bp, sp push dx mov dx, 4[bp] in ax, dx pop dx pop bp ASM_END } #endif void outb(port, val) Bit16u port; Bit8u val; { ASM_START push bp mov bp, sp push ax push dx mov dx, 4[bp] mov al, 6[bp] out dx, al pop dx pop ax pop bp ASM_END } #if BX_USE_ATADRV void outw(port, val) Bit16u port; Bit16u val; { ASM_START push bp mov bp, sp push ax push dx mov dx, 4[bp] mov ax, 6[bp] out dx, ax pop dx pop ax pop bp ASM_END } #endif void outb_cmos(cmos_reg, val) Bit8u cmos_reg; Bit8u val; { ASM_START push bp mov bp, sp mov al, 4[bp] ;; cmos_reg out 0x70, al mov al, 6[bp] ;; val out 0x71, al pop bp ASM_END } Bit8u inb_cmos(cmos_reg) Bit8u cmos_reg; { ASM_START push bp mov bp, sp mov al, 4[bp] ;; cmos_reg out 0x70, al in al, 0x71 pop bp ASM_END } void init_rtc() { outb_cmos(0x0a, 0x26); outb_cmos(0x0b, 0x02); inb_cmos(0x0c); inb_cmos(0x0d); } bx_bool rtc_updating() { // This function checks to see if the update-in-progress bit // is set in CMOS Status Register A. If not, it returns 0. // If it is set, it tries to wait until there is a transition // to 0, and will return 0 if such a transition occurs. A 1 // is returned only after timing out. The maximum period // that this bit should be set is constrained to 244useconds. // The count I use below guarantees coverage or more than // this time, with any reasonable IPS setting. Bit16u count; count = 25000; while (--count != 0) { if ( (inb_cmos(0x0a) & 0x80) == 0 ) return(0); } return(1); // update-in-progress never transitioned to 0 } Bit8u read_byte(seg, offset) Bit16u seg; Bit16u offset; { ASM_START push bp mov bp, sp push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov al, [bx] ;; al = return value (byte) pop ds pop bx pop bp ASM_END } Bit16u read_word(seg, offset) Bit16u seg; Bit16u offset; { ASM_START push bp mov bp, sp push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov ax, [bx] ;; ax = return value (word) pop ds pop bx pop bp ASM_END } void write_byte(seg, offset, data) Bit16u seg; Bit16u offset; Bit8u data; { ASM_START push bp mov bp, sp push ax push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov al, 8[bp] ; data byte mov [bx], al ; write data byte pop ds pop bx pop ax pop bp ASM_END } void write_word(seg, offset, data) Bit16u seg; Bit16u offset; Bit16u data; { ASM_START push bp mov bp, sp push ax push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov ax, 8[bp] ; data word mov [bx], ax ; write data word pop ds pop bx pop ax pop bp ASM_END } Bit16u get_CS() { ASM_START mov ax, cs ASM_END } Bit16u get_SS() { ASM_START mov ax, ss ASM_END } #ifdef HVMASSIST void fixup_base_mem_in_k() { /* Report the proper base memory size at address 0x0413: otherwise * non-e820 code will clobber things if BASE_MEM_IN_K is bigger than * the first e820 entry. Get the size by reading the second 64bit * field of the first e820 slot. */ Bit32u base_mem = read_dword(E820_SEG, E820_OFFSET + 8); write_word(0x40, 0x13, base_mem >> 10); } void enable_rom_write_access() { outb(XEN_PF_IOBASE, 0); } void disable_rom_write_access() { outb(XEN_PF_IOBASE, PFFLAG_ROM_LOCK); } #endif /* HVMASSIST */ #if BX_DEBUG_SERIAL /* serial debug port*/ #define BX_DEBUG_PORT 0x03f8 /* data */ #define UART_RBR 0x00 #define UART_THR 0x00 /* control */ #define UART_IER 0x01 #define UART_IIR 0x02 #define UART_FCR 0x02 #define UART_LCR 0x03 #define UART_MCR 0x04 #define UART_DLL 0x00 #define UART_DLM 0x01 /* status */ #define UART_LSR 0x05 #define UART_MSR 0x06 #define UART_SCR 0x07 int uart_can_tx_byte(base_port) Bit16u base_port; { return inb(base_port + UART_LSR) & 0x20; } void uart_wait_to_tx_byte(base_port) Bit16u base_port; { while (!uart_can_tx_byte(base_port)); } void uart_wait_until_sent(base_port) Bit16u base_port; { while (!(inb(base_port + UART_LSR) & 0x40)); } void uart_tx_byte(base_port, data) Bit16u base_port; Bit8u data; { uart_wait_to_tx_byte(base_port); outb(base_port + UART_THR, data); uart_wait_until_sent(base_port); } #endif void wrch(c) Bit8u c; { ASM_START push bp mov bp, sp push bx mov ah, #0x0e mov al, 4[bp] xor bx,bx int #0x10 pop bx pop bp ASM_END } void send(action, c) Bit16u action; Bit8u c; { #if BX_DEBUG_SERIAL if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r'); uart_tx_byte(BX_DEBUG_PORT, c); #endif #ifdef HVMASSIST outb(0xE9, c); #endif #if BX_VIRTUAL_PORTS if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c); if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c); #endif if (action & BIOS_PRINTF_SCREEN) { if (c == '\n') wrch('\r'); wrch(c); } } void put_int(action, val, width, neg) Bit16u action; short val, width; bx_bool neg; { short nval = val / 10; if (nval) put_int(action, nval, width - 1, neg); else { while (--width > 0) send(action, ' '); if (neg) send(action, '-'); } send(action, val - (nval * 10) + '0'); } void put_uint(action, val, width, neg) Bit16u action; unsigned short val; short width; bx_bool neg; { unsigned short nval = val / 10; if (nval) put_uint(action, nval, width - 1, neg); else { while (--width > 0) send(action, ' '); if (neg) send(action, '-'); } send(action, val - (nval * 10) + '0'); } void put_luint(action, val, width, neg) Bit16u action; unsigned long val; short width; bx_bool neg; { unsigned long nval = val / 10; if (nval) put_luint(action, nval, width - 1, neg); else { while (--width > 0) send(action, ' '); if (neg) send(action, '-'); } send(action, val - (nval * 10) + '0'); } void put_str(action, segment, offset) Bit16u action; Bit16u segment; Bit16u offset; { Bit8u c; while (c = read_byte(segment, offset)) { send(action, c); offset++; } } void delay_ticks(ticks) Bit16u ticks; { long ticks_to_wait, delta; Bit32u prev_ticks, t; /* * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock. * We also have to be careful about interrupt storms. */ ASM_START pushf sti ASM_END ticks_to_wait = ticks; prev_ticks = read_dword(0x0, 0x46c); do { ASM_START hlt ASM_END t = read_dword(0x0, 0x46c); if (t > prev_ticks) { delta = t - prev_ticks; /* The temp var is required or bcc screws up. */ ticks_to_wait -= delta; } else if (t < prev_ticks) { ticks_to_wait -= t; /* wrapped */ } prev_ticks = t; } while (ticks_to_wait > 0); ASM_START cli popf ASM_END } Bit8u check_for_keystroke() { ASM_START mov ax, #0x100 int #0x16 jz no_key mov al, #1 jmp done no_key: xor al, al done: ASM_END } Bit8u get_keystroke() { ASM_START mov ax, #0x0 int #0x16 xchg ah, al ASM_END } void delay_ticks_and_check_for_keystroke(ticks, count) Bit16u ticks, count; { Bit16u i; for (i = 1; i <= count; i++) { delay_ticks(ticks); if (check_for_keystroke()) break; } } //-------------------------------------------------------------------------- // bios_printf() // A compact variable argument printf function. // // Supports %[format_width][length]format // where format can be x,X,u,d,s,S,c // and the optional length modifier is l (ell) //-------------------------------------------------------------------------- void bios_printf(action, s) Bit16u action; Bit8u *s; { Bit8u c, format_char; bx_bool in_format; short i; Bit16u *arg_ptr; Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width, hexadd; arg_ptr = &s; arg_seg = get_SS(); in_format = 0; format_width = 0; if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) { #if BX_VIRTUAL_PORTS outb(PANIC_PORT2, 0x00); #endif bios_printf (BIOS_PRINTF_SCREEN, "FATAL: "); } while (c = read_byte(get_CS(), s)) { if ( c == '%' ) { in_format = 1; format_width = 0; } else if (in_format) { if ( (c>='0') && (c<='9') ) { format_width = (format_width * 10) + (c - '0'); } else { arg_ptr++; // increment to next arg arg = read_word(arg_seg, arg_ptr); if (c == 'x' || c == 'X') { if (format_width == 0) format_width = 4; if (c == 'x') hexadd = 'a'; else hexadd = 'A'; for (i=format_width-1; i>=0; i--) { nibble = (arg >> (4 * i)) & 0x000f; send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd)); } } else if (c == 'u') { put_uint(action, arg, format_width, 0); } else if (c == 'l') { s++; c = read_byte(get_CS(), s); /* is it ld,lx,lu? */ arg_ptr++; /* increment to next arg */ hibyte = read_word(arg_seg, arg_ptr); if (c == 'd') { if (hibyte & 0x8000) put_luint(action, 0L-(((Bit32u) hibyte << 16) | arg), format_width-1, 1); else put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0); } else if (c == 'u') { put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0); } else if (c == 'x' || c == 'X') { if (format_width == 0) format_width = 8; if (c == 'x') hexadd = 'a'; else hexadd = 'A'; for (i=format_width-1; i>=0; i--) { nibble = ((((Bit32u) hibyte <<16) | arg) >> (4 * i)) & 0x000f; send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd)); } } } else if (c == 'd') { if (arg & 0x8000) put_int(action, -arg, format_width - 1, 1); else put_int(action, arg, format_width, 0); } else if (c == 's') { put_str(action, get_CS(), arg); } else if (c == 'S') { hibyte = arg; arg_ptr++; arg = read_word(arg_seg, arg_ptr); put_str(action, hibyte, arg); } else if (c == 'c') { send(action, arg); } else BX_PANIC("bios_printf: unknown format\n"); in_format = 0; } } else { send(action, c); } s ++; } if (action & BIOS_PRINTF_HALT) { // freeze in a busy loop. ASM_START cli halt2_loop: hlt jmp halt2_loop ASM_END } } //-------------------------------------------------------------------------- // keyboard_init //-------------------------------------------------------------------------- // this file is based on LinuxBIOS implementation of keyboard.c // could convert to #asm to gain space void keyboard_init() { Bit16u max; /* ------------------- Flush buffers ------------------------*/ /* Wait until buffer is empty */ max=0xffff; while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00); /* flush incoming keys */ max=2; while (--max > 0) { outb(0x80, 0x00); if (inb(0x64) & 0x01) { inb(0x60); max = 2; } } // Due to timer issues, and if the IPS setting is > 15000000, // the incoming keys might not be flushed here. That will // cause a panic a few lines below. See sourceforge bug report : // [ 642031 ] FATAL: Keyboard RESET error:993 /* ------------------- controller side ----------------------*/ /* send cmd = 0xAA, self test 8042 */ outb(0x64, 0xaa); /* Wait until buffer is empty */ max=0xffff; while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00); if (max==0x0) keyboard_panic(00); /* Wait for data */ max=0xffff; while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01); if (max==0x0) keyboard_panic(01); /* read self-test result, 0x55 should be returned from 0x60 */ if ((inb(0x60) != 0x55)){ keyboard_panic(991); } /* send cmd = 0xAB, keyboard interface test */ outb(0x64,0xab); /* Wait until buffer is empty */ max=0xffff; while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10); if (max==0x0) keyboard_panic(10); /* Wait for data */ max=0xffff; while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11); if (max==0x0) keyboard_panic(11); /* read keyboard interface test result, */ /* 0x00 should be returned form 0x60 */ if ((inb(0x60) != 0x00)) { keyboard_panic(992); } /* Enable Keyboard clock */ outb(0x64,0xae); outb(0x64,0xa8); /* ------------------- keyboard side ------------------------*/ /* reset kerboard and self test (keyboard side) */ outb(0x60, 0xff); /* Wait until buffer is empty */ max=0xffff; while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20); if (max==0x0) keyboard_panic(20); /* Wait for data */ max=0xffff; while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21); if (max==0x0) keyboard_panic(21); /* keyboard should return ACK */ if ((inb(0x60) != 0xfa)) { keyboard_panic(993); } /* Wait for data */ max=0xffff; while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31); if (max==0x0) keyboard_panic(31); if ((inb(0x60) != 0xaa)) { keyboard_panic(994); } /* Disable keyboard */ outb(0x60, 0xf5); /* Wait until buffer is empty */ max=0xffff; while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40); if (max==0x0) keyboard_panic(40); /* Wait for data */ max=0xffff; while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41); if (max==0x0) keyboard_panic(41); /* keyboard should return ACK */ if ((inb(0x60) != 0xfa)) { keyboard_panic(995); } /* Write Keyboard Mode */ outb(0x64, 0x60); /* Wait until buffer is empty */ max=0xffff; while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50); if (max==0x0) keyboard_panic(50); /* send cmd: scan code convert, disable mouse, enable IRQ 1 */ outb(0x60, 0x61); /* Wait until buffer is empty */ max=0xffff; while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60); if (max==0x0) keyboard_panic(60); /* Enable keyboard */ outb(0x60, 0xf4); /* Wait until buffer is empty */ max=0xffff; while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70); if (max==0x0) keyboard_panic(70); /* Wait for data */ max=0xffff; while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71); if (max==0x0) keyboard_panic(70); /* keyboard should return ACK */ if ((inb(0x60) != 0xfa)) { keyboard_panic(996); } outb(0x80, 0x77); } //-------------------------------------------------------------------------- // keyboard_panic //-------------------------------------------------------------------------- void keyboard_panic(status) Bit16u status; { // If you're getting a 993 keyboard panic here, // please see the comment in keyboard_init BX_PANIC("Keyboard error:%u\n",status); } #define CMOS_SHUTDOWN_S3 0xFE //-------------------------------------------------------------------------- // machine_reset //-------------------------------------------------------------------------- void machine_reset() { ASM_START ;we must check whether CMOS_SHUTDOWN_S3 is set or not ;if it is s3 resume, just jmp back to normal Post Entry ;below port io will prevent s3 resume mov al, #0x0f out 0x70, al in al, 0x71 cmp al, #0xFE jz post ASM_END /* Frob the keyboard reset line to reset the processor */ outb(0x64, 0x60); /* Map the flags register at data port (0x60) */ outb(0x60, 0x14); /* Set the flags to system|disable */ outb(0x64, 0xfe); /* Pulse output 0 (system reset) low */ BX_PANIC("Couldn't reset the machine\n"); } //-------------------------------------------------------------------------- // clobber_entry_point // Because PV drivers in HVM guests detach some of the emulated devices, // it is not safe to do a soft reboot by just dropping to real mode and // jumping at ffff:0000. -- the boot drives might have disappeared! // This rather foul function overwrites(!) the BIOS entry point // to point at machine-reset, which will cause the Xen tools to // rebuild the whole machine from scratch. //-------------------------------------------------------------------------- void clobber_entry_point() { /* The instruction at the entry point is one byte (0xea) for the * jump opcode, then two bytes of address, then two of segment. * Overwrite the address bytes.*/ write_word(0xffff, 0x0001, machine_reset); } //-------------------------------------------------------------------------- // shutdown_status_panic // called when the shutdown statsu is not implemented, displays the status //-------------------------------------------------------------------------- void shutdown_status_panic(status) Bit16u status; { BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status); } void s3_resume_panic() { BX_PANIC("Returned from s3_resume.\n"); } //-------------------------------------------------------------------------- // print_bios_banner // displays a the bios version //-------------------------------------------------------------------------- void print_bios_banner() { printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ", BIOS_BUILD_DATE, bios_cvs_version_string); printf( #if BX_APM "apmbios " #endif #if BX_PCIBIOS "pcibios " #endif #if BX_ELTORITO_BOOT "eltorito " #endif #if BX_ROMBIOS32 "rombios32 " #endif #if BX_TCGBIOS "TCG-enabled " #endif #if BX_PMM "PMM " #endif "\n\n"); } //-------------------------------------------------------------------------- // BIOS Boot Specification 1.0.1 compatibility // // Very basic support for the BIOS Boot Specification, which allows expansion // ROMs to register themselves as boot devices, instead of just stealing the // INT 19h boot vector. // // This is a hack: to do it properly requires a proper PnP BIOS and we aren't // one; we just lie to the option ROMs to make them behave correctly. // We also don't support letting option ROMs register as bootable disk // drives (BCVs), only as bootable devices (BEVs). // // http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm //-------------------------------------------------------------------------- static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"}; static void init_boot_vectors() { ipl_entry_t e; Bit16u count = 0; Bit16u ss = get_SS(); Bit16u ebda_seg = read_word(0x0040, 0x000E); /* Clear out the IPL table. */ memsetb(ebda_seg, IPL_TABLE_OFFSET, 0, IPL_SIZE); /* User selected device not set */ write_word(ebda_seg, IPL_BOOTFIRST_OFFSET, 0xFFFF); /* Floppy drive */ e.type = IPL_TYPE_FLOPPY; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; memcpyb(ebda_seg, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); count++; /* First HDD */ e.type = IPL_TYPE_HARDDISK; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; memcpyb(ebda_seg, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); count++; #if BX_ELTORITO_BOOT /* CDROM */ e.type = IPL_TYPE_CDROM; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0; memcpyb(ebda_seg, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e)); count++; #endif /* Remember how many devices we have */ write_word(ebda_seg, IPL_COUNT_OFFSET, count); /* Not tried booting anything yet */ write_word(ebda_seg, IPL_SEQUENCE_OFFSET, 0xffff); } static Bit8u get_boot_vector(i, e) Bit16u i; ipl_entry_t *e; { Bit16u count; Bit16u ss = get_SS(); Bit16u ebda_seg = read_word(0x0040, 0x000E); /* Get the count of boot devices, and refuse to overrun the array */ count = read_word(ebda_seg, IPL_COUNT_OFFSET); if (i >= count) return 0; /* OK to read this device */ memcpyb(ss, e, ebda_seg, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e)); return 1; } #if BX_ELTORITO_BOOT void interactive_bootkey() { ipl_entry_t e; Bit16u count; char description[33]; Bit8u scan_code; Bit8u i; Bit16u ss = get_SS(); Bit16u valid_choice = 0; Bit16u ebda_seg = read_word(0x0040, 0x000E); printf("\n\nPress F12 for boot menu.\n\n"); while (check_for_keystroke()) { scan_code = get_keystroke(); if (scan_code != 0x86) /* F12 */ continue; while (check_for_keystroke()) get_keystroke(); printf("Select boot device:\n\n"); count = read_word(ebda_seg, IPL_COUNT_OFFSET); for (i = 0; i < count; i++) { memcpyb(ss, &e, ebda_seg, IPL_TABLE_OFFSET + i * sizeof (e), sizeof (e)); printf("%d. ", i+1); switch(e.type) { case IPL_TYPE_FLOPPY: case IPL_TYPE_HARDDISK: case IPL_TYPE_CDROM: printf("%s\n", drivetypes[e.type]); break; case IPL_TYPE_BEV: printf("%s", drivetypes[4]); if (e.description != 0) { memcpyb(ss, &description, (Bit16u)(e.description >> 16), (Bit16u)(e.description & 0xffff), 32); description[32] = 0; printf(" [%S]", ss, description); } printf("\n"); break; } } count++; while (!valid_choice) { scan_code = get_keystroke(); if (scan_code == 0x01 || scan_code == 0x58) /* ESC or F12 */ { valid_choice = 1; } else if (scan_code <= count) { valid_choice = 1; scan_code -= 1; /* Set user selected device */ write_word(ebda_seg, IPL_BOOTFIRST_OFFSET, scan_code); } } printf("\n"); break; } } #endif // BX_ELTORITO_BOOT //-------------------------------------------------------------------------- // print_boot_device // displays the boot device //-------------------------------------------------------------------------- void print_boot_device(type, desc) Bit16u type; Bit32u desc; { char description[33]; Bit16u ss = get_SS(); /* NIC appears as type 0x80 */ if (type == IPL_TYPE_BEV) type = 0x4; if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n"); printf("Booting from %s", drivetypes[type]); /* print product string if BEV */ if (type == 4 && desc != 0) { /* first 32 bytes are significant */ memcpyb(ss, &description, (Bit16u)(desc >> 16), (Bit16u)(desc & 0xffff), 32); /* terminate string */ description[32] = 0; printf(" [%S]", ss, description); } printf("...\n"); } //-------------------------------------------------------------------------- // print_boot_failure // displays the reason why boot failed //-------------------------------------------------------------------------- void print_boot_failure(type, reason) Bit16u type; Bit8u reason; { if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n"); printf("Boot from %s failed", drivetypes[type]); if (type < 4) { /* Report the reason too */ if (reason==0) printf(": not a bootable disk"); else printf(": could not read the boot disk"); } printf("\n\n"); } //-------------------------------------------------------------------------- // print_cdromboot_failure // displays the reason why boot failed //-------------------------------------------------------------------------- void print_cdromboot_failure( code ) Bit16u code; { bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code); return; } void nmi_handler_msg() { BX_PANIC("NMI Handler called\n"); } void int18_panic_msg() { BX_PANIC("INT18: BOOT FAILURE\n"); } void log_bios_start() { #if BX_DEBUG_SERIAL outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */ #endif BX_INFO("%s\n", bios_cvs_version_string); } bx_bool set_enable_a20(val) bx_bool val; { Bit8u oldval; // Use PS2 System Control port A to set A20 enable // get current setting first oldval = inb(0x92); // change A20 status if (val) outb(0x92, oldval | 0x02); else outb(0x92, oldval & 0xfd); return((oldval & 0x02) != 0); } void debugger_on() { outb(0xfedc, 0x01); } void debugger_off() { outb(0xfedc, 0x00); } int s3_resume() { Bit32u s3_wakeup_vector; Bit8u s3_resume_flag; s3_resume_flag = read_byte(0x40, 0xb0); #ifdef HVMASSIST s3_wakeup_vector = get_s3_waking_vector(); #else s3_wakeup_vector = read_dword(0x40, 0xb2); #endif BX_INFO("S3 resume called %x 0x%lx\n", s3_resume_flag, s3_wakeup_vector); if (s3_resume_flag != CMOS_SHUTDOWN_S3 || !s3_wakeup_vector) return 0; write_byte(0x40, 0xb0, 0); /* setup wakeup vector */ write_word(0x40, 0xb6, (s3_wakeup_vector & 0xF)); /* IP */ write_word(0x40, 0xb8, (s3_wakeup_vector >> 4)); /* CS */ BX_INFO("S3 resume jump to %x:%x\n", (s3_wakeup_vector >> 4), (s3_wakeup_vector & 0xF)); ASM_START jmpf [0x04b6] ASM_END return 1; } #if BX_USE_ATADRV // --------------------------------------------------------------------------- // Start of ATA/ATAPI Driver // --------------------------------------------------------------------------- // Global defines -- ATA register and register bits. // command block & control block regs #define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0 #define ATA_CB_ERR 1 // error in pio_base_addr1+1 #define ATA_CB_FR 1 // feature reg out pio_base_addr1+1 #define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2 #define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3 #define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4 #define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5 #define ATA_CB_DH 6 // device head in/out pio_base_addr1+6 #define ATA_CB_STAT 7 // primary status in pio_base_addr1+7 #define ATA_CB_CMD 7 // command out pio_base_addr1+7 #define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6 #define ATA_CB_DC 6 // device control out pio_base_addr2+6 #define ATA_CB_DA 7 // device address in pio_base_addr2+7 #define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC #define ATA_CB_ER_BBK 0x80 // ATA bad block #define ATA_CB_ER_UNC 0x40 // ATA uncorrected error #define ATA_CB_ER_MC 0x20 // ATA media change #define ATA_CB_ER_IDNF 0x10 // ATA id not found #define ATA_CB_ER_MCR 0x08 // ATA media change request #define ATA_CB_ER_ABRT 0x04 // ATA command aborted #define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found #define ATA_CB_ER_NDAM 0x01 // ATA address mark not found #define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask) #define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request #define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort #define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media #define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication // ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC) #define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask) #define ATA_CB_SC_P_REL 0x04 // ATAPI release #define ATA_CB_SC_P_IO 0x02 // ATAPI I/O #define ATA_CB_SC_P_CD 0x01 // ATAPI C/D // bits 7-4 of the device/head (CB_DH) reg #define ATA_CB_DH_DEV0 0xa0 // select device 0 #define ATA_CB_DH_DEV1 0xb0 // select device 1 #define ATA_CB_DH_LBA 0x40 // use LBA // status reg (CB_STAT and CB_ASTAT) bits #define ATA_CB_STAT_BSY 0x80 // busy #define ATA_CB_STAT_RDY 0x40 // ready #define ATA_CB_STAT_DF 0x20 // device fault #define ATA_CB_STAT_WFT 0x20 // write fault (old name) #define ATA_CB_STAT_SKC 0x10 // seek complete #define ATA_CB_STAT_SERV 0x10 // service #define ATA_CB_STAT_DRQ 0x08 // data request #define ATA_CB_STAT_CORR 0x04 // corrected #define ATA_CB_STAT_IDX 0x02 // index #define ATA_CB_STAT_ERR 0x01 // error (ATA) #define ATA_CB_STAT_CHK 0x01 // check (ATAPI) // device control reg (CB_DC) bits #define ATA_CB_DC_HD15 0x08 // bit should always be set to one #define ATA_CB_DC_SRST 0x04 // soft reset #define ATA_CB_DC_NIEN 0x02 // disable interrupts // Most mandtory and optional ATA commands (from ATA-3), #define ATA_CMD_CFA_ERASE_SECTORS 0xC0 #define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 #define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87 #define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD #define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 #define ATA_CMD_CHECK_POWER_MODE1 0xE5 #define ATA_CMD_CHECK_POWER_MODE2 0x98 #define ATA_CMD_DEVICE_RESET 0x08 #define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 #define ATA_CMD_FLUSH_CACHE 0xE7 #define ATA_CMD_FORMAT_TRACK 0x50 #define ATA_CMD_IDENTIFY_DEVICE 0xEC #define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1 #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1 #define ATA_CMD_IDLE1 0xE3 #define ATA_CMD_IDLE2 0x97 #define ATA_CMD_IDLE_IMMEDIATE1 0xE1 #define ATA_CMD_IDLE_IMMEDIATE2 0x95 #define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91 #define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 #define ATA_CMD_NOP 0x00 #define ATA_CMD_PACKET 0xA0 #define ATA_CMD_READ_BUFFER 0xE4 #define ATA_CMD_READ_DMA 0xC8 #define ATA_CMD_READ_DMA_QUEUED 0xC7 #define ATA_CMD_READ_MULTIPLE 0xC4 #define ATA_CMD_READ_SECTORS 0x20 #define ATA_CMD_READ_VERIFY_SECTORS 0x40 #define ATA_CMD_RECALIBRATE 0x10 #define ATA_CMD_REQUEST_SENSE 0x03 #define ATA_CMD_SEEK 0x70 #define ATA_CMD_SET_FEATURES 0xEF #define ATA_CMD_SET_MULTIPLE_MODE 0xC6 #define ATA_CMD_SLEEP1 0xE6 #define ATA_CMD_SLEEP2 0x99 #define ATA_CMD_STANDBY1 0xE2 #define ATA_CMD_STANDBY2 0x96 #define ATA_CMD_STANDBY_IMMEDIATE1 0xE0 #define ATA_CMD_STANDBY_IMMEDIATE2 0x94 #define ATA_CMD_WRITE_BUFFER 0xE8 #define ATA_CMD_WRITE_DMA 0xCA #define ATA_CMD_WRITE_DMA_QUEUED 0xCC #define ATA_CMD_WRITE_MULTIPLE 0xC5 #define ATA_CMD_WRITE_SECTORS 0x30 #define ATA_CMD_WRITE_VERIFY 0x3C #define ATA_IFACE_NONE 0x00 #define ATA_IFACE_ISA 0x00 #define ATA_IFACE_PCI 0x01 #define ATA_TYPE_NONE 0x00 #define ATA_TYPE_UNKNOWN 0x01 #define ATA_TYPE_ATA 0x02 #define ATA_TYPE_ATAPI 0x03 #define ATA_DEVICE_NONE 0x00 #define ATA_DEVICE_HD 0xFF #define ATA_DEVICE_CDROM 0x05 #define ATA_MODE_NONE 0x00 #define ATA_MODE_PIO16 0x00 #define ATA_MODE_PIO32 0x01 #define ATA_MODE_ISADMA 0x02 #define ATA_MODE_PCIDMA 0x03 #define ATA_MODE_USEIRQ 0x10 #define ATA_TRANSLATION_NONE 0 #define ATA_TRANSLATION_LBA 1 #define ATA_TRANSLATION_LARGE 2 #define ATA_TRANSLATION_RECHS 3 #define ATA_DATA_NO 0x00 #define ATA_DATA_IN 0x01 #define ATA_DATA_OUT 0x02 // --------------------------------------------------------------------------- // ATA/ATAPI driver : initialization // --------------------------------------------------------------------------- void ata_init( ) { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit8u channel, device; // Channels info init. for (channel=0; channelata.channels[channel].iface,ATA_IFACE_NONE); write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0); write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0); write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0); } // Devices info init. for (device=0; deviceata.devices[device].type,ATA_TYPE_NONE); write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE); write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0); write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0); write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE); write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0); write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE); write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0); write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0); write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0); write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0); write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0); write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0); write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low,0L); write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high,0L); } // hdidmap and cdidmap init. for (device=0; deviceata.hdidmap[device],BX_MAX_ATA_DEVICES); write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES); } write_byte(ebda_seg,&EbdaData->ata.hdcount,0); write_byte(ebda_seg,&EbdaData->ata.cdcount,0); } #define TIMEOUT 0 #define BSY 1 #define NOT_BSY 2 #define NOT_BSY_DRQ 3 #define NOT_BSY_NOT_DRQ 4 #define NOT_BSY_RDY 5 #define IDE_TIMEOUT 32000u //32 seconds max for IDE ops Bit8u await_ide(); static Bit8u await_ide(when_done,base,timeout) Bit8u when_done; Bit16u base; Bit16u timeout; { Bit32u time=0,last=0; Bit8u status; Bit8u result; for(;;) { status = inb(base+ATA_CB_STAT); time++; if (when_done == BSY) result = status & ATA_CB_STAT_BSY; else if (when_done == NOT_BSY) result = !(status & ATA_CB_STAT_BSY); else if (when_done == NOT_BSY_DRQ) result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ); else if (when_done == NOT_BSY_NOT_DRQ) result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ); else if (when_done == NOT_BSY_RDY) result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY); else if (when_done == TIMEOUT) result = 0; if (result) return status; if (time>>16 != last) // mod 2048 each 16 ms { last = time >>16; BX_DEBUG_ATA("await_ide: (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout); } if (status & ATA_CB_STAT_ERR) { BX_DEBUG_ATA("await_ide: ERROR (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout); return status; } if ((timeout == 0) || ((time>>11) > timeout)) break; } BX_INFO("IDE time out\n"); return status; } // --------------------------------------------------------------------------- // ATA/ATAPI driver : device detection // --------------------------------------------------------------------------- void ata_detect( ) { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit8u hdcount, cdcount, device, type; Bit8u buffer[0x0200]; #if BX_MAX_ATA_INTERFACES > 0 write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA); write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0); write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0); write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14); #endif #if BX_MAX_ATA_INTERFACES > 1 write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA); write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170); write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370); write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15); #endif #if BX_MAX_ATA_INTERFACES > 2 write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA); write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8); write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0); write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12); #endif #if BX_MAX_ATA_INTERFACES > 3 write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA); write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168); write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360); write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11); #endif #if BX_MAX_ATA_INTERFACES > 4 #error Please fill the ATA interface informations #endif // Device detection hdcount=cdcount=0; for(device=0; deviceata.channels[channel].iobase1); iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2); // Disable interrupts outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); // Look for device outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0); outb(iobase1+ATA_CB_SC, 0x55); outb(iobase1+ATA_CB_SN, 0xaa); outb(iobase1+ATA_CB_SC, 0xaa); outb(iobase1+ATA_CB_SN, 0x55); outb(iobase1+ATA_CB_SC, 0x55); outb(iobase1+ATA_CB_SN, 0xaa); // If we found something sc = inb(iobase1+ATA_CB_SC); sn = inb(iobase1+ATA_CB_SN); if ( (sc == 0x55) && (sn == 0xaa) ) { write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN); // reset the channel ata_reset(device); // check for ATA or ATAPI outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0); sc = inb(iobase1+ATA_CB_SC); sn = inb(iobase1+ATA_CB_SN); if ((sc==0x01) && (sn==0x01)) { cl = inb(iobase1+ATA_CB_CL); ch = inb(iobase1+ATA_CB_CH); st = inb(iobase1+ATA_CB_STAT); if ((cl==0x14) && (ch==0xeb)) { write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI); } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) { write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA); } else if ((cl==0xff) && (ch==0xff)) { write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE); } } } type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type); // Now we send a IDENTIFY command to ATA device if(type == ATA_TYPE_ATA) { Bit32u sectors_low, sectors_high; Bit16u cylinders, heads, spt, blksize; Bit8u translation, removable, mode; // default mode to PIO16 mode = ATA_MODE_PIO16; //Temporary values to do the transfer write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD); write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16); if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) !=0 ) BX_PANIC("ata-detect: Failed to detect ATA device\n"); removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0; #ifndef NO_PIO32 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16; #endif blksize = read_word(get_SS(),buffer+10); cylinders = read_word(get_SS(),buffer+(1*2)); // word 1 heads = read_word(get_SS(),buffer+(3*2)); // word 3 spt = read_word(get_SS(),buffer+(6*2)); // word 6 if (read_word(get_SS(),buffer+(83*2)) & (1 << 10)) { // word 83 - lba48 support sectors_low = read_dword(get_SS(),buffer+(100*2)); // word 100 and word 101 sectors_high = read_dword(get_SS(),buffer+(102*2)); // word 102 and word 103 } else { sectors_low = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61 sectors_high = 0; } write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD); write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable); write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode); write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize); write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads); write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders); write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt); write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors_low); write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high, sectors_high); BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt); translation = inb_cmos(0x39 + channel/2); for (shift=device%4; shift>0; shift--) translation >>= 2; translation &= 0x03; write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation); switch (translation) { case ATA_TRANSLATION_NONE: BX_INFO("none"); break; case ATA_TRANSLATION_LBA: BX_INFO("lba"); break; case ATA_TRANSLATION_LARGE: BX_INFO("large"); break; case ATA_TRANSLATION_RECHS: BX_INFO("r-echs"); break; } switch (translation) { case ATA_TRANSLATION_NONE: break; case ATA_TRANSLATION_LBA: spt = 63; sectors_low /= 63; heads = sectors_low / 1024; if (heads>128) heads = 255; else if (heads>64) heads = 128; else if (heads>32) heads = 64; else if (heads>16) heads = 32; else heads=16; cylinders = sectors_low / heads; break; case ATA_TRANSLATION_RECHS: // Take care not to overflow if (heads==16) { if(cylinders>61439) cylinders=61439; heads=15; cylinders = (Bit16u)((Bit32u)(cylinders)*16/15); } // then go through the large bitshift process case ATA_TRANSLATION_LARGE: while(cylinders > 1024) { cylinders >>= 1; heads <<= 1; // If we max out the head count if (heads > 127) break; } break; } // clip to 1024 cylinders in lchs if (cylinders > 1024) cylinders=1024; BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt); write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads); write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders); write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt); // fill hdidmap write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device); hdcount++; } // Now we send a IDENTIFY command to ATAPI device if(type == ATA_TYPE_ATAPI) { Bit8u type, removable, mode; Bit16u blksize; // default mode to PIO16 mode = ATA_MODE_PIO16; //Temporary values to do the transfer write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM); write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16); if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) != 0) BX_PANIC("ata-detect: Failed to detect ATAPI device\n"); type = read_byte(get_SS(),buffer+1) & 0x1f; removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0; #ifndef NO_PIO32 mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16; #endif blksize = 2048; write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type); write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable); write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode); write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize); // fill cdidmap write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device); cdcount++; } { Bit32u sizeinmb; Bit16u ataversion; Bit8u c, i, version, model[41]; switch (type) { case ATA_TYPE_ATA: sizeinmb = (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high) << 21) | (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low) >> 11); case ATA_TYPE_ATAPI: // Read ATA/ATAPI version ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160); for(version=15;version>0;version--) { if((ataversion&(1<0;i--){ if(read_byte(get_SS(),model+i)==0x20) write_byte(get_SS(),model+i,0x00); else break; } if (i>36) { write_byte(get_SS(),model+36,0x00); for(i=35;i>32;i--){ write_byte(get_SS(),model+i,0x2E); } } break; } switch (type) { case ATA_TYPE_ATA: printf("ata%d %s: ",channel,slave?" slave":"master"); i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c); if (sizeinmb < (1UL<<16)) printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb); else printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10)); break; case ATA_TYPE_ATAPI: printf("ata%d %s: ",channel,slave?" slave":"master"); i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c); if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM) printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version); else printf(" ATAPI-%d Device\n",version); break; case ATA_TYPE_UNKNOWN: printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master"); break; } } } // Store the devices counts write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount); write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount); write_byte(0x40,0x75, hdcount); printf("\n"); // FIXME : should use bios=cmos|auto|disable bits // FIXME : should know about translation bits // FIXME : move hard_drive_post here } // --------------------------------------------------------------------------- // ATA/ATAPI driver : software reset // --------------------------------------------------------------------------- // ATA-3 // 8.2.1 Software reset - Device 0 void ata_reset(device) Bit16u device; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u iobase1, iobase2; Bit8u channel, slave, sn, sc; Bit8u type; Bit16u max; channel = device / 2; slave = device % 2; iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); // Reset // 8.2.1 (a) -- set SRST in DC outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST); // 8.2.1 (b) -- wait outb(0x80, 0x00); // 8.2.1 (f) -- clear SRST outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type); if (type != ATA_TYPE_NONE) { // 8.2.1 (g) -- check for sc==sn==0x01 // select device outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0); sc = inb(iobase1+ATA_CB_SC); sn = inb(iobase1+ATA_CB_SN); if ( (sc==0x01) && (sn==0x01) ) { if (type == ATA_TYPE_ATA) //ATA await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT); else //ATAPI await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); } // 8.2.1 (h) -- wait for not BSY await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); } // Enable interrupts outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15); } // --------------------------------------------------------------------------- // ATA/ATAPI driver : execute a non data command // --------------------------------------------------------------------------- Bit16u ata_cmd_non_data() {return 0;} // --------------------------------------------------------------------------- // ATA/ATAPI driver : execute a data-in command // --------------------------------------------------------------------------- // returns // 0 : no error // 1 : BUSY bit set // 2 : read error // 3 : expected DRQ=1 // 4 : no sectors left to read/verify // 5 : more sectors to read/verify // 6 : no sectors left to write // 7 : more sectors to write Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset) Bit16u device, command, count, cylinder, head, sector, segment, offset; Bit32u lba_low, lba_high; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u iobase1, iobase2, blksize; Bit8u channel, slave; Bit8u status, current, mode; channel = device / 2; slave = device % 2; iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize); if (mode == ATA_MODE_PIO32) blksize>>=2; else blksize>>=1; // Reset count of transferred data write_word(ebda_seg, &EbdaData->ata.trsfsectors,0); write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L); current = 0; status = inb(iobase1 + ATA_CB_STAT); if (status & ATA_CB_STAT_BSY) return 1; outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); // sector will be 0 only on lba access. Convert to lba-chs if (sector == 0) { if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) { outb(iobase1 + ATA_CB_FR, 0x00); outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff); outb(iobase1 + ATA_CB_SN, lba_low >> 24); outb(iobase1 + ATA_CB_CL, lba_high & 0xff); outb(iobase1 + ATA_CB_CH, lba_high >> 8); command |= 0x04; count &= (1UL << 8) - 1; lba_low &= (1UL << 24) - 1; } sector = (Bit16u) (lba_low & 0x000000ffL); cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL); head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA; } outb(iobase1 + ATA_CB_FR, 0x00); outb(iobase1 + ATA_CB_SC, count); outb(iobase1 + ATA_CB_SN, sector); outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff); outb(iobase1 + ATA_CB_CH, cylinder >> 8); outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head ); outb(iobase1 + ATA_CB_CMD, command); status = await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT); if (status & ATA_CB_STAT_ERR) { BX_DEBUG_ATA("ata_cmd_data_in : read error\n"); return 2; } else if ( !(status & ATA_CB_STAT_DRQ) ) { BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status); return 3; } // FIXME : move seg/off translation here ASM_START sti ;; enable higher priority interrupts ASM_END while (1) { ASM_START push bp mov bp, sp mov di, _ata_cmd_data_in.offset + 2[bp] mov ax, _ata_cmd_data_in.segment + 2[bp] mov cx, _ata_cmd_data_in.blksize + 2[bp] ;; adjust if there will be an overrun. 2K max sector size cmp di, #0xf800 ;; jbe ata_in_no_adjust ata_in_adjust: sub di, #0x0800 ;; sub 2 kbytes from offset add ax, #0x0080 ;; add 2 Kbytes to segment ata_in_no_adjust: mov es, ax ;; segment in es mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port mov ah, _ata_cmd_data_in.mode + 2[bp] cmp ah, #ATA_MODE_PIO32 je ata_in_32 ata_in_16: rep insw ;; CX words transfered from port(DX) to ES:[DI] jmp ata_in_done ata_in_32: rep insd ;; CX dwords transfered from port(DX) to ES:[DI] ata_in_done: mov _ata_cmd_data_in.offset + 2[bp], di mov _ata_cmd_data_in.segment + 2[bp], es pop bp ASM_END current++; write_word(ebda_seg, &EbdaData->ata.trsfsectors,current); count--; status = await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); if (count == 0) { if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) != ATA_CB_STAT_RDY ) { BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status); return 4; } break; } else { if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) { BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status); return 5; } continue; } } // Enable interrupts outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15); return 0; } // --------------------------------------------------------------------------- // ATA/ATAPI driver : execute a data-out command // --------------------------------------------------------------------------- // returns // 0 : no error // 1 : BUSY bit set // 2 : read error // 3 : expected DRQ=1 // 4 : no sectors left to read/verify // 5 : more sectors to read/verify // 6 : no sectors left to write // 7 : more sectors to write Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset) Bit16u device, command, count, cylinder, head, sector, segment, offset; Bit32u lba_low, lba_high; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u iobase1, iobase2, blksize; Bit8u channel, slave; Bit8u status, current, mode; channel = device / 2; slave = device % 2; iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize); if (mode == ATA_MODE_PIO32) blksize>>=2; else blksize>>=1; // Reset count of transferred data write_word(ebda_seg, &EbdaData->ata.trsfsectors,0); write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L); current = 0; status = inb(iobase1 + ATA_CB_STAT); if (status & ATA_CB_STAT_BSY) return 1; outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); // sector will be 0 only on lba access. Convert to lba-chs if (sector == 0) { if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) { outb(iobase1 + ATA_CB_FR, 0x00); outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff); outb(iobase1 + ATA_CB_SN, lba_low >> 24); outb(iobase1 + ATA_CB_CL, lba_high & 0xff); outb(iobase1 + ATA_CB_CH, lba_high >> 8); command |= 0x04; count &= (1UL << 8) - 1; lba_low &= (1UL << 24) - 1; } sector = (Bit16u) (lba_low & 0x000000ffL); cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL); head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA; } outb(iobase1 + ATA_CB_FR, 0x00); outb(iobase1 + ATA_CB_SC, count); outb(iobase1 + ATA_CB_SN, sector); outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff); outb(iobase1 + ATA_CB_CH, cylinder >> 8); outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head ); outb(iobase1 + ATA_CB_CMD, command); status = await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT); if (status & ATA_CB_STAT_ERR) { BX_DEBUG_ATA("ata_cmd_data_out : read error\n"); return 2; } else if ( !(status & ATA_CB_STAT_DRQ) ) { BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status); return 3; } // FIXME : move seg/off translation here ASM_START sti ;; enable higher priority interrupts ASM_END while (1) { ASM_START push bp mov bp, sp mov si, _ata_cmd_data_out.offset + 2[bp] mov ax, _ata_cmd_data_out.segment + 2[bp] mov cx, _ata_cmd_data_out.blksize + 2[bp] ;; adjust if there will be an overrun. 2K max sector size cmp si, #0xf800 ;; jbe ata_out_no_adjust ata_out_adjust: sub si, #0x0800 ;; sub 2 kbytes from offset add ax, #0x0080 ;; add 2 Kbytes to segment ata_out_no_adjust: mov es, ax ;; segment in es mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port mov ah, _ata_cmd_data_out.mode + 2[bp] cmp ah, #ATA_MODE_PIO32 je ata_out_32 ata_out_16: seg ES rep outsw ;; CX words transfered from port(DX) to ES:[SI] jmp ata_out_done ata_out_32: seg ES rep outsd ;; CX dwords transfered from port(DX) to ES:[SI] ata_out_done: mov _ata_cmd_data_out.offset + 2[bp], si mov _ata_cmd_data_out.segment + 2[bp], es pop bp ASM_END current++; write_word(ebda_seg, &EbdaData->ata.trsfsectors,current); count--; status = inb(iobase1 + ATA_CB_STAT); if (count == 0) { if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) != ATA_CB_STAT_RDY ) { BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status); return 6; } break; } else { if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) { BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status); return 7; } continue; } } // Enable interrupts outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15); return 0; } // --------------------------------------------------------------------------- // ATA/ATAPI driver : execute a packet command // --------------------------------------------------------------------------- // returns // 0 : no error // 1 : error in parameters // 2 : BUSY bit set // 3 : error // 4 : not ready Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff) Bit8u cmdlen,inout; Bit16u device,cmdseg, cmdoff, bufseg, bufoff; Bit16u header; Bit32u length; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u iobase1, iobase2; Bit16u lcount, lbefore, lafter, count; Bit8u channel, slave; Bit8u status, mode, lmode; Bit32u total, transfer; channel = device / 2; slave = device % 2; // Data out is not supported yet if (inout == ATA_DATA_OUT) { BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n"); return 1; } // The header length must be even if (header & 1) { BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header); return 1; } iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); transfer= 0L; if (cmdlen < 12) cmdlen=12; if (cmdlen > 12) cmdlen=16; cmdlen>>=1; // Reset count of transferred data write_word(ebda_seg, &EbdaData->ata.trsfsectors,0); write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L); status = inb(iobase1 + ATA_CB_STAT); if (status & ATA_CB_STAT_BSY) return 2; outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN); outb(iobase1 + ATA_CB_FR, 0x00); outb(iobase1 + ATA_CB_SC, 0x00); outb(iobase1 + ATA_CB_SN, 0x00); outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff); outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8); outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0); outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET); // Device should ok to receive command status = await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT); if (status & ATA_CB_STAT_ERR) { BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status); return 3; } else if ( !(status & ATA_CB_STAT_DRQ) ) { BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status); return 4; } // Normalize address cmdseg += (cmdoff / 16); cmdoff %= 16; // Send command to device ASM_START sti ;; enable higher priority interrupts push bp mov bp, sp mov si, _ata_cmd_packet.cmdoff + 2[bp] mov ax, _ata_cmd_packet.cmdseg + 2[bp] mov cx, _ata_cmd_packet.cmdlen + 2[bp] mov es, ax ;; segment in es mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port seg ES rep outsw ;; CX words transfered from port(DX) to ES:[SI] pop bp ASM_END if (inout == ATA_DATA_NO) { status = await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); } else { Bit16u loops = 0; Bit8u sc; while (1) { if (loops == 0) {//first time through status = inb(iobase2 + ATA_CB_ASTAT); status = await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT); } else status = await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); loops++; sc = inb(iobase1 + ATA_CB_SC); // Check if command completed if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) && ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY)) break; if (status & ATA_CB_STAT_ERR) { BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status); return 3; } // Normalize address bufseg += (bufoff / 16); bufoff %= 16; // Get the byte count lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL); // adjust to read what we want if(header>lcount) { lbefore=lcount; header-=lcount; lcount=0; } else { lbefore=header; header=0; lcount-=lbefore; } if(lcount>length) { lafter=lcount-length; lcount=length; length=0; } else { lafter=0; length-=lcount; } // Save byte count count = lcount; BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter); BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff); // If counts not dividable by 4, use 16bits mode lmode = mode; if (lbefore & 0x03) lmode=ATA_MODE_PIO16; if (lcount & 0x03) lmode=ATA_MODE_PIO16; if (lafter & 0x03) lmode=ATA_MODE_PIO16; // adds an extra byte if count are odd. before is always even if (lcount & 0x01) { lcount+=1; if ((lafter > 0) && (lafter & 0x01)) { lafter-=1; } } if (lmode == ATA_MODE_PIO32) { lcount>>=2; lbefore>>=2; lafter>>=2; } else { lcount>>=1; lbefore>>=1; lafter>>=1; } ; // FIXME bcc bug ASM_START push bp mov bp, sp mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port mov cx, _ata_cmd_packet.lbefore + 2[bp] jcxz ata_packet_no_before mov ah, _ata_cmd_packet.lmode + 2[bp] cmp ah, #ATA_MODE_PIO32 je ata_packet_in_before_32 ata_packet_in_before_16: in ax, dx loop ata_packet_in_before_16 jmp ata_packet_no_before ata_packet_in_before_32: push eax ata_packet_in_before_32_loop: in eax, dx loop ata_packet_in_before_32_loop pop eax ata_packet_no_before: mov cx, _ata_cmd_packet.lcount + 2[bp] jcxz ata_packet_after mov di, _ata_cmd_packet.bufoff + 2[bp] mov ax, _ata_cmd_packet.bufseg + 2[bp] mov es, ax mov ah, _ata_cmd_packet.lmode + 2[bp] cmp ah, #ATA_MODE_PIO32 je ata_packet_in_32 ata_packet_in_16: rep insw ;; CX words transfered tp port(DX) to ES:[DI] jmp ata_packet_after ata_packet_in_32: rep insd ;; CX dwords transfered to port(DX) to ES:[DI] ata_packet_after: mov cx, _ata_cmd_packet.lafter + 2[bp] jcxz ata_packet_done mov ah, _ata_cmd_packet.lmode + 2[bp] cmp ah, #ATA_MODE_PIO32 je ata_packet_in_after_32 ata_packet_in_after_16: in ax, dx loop ata_packet_in_after_16 jmp ata_packet_done ata_packet_in_after_32: push eax ata_packet_in_after_32_loop: in eax, dx loop ata_packet_in_after_32_loop pop eax ata_packet_done: pop bp ASM_END // Compute new buffer address bufoff += count; // Save transferred bytes count transfer += count; write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer); } } // Final check, device must be ready if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) != ATA_CB_STAT_RDY ) { BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status); return 4; } // Enable interrupts outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15); return 0; } // --------------------------------------------------------------------------- // End of ATA/ATAPI Driver // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Start of ATA/ATAPI generic functions // --------------------------------------------------------------------------- Bit16u atapi_get_sense(device, seg, asc, ascq) Bit16u device; { Bit8u atacmd[12]; Bit8u buffer[18]; Bit8u i; memsetb(get_SS(),atacmd,0,12); // Request SENSE atacmd[0]=ATA_CMD_REQUEST_SENSE; atacmd[4]=sizeof(buffer); if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 18L, ATA_DATA_IN, get_SS(), buffer) != 0) return 0x0002; write_byte(seg,asc,buffer[12]); write_byte(seg,ascq,buffer[13]); return 0; } Bit16u atapi_is_ready(device) Bit16u device; { Bit8u packet[12]; Bit8u buf[8]; Bit32u block_len; Bit32u sectors; Bit32u timeout; //measured in ms Bit32u time; Bit8u asc, ascq; Bit8u in_progress; Bit16u ebda_seg = read_word(0x0040,0x000E); if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) { printf("not implemented for non-ATAPI device\n"); return -1; } BX_DEBUG_ATA("ata_detect_medium: begin\n"); memsetb(get_SS(),packet, 0, sizeof packet); packet[0] = 0x25; /* READ CAPACITY */ /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT * is reported by the device. If the device reports "IN PROGRESS", * 30 seconds is added. */ timeout = 5000; time = 0; in_progress = 0; while (time < timeout) { if (ata_cmd_packet(device, sizeof(packet), get_SS(), packet, 0, 8L, ATA_DATA_IN, get_SS(), buf) == 0) goto ok; if (atapi_get_sense(device, get_SS(), &asc, &ascq) == 0) { if (asc == 0x3a) { /* MEDIUM NOT PRESENT */ BX_DEBUG_ATA("Device reports MEDIUM NOT PRESENT\n"); return -1; } if (asc == 0x04 && ascq == 0x01 && !in_progress) { /* IN PROGRESS OF BECOMING READY */ printf("Waiting for device to detect medium... "); /* Allow 30 seconds more */ timeout = 30000; in_progress = 1; } } time += 100; } BX_DEBUG_ATA("read capacity failed\n"); return -1; ok: block_len = (Bit32u) buf[4] << 24 | (Bit32u) buf[5] << 16 | (Bit32u) buf[6] << 8 | (Bit32u) buf[7] << 0; BX_DEBUG_ATA("block_len=%u\n", block_len); if (block_len!= 2048 && block_len!= 512) { printf("Unsupported sector size %u\n", block_len); return -1; } write_dword(ebda_seg,&EbdaData->ata.devices[device].blksize, block_len); sectors = (Bit32u) buf[0] << 24 | (Bit32u) buf[1] << 16 | (Bit32u) buf[2] << 8 | (Bit32u) buf[3] << 0; BX_DEBUG_ATA("sectors=%u\n", sectors); if (block_len == 2048) sectors <<= 2; /* # of sectors in 512-byte "soft" sector */ if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low)) printf("%dMB medium detected\n", sectors>>(20-9)); write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors); return 0; } Bit16u atapi_is_cdrom(device) Bit8u device; { Bit16u ebda_seg=read_word(0x0040,0x000E); if (device >= BX_MAX_ATA_DEVICES) return 0; if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) return 0; if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM) return 0; return 1; } // --------------------------------------------------------------------------- // End of ATA/ATAPI generic functions // --------------------------------------------------------------------------- #endif // BX_USE_ATADRV #if BX_ELTORITO_BOOT // --------------------------------------------------------------------------- // Start of El-Torito boot functions // --------------------------------------------------------------------------- void cdemu_init() { Bit16u ebda_seg=read_word(0x0040,0x000E); // the only important data is this one for now write_byte(ebda_seg,&EbdaData->cdemu.active,0x00); } Bit8u cdemu_isactive() { Bit16u ebda_seg=read_word(0x0040,0x000E); return(read_byte(ebda_seg,&EbdaData->cdemu.active)); } Bit8u cdemu_emulated_drive() { Bit16u ebda_seg=read_word(0x0040,0x000E); return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)); } static char isotag[6]="CD001"; static char eltorito[24]="EL TORITO SPECIFICATION"; // // Returns ah: emulated drive, al: error code // Bit16u cdrom_boot() { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit8u atacmd[12], buffer[2048]; Bit32u lba; Bit16u boot_segment, nbsectors, i, error; Bit8u device; // Find out the first cdrom for (device=0; device= BX_MAX_ATA_DEVICES) return 2; if(error = atapi_is_ready(device) != 0) BX_INFO("ata_is_ready returned %d\n",error); // Read the Boot Record Volume Descriptor memsetb(get_SS(),atacmd,0,12); atacmd[0]=0x28; // READ command atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors atacmd[8]=(0x01 & 0x00ff); // Sectors atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA atacmd[3]=(0x11 & 0x00ff0000) >> 16; atacmd[4]=(0x11 & 0x0000ff00) >> 8; atacmd[5]=(0x11 & 0x000000ff); if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0) return 3; // Validity checks if(buffer[0]!=0)return 4; for(i=0;i<5;i++){ if(read_byte(get_SS(),&buffer[1+i])!=read_byte(0xf000,&isotag[i]))return 5; } for(i=0;i<23;i++) if(read_byte(get_SS(),&buffer[7+i])!=read_byte(0xf000,&eltorito[i]))return 6; // ok, now we calculate the Boot catalog address lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47]; // And we read the Boot Catalog memsetb(get_SS(),atacmd,0,12); atacmd[0]=0x28; // READ command atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors atacmd[8]=(0x01 & 0x00ff); // Sectors atacmd[2]=(lba & 0xff000000) >> 24; // LBA atacmd[3]=(lba & 0x00ff0000) >> 16; atacmd[4]=(lba & 0x0000ff00) >> 8; atacmd[5]=(lba & 0x000000ff); if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0) return 7; // Validation entry if(buffer[0x00]!=0x01)return 8; // Header if(buffer[0x01]!=0x00)return 9; // Platform if(buffer[0x1E]!=0x55)return 10; // key 1 if(buffer[0x1F]!=0xAA)return 10; // key 2 // Initial/Default Entry if(buffer[0x20]!=0x88)return 11; // Bootable #if BX_TCGBIOS /* specs: 8.2.3 step 5 and 8.2.5.6, measure El Torito boot catalog */ /* measure 2048 bytes (one sector) */ tcpa_add_bootdevice((Bit32u)1L, (Bit32u)0L); /* bootcd = 1 */ tcpa_ipl((Bit32u)2L,(Bit32u)get_SS(),(Bit32u)buffer,(Bit32u)2048L); #endif write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]); if(buffer[0x21]==0){ // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0. // Win2000 cd boot needs to know it booted from cd write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0); } else if(buffer[0x21]<4) write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00); else write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80); write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2); write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2); boot_segment=buffer[0x23]*0x100+buffer[0x22]; if(boot_segment==0x0000)boot_segment=0x07C0; write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment); write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000); nbsectors=buffer[0x27]*0x100+buffer[0x26]; write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors); lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28]; write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba); // And we read the image in memory memsetb(get_SS(),atacmd,0,12); atacmd[0]=0x28; // READ command atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors atacmd[2]=(lba & 0xff000000) >> 24; // LBA atacmd[3]=(lba & 0x00ff0000) >> 16; atacmd[4]=(lba & 0x0000ff00) >> 8; atacmd[5]=(lba & 0x000000ff); if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0) return 12; #if BX_TCGBIOS /* specs: 8.2.3 step 4 and 8.2.5.6, measure El Torito boot image */ /* measure 1st 512 bytes */ tcpa_ipl((Bit32u)1L,(Bit32u)boot_segment,(Bit32u)0L,(Bit32u)512L); #endif // Remember the media type switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) { case 0x01: // 1.2M floppy write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15); write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80); write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2); break; case 0x02: // 1.44M floppy write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18); write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80); write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2); break; case 0x03: // 2.88M floppy write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36); write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80); write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2); break; case 0x04: // Harddrive write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f); write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders, (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1); write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1); break; } if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) { // Increase bios installed hardware number of devices if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00) write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41); else write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1); } // everything is ok, so from now on, the emulation is active if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) write_byte(ebda_seg,&EbdaData->cdemu.active,0x01); // return the boot drive + no error return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0; } // --------------------------------------------------------------------------- // End of El-Torito boot functions // --------------------------------------------------------------------------- #endif // BX_ELTORITO_BOOT void int14_function(regs, ds, iret_addr) pusha_regs_t regs; // regs pushed from PUSHA instruction Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call { Bit16u addr,timer,val16; Bit8u timeout; ASM_START sti ASM_END addr = read_word(0x0040, (regs.u.r16.dx << 1)); timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx); if ((regs.u.r16.dx < 4) && (addr > 0)) { switch (regs.u.r8.ah) { case 0: outb(addr+3, inb(addr+3) | 0x80); if (regs.u.r8.al & 0xE0 == 0) { outb(addr, 0x17); outb(addr+1, 0x04); } else { val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5); outb(addr, val16 & 0xFF); outb(addr+1, val16 >> 8); } outb(addr+3, regs.u.r8.al & 0x1F); regs.u.r8.ah = inb(addr+5); regs.u.r8.al = inb(addr+6); ClearCF(iret_addr.flags); break; case 1: timer = read_word(0x0040, 0x006C); while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) { val16 = read_word(0x0040, 0x006C); if (val16 != timer) { timer = val16; timeout--; } } if (timeout) outb(addr, regs.u.r8.al); regs.u.r8.ah = inb(addr+5); if (!timeout) regs.u.r8.ah |= 0x80; ClearCF(iret_addr.flags); break; case 2: timer = read_word(0x0040, 0x006C); while (((inb(addr+5) & 0x01) == 0) && (timeout)) { val16 = read_word(0x0040, 0x006C); if (val16 != timer) { timer = val16; timeout--; } } if (timeout) { regs.u.r8.ah = 0; regs.u.r8.al = inb(addr); } else { regs.u.r8.ah = inb(addr+5); } ClearCF(iret_addr.flags); break; case 3: regs.u.r8.ah = inb(addr+5); regs.u.r8.al = inb(addr+6); ClearCF(iret_addr.flags); break; default: SetCF(iret_addr.flags); // Unsupported } } else { SetCF(iret_addr.flags); // Unsupported } } void int15_function(regs, ES, DS, FLAGS) pusha_regs_t regs; // REGS pushed via pusha Bit16u ES, DS, FLAGS; { Bit16u ebda_seg=read_word(0x0040,0x000E); bx_bool prev_a20_enable; Bit16u base15_00; Bit8u base23_16; Bit16u ss; Bit16u CX,DX; Bit16u bRegister; Bit8u irqDisable; BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax); switch (regs.u.r8.ah) { case 0x24: /* A20 Control */ switch (regs.u.r8.al) { case 0x00: set_enable_a20(0); CLEAR_CF(); regs.u.r8.ah = 0; break; case 0x01: set_enable_a20(1); CLEAR_CF(); regs.u.r8.ah = 0; break; case 0x02: regs.u.r8.al = (inb(0x92) >> 1) & 0x01; CLEAR_CF(); regs.u.r8.ah = 0; break; case 0x03: CLEAR_CF(); regs.u.r8.ah = 0; regs.u.r16.bx = 3; break; default: BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al); SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; } break; case 0x41: SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; break; case 0x4f: /* keyboard intercept */ #if BX_CPU < 2 regs.u.r8.ah = UNSUPPORTED_FUNCTION; #else // nop #endif SET_CF(); break; case 0x52: // removable media eject CLEAR_CF(); regs.u.r8.ah = 0; // "ok ejection may proceed" break; case 0x83: { if( regs.u.r8.al == 0 ) { // Set Interval requested. if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) { // Interval not already set. write_byte( 0x40, 0xA0, 1 ); // Set status byte. write_word( 0x40, 0x98, ES ); // Byte location, segment write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay. CLEAR_CF( ); irqDisable = inb( 0xA1 ); outb( 0xA1, irqDisable & 0xFE ); bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through. outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer } else { // Interval already set. BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" ); SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; } } else if( regs.u.r8.al == 1 ) { // Clear Interval requested write_byte( 0x40, 0xA0, 0 ); // Clear status byte CLEAR_CF( ); bRegister = inb_cmos( 0xB ); outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer } else { BX_DEBUG_INT15("int15: Func 83h, failed.\n" ); SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; regs.u.r8.al--; } break; } case 0x87: #if BX_CPU < 3 # error "Int15 function 87h not supported on < 80386" #endif // +++ should probably have descriptor checks // +++ should have exception handlers // turn off interrupts ASM_START cli ASM_END prev_a20_enable = set_enable_a20(1); // enable A20 line // 128K max of transfer on 386+ ??? // source == destination ??? // ES:SI points to descriptor table // offset use initially comments // ============================================== // 00..07 Unused zeros Null descriptor // 08..0f GDT zeros filled in by BIOS // 10..17 source ssssssss source of data // 18..1f dest dddddddd destination of data // 20..27 CS zeros filled in by BIOS // 28..2f SS zeros filled in by BIOS //es:si //eeee0 //0ssss //----- // check for access rights of source & dest here // Initialize GDT descriptor base15_00 = (ES << 4) + regs.u.r16.si; base23_16 = ES >> 12; if (base15_00 < (ES<<4)) base23_16++; write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00 write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16 write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16 // Initialize CS descriptor write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00 write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16 write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16 // Initialize SS descriptor ss = get_SS(); base15_00 = ss << 4; base23_16 = ss >> 12; write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00 write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16 write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16 CX = regs.u.r16.cx; ASM_START // Compile generates locals offset info relative to SP. // Get CX (word count) from stack. mov bx, sp SEG SS mov cx, _int15_function.CX [bx] // since we need to set SS:SP, save them to the BDA // for future restore push eax xor eax, eax mov ds, ax mov 0x0469, ss mov 0x0467, sp SEG ES lgdt [si + 0x08] SEG CS lidt [pmode_IDT_info] ;; perhaps do something with IDT here ;; set PE bit in CR0 mov eax, cr0 or al, #0x01 mov cr0, eax ;; far jump to flush CPU queue after transition to protected mode JMP_AP(0x0020, protected_mode) protected_mode: ;; GDT points to valid descriptor table, now load SS, DS, ES mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00 mov ss, ax mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00 mov ds, ax mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00 mov es, ax xor si, si xor di, di cld rep movsw ;; move CX words from DS:SI to ES:DI ;; make sure DS and ES limits are 64KB mov ax, #0x28 mov ds, ax mov es, ax ;; reset PG bit in CR0 ??? mov eax, cr0 and al, #0xFE mov cr0, eax ;; far jump to flush CPU queue after transition to real mode JMP_AP(0xf000, real_mode) real_mode: ;; restore IDT to normal real-mode defaults SEG CS lidt [rmode_IDT_info] // restore SS:SP from the BDA xor ax, ax mov ds, ax mov ss, 0x0469 mov sp, 0x0467 pop eax ASM_END set_enable_a20(prev_a20_enable); // turn back on interrupts ASM_START sti ASM_END regs.u.r8.ah = 0; CLEAR_CF(); break; case 0x88: // Get the amount of extended memory (above 1M) #if BX_CPU < 2 regs.u.r8.ah = UNSUPPORTED_FUNCTION; SET_CF(); #else regs.u.r8.al = inb_cmos(0x30); regs.u.r8.ah = inb_cmos(0x31); // According to Ralf Brown's interrupt the limit should be 15M, // but real machines mostly return max. 63M. if(regs.u.r16.ax > 0xffc0) regs.u.r16.ax = 0xffc0; CLEAR_CF(); #endif break; case 0x90: /* Device busy interrupt. Called by Int 16h when no key available */ break; case 0x91: /* Interrupt complete. Called by Int 16h when key becomes available */ break; case 0xbf: BX_INFO("*** int 15h function AH=bf not yet supported!\n"); SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; break; case 0xC0: #if 0 SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; break; #endif CLEAR_CF(); regs.u.r8.ah = 0; regs.u.r16.bx = BIOS_CONFIG_TABLE; ES = 0xF000; break; case 0xc1: ES = ebda_seg; CLEAR_CF(); break; case 0xd8: bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n"); SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; break; default: BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n", (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx); SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; break; } } #if BX_USE_PS2_MOUSE void int15_function_mouse(regs, ES, DS, FLAGS) pusha_regs_t regs; // REGS pushed via pusha Bit16u ES, DS, FLAGS; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit8u mouse_flags_1, mouse_flags_2; Bit16u mouse_driver_seg; Bit16u mouse_driver_offset; Bit8u comm_byte, prev_command_byte; Bit8u ret, mouse_data1, mouse_data2, mouse_data3; BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax); switch (regs.u.r8.ah) { case 0xC2: // Return Codes status in AH // ========================= // 00: success // 01: invalid subfunction (AL > 7) // 02: invalid input value (out of allowable range) // 03: interface error // 04: resend command received from mouse controller, // device driver should attempt command again // 05: cannot enable mouse, since no far call has been installed // 80/86: mouse service not implemented switch (regs.u.r8.al) { case 0: // Disable/Enable Mouse BX_DEBUG_INT15("case 0:\n"); switch (regs.u.r8.bh) { case 0: // Disable Mouse BX_DEBUG_INT15("case 0: disable mouse\n"); inhibit_mouse_int_and_events(); // disable IRQ12 and packets ret = send_to_mouse_ctrl(0xF5); // disable mouse command if (ret == 0) { ret = get_mouse_data(&mouse_data1); if ( (ret == 0) || (mouse_data1 == 0xFA) ) { CLEAR_CF(); regs.u.r8.ah = 0; return; } } // error SET_CF(); regs.u.r8.ah = ret; return; break; case 1: // Enable Mouse BX_DEBUG_INT15("case 1: enable mouse\n"); mouse_flags_2 = read_byte(ebda_seg, 0x0027); if ( (mouse_flags_2 & 0x80) == 0 ) { BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n"); SET_CF(); // error regs.u.r8.ah = 5; // no far call installed return; } inhibit_mouse_int_and_events(); // disable IRQ12 and packets ret = send_to_mouse_ctrl(0xF4); // enable mouse command if (ret == 0) { ret = get_mouse_data(&mouse_data1); if ( (ret == 0) && (mouse_data1 == 0xFA) ) { enable_mouse_int_and_events(); // turn IRQ12 and packet generation on CLEAR_CF(); regs.u.r8.ah = 0; return; } } SET_CF(); regs.u.r8.ah = ret; return; default: // invalid subfunction BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh); SET_CF(); // error regs.u.r8.ah = 1; // invalid subfunction return; } break; case 1: // Reset Mouse case 5: // Initialize Mouse BX_DEBUG_INT15("case 1 or 5:\n"); if (regs.u.r8.al == 5) { if (regs.u.r8.bh != 3) { SET_CF(); regs.u.r8.ah = 0x02; // invalid input return; } mouse_flags_2 = read_byte(ebda_seg, 0x0027); mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh; mouse_flags_1 = 0x00; write_byte(ebda_seg, 0x0026, mouse_flags_1); write_byte(ebda_seg, 0x0027, mouse_flags_2); } inhibit_mouse_int_and_events(); // disable IRQ12 and packets ret = send_to_mouse_ctrl(0xFF); // reset mouse command if (ret == 0) { ret = get_mouse_data(&mouse_data3); // if no mouse attached, it will return RESEND if (mouse_data3 == 0xfe) { SET_CF(); return; } if (mouse_data3 != 0xfa) BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3); if ( ret == 0 ) { ret = get_mouse_data(&mouse_data1); if ( ret == 0 ) { ret = get_mouse_data(&mouse_data2); if ( ret == 0 ) { // turn IRQ12 and packet generation on enable_mouse_int_and_events(); CLEAR_CF(); regs.u.r8.ah = 0; regs.u.r8.bl = mouse_data1; regs.u.r8.bh = mouse_data2; return; } } } } // error SET_CF(); regs.u.r8.ah = ret; return; case 2: // Set Sample Rate BX_DEBUG_INT15("case 2:\n"); switch (regs.u.r8.bh) { case 0: mouse_data1 = 10; break; // 10 reports/sec case 1: mouse_data1 = 20; break; // 20 reports/sec case 2: mouse_data1 = 40; break; // 40 reports/sec case 3: mouse_data1 = 60; break; // 60 reports/sec case 4: mouse_data1 = 80; break; // 80 reports/sec case 5: mouse_data1 = 100; break; // 100 reports/sec (default) case 6: mouse_data1 = 200; break; // 200 reports/sec default: mouse_data1 = 0; } if (mouse_data1 > 0) { ret = send_to_mouse_ctrl(0xF3); // set sample rate command if (ret == 0) { ret = get_mouse_data(&mouse_data2); ret = send_to_mouse_ctrl(mouse_data1); ret = get_mouse_data(&mouse_data2); CLEAR_CF(); regs.u.r8.ah = 0; } else { // error SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; } } else { // error SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; } break; case 3: // Set Resolution BX_DEBUG_INT15("case 3:\n"); // BH: // 0 = 25 dpi, 1 count per millimeter // 1 = 50 dpi, 2 counts per millimeter // 2 = 100 dpi, 4 counts per millimeter // 3 = 200 dpi, 8 counts per millimeter comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets if (regs.u.r8.bh < 4) { ret = send_to_mouse_ctrl(0xE8); // set resolution command if (ret == 0) { ret = get_mouse_data(&mouse_data1); if (mouse_data1 != 0xfa) BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1); ret = send_to_mouse_ctrl(regs.u.r8.bh); ret = get_mouse_data(&mouse_data1); if (mouse_data1 != 0xfa) BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1); CLEAR_CF(); regs.u.r8.ah = 0; } else { // error SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; } } else { // error SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; } set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable break; case 4: // Get Device ID BX_DEBUG_INT15("case 4:\n"); inhibit_mouse_int_and_events(); // disable IRQ12 and packets ret = send_to_mouse_ctrl(0xF2); // get mouse ID command if (ret == 0) { ret = get_mouse_data(&mouse_data1); ret = get_mouse_data(&mouse_data2); CLEAR_CF(); regs.u.r8.ah = 0; regs.u.r8.bh = mouse_data2; } else { // error SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; } break; case 6: // Return Status & Set Scaling Factor... BX_DEBUG_INT15("case 6:\n"); switch (regs.u.r8.bh) { case 0: // Return Status comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets ret = send_to_mouse_ctrl(0xE9); // get mouse info command if (ret == 0) { ret = get_mouse_data(&mouse_data1); if (mouse_data1 != 0xfa) BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1); if (ret == 0) { ret = get_mouse_data(&mouse_data1); if ( ret == 0 ) { ret = get_mouse_data(&mouse_data2); if ( ret == 0 ) { ret = get_mouse_data(&mouse_data3); if ( ret == 0 ) { CLEAR_CF(); regs.u.r8.ah = 0; regs.u.r8.bl = mouse_data1; regs.u.r8.cl = mouse_data2; regs.u.r8.dl = mouse_data3; set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable return; } } } } } // error SET_CF(); regs.u.r8.ah = ret; set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable return; case 1: // Set Scaling Factor to 1:1 case 2: // Set Scaling Factor to 2:1 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets if (regs.u.r8.bh == 1) { ret = send_to_mouse_ctrl(0xE6); } else { ret = send_to_mouse_ctrl(0xE7); } if (ret == 0) { get_mouse_data(&mouse_data1); ret = (mouse_data1 != 0xFA); } if (ret == 0) { CLEAR_CF(); regs.u.r8.ah = 0; } else { // error SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; } set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable break; default: BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh); } break; case 7: // Set Mouse Handler Address BX_DEBUG_INT15("case 7:\n"); mouse_driver_seg = ES; mouse_driver_offset = regs.u.r16.bx; write_word(ebda_seg, 0x0022, mouse_driver_offset); write_word(ebda_seg, 0x0024, mouse_driver_seg); mouse_flags_2 = read_byte(ebda_seg, 0x0027); if (mouse_driver_offset == 0 && mouse_driver_seg == 0) { /* remove handler */ if ( (mouse_flags_2 & 0x80) != 0 ) { mouse_flags_2 &= ~0x80; inhibit_mouse_int_and_events(); // disable IRQ12 and packets } } else { /* install handler */ mouse_flags_2 |= 0x80; } write_byte(ebda_seg, 0x0027, mouse_flags_2); CLEAR_CF(); regs.u.r8.ah = 0; break; default: BX_DEBUG_INT15("case default:\n"); regs.u.r8.ah = 1; // invalid function SET_CF(); } break; default: BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n", (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx); SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; break; } } #endif // BX_USE_PS2_MOUSE void set_e820_range(ES, DI, start, end, type) Bit16u ES; Bit16u DI; Bit32u start; Bit32u end; Bit16u type; { write_word(ES, DI, start); write_word(ES, DI+2, start >> 16); write_word(ES, DI+4, 0x00); write_word(ES, DI+6, 0x00); end -= start; write_word(ES, DI+8, end); write_word(ES, DI+10, end >> 16); write_word(ES, DI+12, 0x0000); write_word(ES, DI+14, 0x0000); write_word(ES, DI+16, type); write_word(ES, DI+18, 0x0); } void int15_function32(regs, ES, DS, FLAGS) pushad_regs_t regs; // REGS pushed via pushad Bit16u ES, DS, FLAGS; { Bit32u extended_memory_size=0; // 64bits long Bit16u CX,DX; #ifdef HVMASSIST Bit16u off, e820_table_size; Bit32u base, type, size; #endif BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax); switch (regs.u.r8.ah) { case 0x86: // Wait for CX:DX microseconds. currently using the // refresh request port 0x61 bit4, toggling every 15usec CX = regs.u.r16.cx; DX = regs.u.r16.dx; ASM_START sti ;; Get the count in eax mov bx, sp SEG SS mov ax, _int15_function32.CX [bx] shl eax, #16 SEG SS mov ax, _int15_function32.DX [bx] ;; convert to numbers of 15usec ticks mov ebx, #15 xor edx, edx div eax, ebx mov ecx, eax ;; wait for ecx number of refresh requests in al, #0x61 and al,#0x10 mov ah, al or ecx, ecx je int1586_tick_end int1586_tick: in al, #0x61 and al,#0x10 cmp al, ah je int1586_tick mov ah, al dec ecx jnz int1586_tick int1586_tick_end: ASM_END break; case 0xe8: switch(regs.u.r8.al) { #ifdef HVMASSIST case 0x20: { e820_table_size = read_word(E820_SEG, E820_NR_OFFSET) * 0x14; if (regs.u.r32.edx != 0x534D4150) /* SMAP */ goto int15_unimplemented; if ((regs.u.r16.bx / 0x14) * 0x14 == regs.u.r16.bx) { if (regs.u.r16.bx + 0x14 <= e820_table_size) memcpyb(ES, regs.u.r16.di, E820_SEG, E820_OFFSET + regs.u.r16.bx, 0x14); regs.u.r32.ebx += 0x14; if ((regs.u.r32.ebx + 0x14 - 1) > e820_table_size) regs.u.r32.ebx = 0; } else if (regs.u.r16.bx == 1) { for (off = 0; off < e820_table_size; off += 0x14) { base = read_dword(E820_SEG, E820_OFFSET + off); type = read_dword(E820_SEG, E820_OFFSET + 0x10 + off); if ((base >= 0x100000) && (type == 1)) break; } if (off == e820_table_size) { SET_CF(); break; } memcpyb(ES, regs.u.r16.di, E820_SEG, E820_OFFSET + off, 0x14); regs.u.r32.ebx = 0; } else { /* AX=E820, DX=534D4150, BX unrecognized */ goto int15_unimplemented; } regs.u.r32.eax = 0x534D4150; regs.u.r32.ecx = 0x14; CLEAR_CF(); break; } case 0x01: { e820_table_size = read_word(E820_SEG, E820_NR_OFFSET) * 0x14; // do we have any reason to fail here ? CLEAR_CF(); // Get the amount of extended memory (above 1M) regs.u.r8.cl = inb_cmos(0x30); regs.u.r8.ch = inb_cmos(0x31); // limit to 15M if (regs.u.r16.cx > (15*1024)) regs.u.r16.cx = 15*1024; // Find first RAM E820 entry >= 1MB. for (off = 0; off < e820_table_size; off += 0x14) { base = read_dword(E820_SEG, E820_OFFSET + off); type = read_dword(E820_SEG, E820_OFFSET + 0x10 + off); if ((base >= 0x100000) && (type == 1)) break; } // If there is RAM above 16MB, return amount in 64kB chunks. regs.u.r16.dx = 0; if (off != e820_table_size) { size = base + read_dword(E820_SEG, E820_OFFSET + 0x8 + off); if (size > 0x1000000) { size -= 0x1000000; regs.u.r16.dx = (Bit16u)(size >> 16); } } // Set configured memory equal to extended memory regs.u.r16.ax = regs.u.r16.cx; regs.u.r16.bx = regs.u.r16.dx; break; } default: /* AH=0xE8?? but not implemented */ goto int15_unimplemented; } break; int15_unimplemented: // fall into the default default: BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n", (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx); SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; break; } #else case 0x20: // coded by osmaker aka K.J. if(regs.u.r32.edx == 0x534D4150) { extended_memory_size = inb_cmos(0x35); extended_memory_size <<= 8; extended_memory_size |= inb_cmos(0x34); extended_memory_size *= 64; // greater than EFF00000??? if(extended_memory_size > 0x3bc000) { extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000 } extended_memory_size *= 1024; extended_memory_size += (16L * 1024 * 1024); if(extended_memory_size <= (16L * 1024 * 1024)) { extended_memory_size = inb_cmos(0x31); extended_memory_size <<= 8; extended_memory_size |= inb_cmos(0x30); extended_memory_size *= 1024; extended_memory_size += (1L * 1024 * 1024); } switch(regs.u.r16.bx) { case 0: set_e820_range(ES, regs.u.r16.di, 0x0000000L, 0x0009f000L, 1); regs.u.r32.ebx = 1; regs.u.r32.eax = 0x534D4150; regs.u.r32.ecx = 0x14; CLEAR_CF(); return; break; case 1: set_e820_range(ES, regs.u.r16.di, 0x0009f000L, 0x000a0000L, 2); regs.u.r32.ebx = 2; regs.u.r32.eax = 0x534D4150; regs.u.r32.ecx = 0x14; CLEAR_CF(); return; break; case 2: set_e820_range(ES, regs.u.r16.di, 0x000e8000L, 0x00100000L, 2); regs.u.r32.ebx = 3; regs.u.r32.eax = 0x534D4150; regs.u.r32.ecx = 0x14; CLEAR_CF(); return; break; case 3: #if BX_ROMBIOS32 set_e820_range(ES, regs.u.r16.di, 0x00100000L, extended_memory_size - ACPI_DATA_SIZE, 1); regs.u.r32.ebx = 4; #else set_e820_range(ES, regs.u.r16.di, 0x00100000L, extended_memory_size, 1); regs.u.r32.ebx = 5; #endif regs.u.r32.eax = 0x534D4150; regs.u.r32.ecx = 0x14; CLEAR_CF(); return; break; case 4: set_e820_range(ES, regs.u.r16.di, extended_memory_size - ACPI_DATA_SIZE, extended_memory_size, 3); // ACPI RAM regs.u.r32.ebx = 5; regs.u.r32.eax = 0x534D4150; regs.u.r32.ecx = 0x14; CLEAR_CF(); return; break; case 5: /* 256KB BIOS area at the end of 4 GB */ set_e820_range(ES, regs.u.r16.di, 0xfffc0000L, 0x00000000L, 2); regs.u.r32.ebx = 0; regs.u.r32.eax = 0x534D4150; regs.u.r32.ecx = 0x14; CLEAR_CF(); return; default: /* AX=E820, DX=534D4150, BX unrecognized */ goto int15_unimplemented; break; } } else { // if DX != 0x534D4150) goto int15_unimplemented; } break; case 0x01: // do we have any reason to fail here ? CLEAR_CF(); // my real system sets ax and bx to 0 // this is confirmed by Ralph Brown list // but syslinux v1.48 is known to behave // strangely if ax is set to 0 // regs.u.r16.ax = 0; // regs.u.r16.bx = 0; // Get the amount of extended memory (above 1M) regs.u.r8.cl = inb_cmos(0x30); regs.u.r8.ch = inb_cmos(0x31); // limit to 15M if(regs.u.r16.cx > 0x3c00) { regs.u.r16.cx = 0x3c00; } // Get the amount of extended memory above 16M in 64k blocs regs.u.r8.dl = inb_cmos(0x34); regs.u.r8.dh = inb_cmos(0x35); // Set configured memory equal to extended memory regs.u.r16.ax = regs.u.r16.cx; regs.u.r16.bx = regs.u.r16.dx; break; default: /* AH=0xE8?? but not implemented */ goto int15_unimplemented; } break; int15_unimplemented: // fall into the default default: BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n", (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx); SET_CF(); regs.u.r8.ah = UNSUPPORTED_FUNCTION; break; } #endif /* HVMASSIST */ } void int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS) Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS; { Bit8u scan_code, ascii_code, shift_flags, led_flags, count; Bit16u kbd_code, max; BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX); shift_flags = read_byte(0x0040, 0x17); led_flags = read_byte(0x0040, 0x97); if ((((shift_flags >> 4) & 0x07) ^ (led_flags & 0x07)) != 0) { ASM_START cli ASM_END outb(0x60, 0xed); while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21); if ((inb(0x60) == 0xfa)) { led_flags &= 0xf8; led_flags |= ((shift_flags >> 4) & 0x07); outb(0x60, led_flags & 0x07); while ((inb(0x64) & 0x01) == 0) outb(0x80, 0x21); inb(0x60); write_byte(0x0040, 0x97, led_flags); } ASM_START sti ASM_END } switch (GET_AH()) { case 0x00: /* read keyboard input */ if ( !dequeue_key(&scan_code, &ascii_code, 1) ) { BX_PANIC("KBD: int16h: out of keyboard input\n"); } if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; else if (ascii_code == 0xE0) ascii_code = 0; AX = (scan_code << 8) | ascii_code; break; case 0x01: /* check keyboard status */ if ( !dequeue_key(&scan_code, &ascii_code, 0) ) { SET_ZF(); return; } if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; else if (ascii_code == 0xE0) ascii_code = 0; AX = (scan_code << 8) | ascii_code; CLEAR_ZF(); break; case 0x02: /* get shift flag status */ shift_flags = read_byte(0x0040, 0x17); SET_AL(shift_flags); break; case 0x05: /* store key-stroke into buffer */ if ( !enqueue_key(GET_CH(), GET_CL()) ) { SET_AL(1); } else { SET_AL(0); } break; case 0x09: /* GET KEYBOARD FUNCTIONALITY */ // bit Bochs Description // 7 0 reserved // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support) // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support) // 4 1 INT 16/AH=0Ah supported // 3 0 INT 16/AX=0306h supported // 2 0 INT 16/AX=0305h supported // 1 0 INT 16/AX=0304h supported // 0 0 INT 16/AX=0300h supported // SET_AL(0x30); break; case 0x0A: /* GET KEYBOARD ID */ count = 2; kbd_code = 0x0; outb(0x60, 0xf2); /* Wait for data */ max=0xffff; while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00); if (max>0x0) { if ((inb(0x60) == 0xfa)) { do { max=0xffff; while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00); if (max>0x0) { kbd_code >>= 8; kbd_code |= (inb(0x60) << 8); } } while (--count>0); } } BX=kbd_code; break; case 0x10: /* read MF-II keyboard input */ if ( !dequeue_key(&scan_code, &ascii_code, 1) ) { BX_PANIC("KBD: int16h: out of keyboard input\n"); } if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; AX = (scan_code << 8) | ascii_code; break; case 0x11: /* check MF-II keyboard status */ if ( !dequeue_key(&scan_code, &ascii_code, 0) ) { SET_ZF(); return; } if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; AX = (scan_code << 8) | ascii_code; CLEAR_ZF(); break; case 0x12: /* get extended keyboard status */ shift_flags = read_byte(0x0040, 0x17); SET_AL(shift_flags); shift_flags = read_byte(0x0040, 0x18) & 0x73; shift_flags |= read_byte(0x0040, 0x96) & 0x0c; SET_AH(shift_flags); BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX); break; case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */ SET_AH(0x80); // function int16 ah=0x10-0x12 supported break; case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */ // don't change AH : function int16 ah=0x20-0x22 NOT supported break; case 0x6F: if (GET_AL() == 0x08) SET_AH(0x02); // unsupported, aka normal keyboard default: BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH()); } } unsigned int dequeue_key(scan_code, ascii_code, incr) Bit8u *scan_code; Bit8u *ascii_code; unsigned int incr; { Bit16u buffer_start, buffer_end, buffer_head, buffer_tail; Bit16u ss; Bit8u acode, scode; #if BX_CPU < 2 buffer_start = 0x001E; buffer_end = 0x003E; #else buffer_start = read_word(0x0040, 0x0080); buffer_end = read_word(0x0040, 0x0082); #endif buffer_head = read_word(0x0040, 0x001a); buffer_tail = read_word(0x0040, 0x001c); if (buffer_head != buffer_tail) { ss = get_SS(); acode = read_byte(0x0040, buffer_head); scode = read_byte(0x0040, buffer_head+1); write_byte(ss, ascii_code, acode); write_byte(ss, scan_code, scode); if (incr) { buffer_head += 2; if (buffer_head >= buffer_end) buffer_head = buffer_start; write_word(0x0040, 0x001a, buffer_head); } return(1); } else { return(0); } } static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n"; Bit8u inhibit_mouse_int_and_events() { Bit8u command_byte, prev_command_byte; // Turn off IRQ generation and aux data line if ( inb(0x64) & 0x02 ) BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse"); outb(0x64, 0x20); // get command byte while ( (inb(0x64) & 0x01) != 0x01 ); prev_command_byte = inb(0x60); command_byte = prev_command_byte; //while ( (inb(0x64) & 0x02) ); if ( inb(0x64) & 0x02 ) BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse"); command_byte &= 0xfd; // turn off IRQ 12 generation command_byte |= 0x20; // disable mouse serial clock line outb(0x64, 0x60); // write command byte outb(0x60, command_byte); return(prev_command_byte); } void enable_mouse_int_and_events() { Bit8u command_byte; // Turn on IRQ generation and aux data line if ( inb(0x64) & 0x02 ) BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse"); outb(0x64, 0x20); // get command byte while ( (inb(0x64) & 0x01) != 0x01 ); command_byte = inb(0x60); //while ( (inb(0x64) & 0x02) ); if ( inb(0x64) & 0x02 ) BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse"); command_byte |= 0x02; // turn on IRQ 12 generation command_byte &= 0xdf; // enable mouse serial clock line outb(0x64, 0x60); // write command byte outb(0x60, command_byte); } Bit8u send_to_mouse_ctrl(sendbyte) Bit8u sendbyte; { Bit8u response; // wait for chance to write to ctrl if ( inb(0x64) & 0x02 ) BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse"); outb(0x64, 0xD4); outb(0x60, sendbyte); return(0); } Bit8u get_mouse_data(data) Bit8u *data; { Bit8u response; Bit16u ss; while ( (inb(0x64) & 0x21) != 0x21 ) { } response = inb(0x60); ss = get_SS(); write_byte(ss, data, response); return(0); } void set_kbd_command_byte(command_byte) Bit8u command_byte; { if ( inb(0x64) & 0x02 ) BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm"); outb(0x64, 0xD4); outb(0x64, 0x60); // write command byte outb(0x60, command_byte); } void int09_function(DI, SI, BP, SP, BX, DX, CX, AX) Bit16u DI, SI, BP, SP, BX, DX, CX, AX; { Bit8u scancode, asciicode, shift_flags; Bit8u mf2_flags, mf2_state; // // DS has been set to F000 before call // scancode = GET_AL(); if (scancode == 0) { BX_INFO("KBD: int09 handler: AL=0\n"); return; } shift_flags = read_byte(0x0040, 0x17); mf2_flags = read_byte(0x0040, 0x18); mf2_state = read_byte(0x0040, 0x96); asciicode = 0; switch (scancode) { case 0x3a: /* Caps Lock press */ shift_flags ^= 0x40; write_byte(0x0040, 0x17, shift_flags); mf2_flags |= 0x40; write_byte(0x0040, 0x18, mf2_flags); break; case 0xba: /* Caps Lock release */ mf2_flags &= ~0x40; write_byte(0x0040, 0x18, mf2_flags); break; case 0x2a: /* L Shift press */ shift_flags |= 0x02; write_byte(0x0040, 0x17, shift_flags); break; case 0xaa: /* L Shift release */ shift_flags &= ~0x02; write_byte(0x0040, 0x17, shift_flags); break; case 0x36: /* R Shift press */ shift_flags |= 0x01; write_byte(0x0040, 0x17, shift_flags); break; case 0xb6: /* R Shift release */ shift_flags &= ~0x01; write_byte(0x0040, 0x17, shift_flags); break; case 0x1d: /* Ctrl press */ if ((mf2_state & 0x01) == 0) { shift_flags |= 0x04; write_byte(0x0040, 0x17, shift_flags); if (mf2_state & 0x02) { mf2_state |= 0x04; write_byte(0x0040, 0x96, mf2_state); } else { mf2_flags |= 0x01; write_byte(0x0040, 0x18, mf2_flags); } } break; case 0x9d: /* Ctrl release */ if ((mf2_state & 0x01) == 0) { shift_flags &= ~0x04; write_byte(0x0040, 0x17, shift_flags); if (mf2_state & 0x02) { mf2_state &= ~0x04; write_byte(0x0040, 0x96, mf2_state); } else { mf2_flags &= ~0x01; write_byte(0x0040, 0x18, mf2_flags); } } break; case 0x38: /* Alt press */ shift_flags |= 0x08; write_byte(0x0040, 0x17, shift_flags); if (mf2_state & 0x02) { mf2_state |= 0x08; write_byte(0x0040, 0x96, mf2_state); } else { mf2_flags |= 0x02; write_byte(0x0040, 0x18, mf2_flags); } break; case 0xb8: /* Alt release */ shift_flags &= ~0x08; write_byte(0x0040, 0x17, shift_flags); if (mf2_state & 0x02) { mf2_state &= ~0x08; write_byte(0x0040, 0x96, mf2_state); } else { mf2_flags &= ~0x02; write_byte(0x0040, 0x18, mf2_flags); } break; case 0x45: /* Num Lock press */ if ((mf2_state & 0x03) == 0) { mf2_flags |= 0x20; write_byte(0x0040, 0x18, mf2_flags); shift_flags ^= 0x20; write_byte(0x0040, 0x17, shift_flags); } break; case 0xc5: /* Num Lock release */ if ((mf2_state & 0x03) == 0) { mf2_flags &= ~0x20; write_byte(0x0040, 0x18, mf2_flags); } break; case 0x46: /* Scroll Lock press */ mf2_flags |= 0x10; write_byte(0x0040, 0x18, mf2_flags); shift_flags ^= 0x10; write_byte(0x0040, 0x17, shift_flags); break; case 0xc6: /* Scroll Lock release */ mf2_flags &= ~0x10; write_byte(0x0040, 0x18, mf2_flags); break; case 0x53: /* Del */ if ((shift_flags & 0x0c) == 0x0c) /* Ctrl + Alt */ machine_reset(); /* Fall through */ default: if (scancode & 0x80) { break; /* toss key releases ... */ } if (scancode > MAX_SCAN_CODE) { BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode); return; } if (shift_flags & 0x08) { /* ALT */ asciicode = scan_to_scanascii[scancode].alt; scancode = scan_to_scanascii[scancode].alt >> 8; } else if (shift_flags & 0x04) { /* CONTROL */ asciicode = scan_to_scanascii[scancode].control; scancode = scan_to_scanascii[scancode].control >> 8; } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) { /* extended keys handling */ asciicode = 0xe0; scancode = scan_to_scanascii[scancode].normal >> 8; } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */ /* check if lock state should be ignored * because a SHIFT key are pressed */ if (shift_flags & scan_to_scanascii[scancode].lock_flags) { asciicode = scan_to_scanascii[scancode].normal; scancode = scan_to_scanascii[scancode].normal >> 8; } else { asciicode = scan_to_scanascii[scancode].shift; scancode = scan_to_scanascii[scancode].shift >> 8; } } else { /* check if lock is on */ if (shift_flags & scan_to_scanascii[scancode].lock_flags) { asciicode = scan_to_scanascii[scancode].shift; scancode = scan_to_scanascii[scancode].shift >> 8; } else { asciicode = scan_to_scanascii[scancode].normal; scancode = scan_to_scanascii[scancode].normal >> 8; } } if (scancode==0 && asciicode==0) { BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n"); } enqueue_key(scancode, asciicode); break; } if ((scancode & 0x7f) != 0x1d) { mf2_state &= ~0x01; } mf2_state &= ~0x02; write_byte(0x0040, 0x96, mf2_state); } unsigned int enqueue_key(scan_code, ascii_code) Bit8u scan_code, ascii_code; { Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail; #if BX_CPU < 2 buffer_start = 0x001E; buffer_end = 0x003E; #else buffer_start = read_word(0x0040, 0x0080); buffer_end = read_word(0x0040, 0x0082); #endif buffer_head = read_word(0x0040, 0x001A); buffer_tail = read_word(0x0040, 0x001C); temp_tail = buffer_tail; buffer_tail += 2; if (buffer_tail >= buffer_end) buffer_tail = buffer_start; if (buffer_tail == buffer_head) { return(0); } write_byte(0x0040, temp_tail, ascii_code); write_byte(0x0040, temp_tail+1, scan_code); write_word(0x0040, 0x001C, buffer_tail); return(1); } void int74_function(make_farcall, Z, Y, X, status) Bit16u make_farcall, Z, Y, X, status; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit8u in_byte, index, package_count; Bit8u mouse_flags_1, mouse_flags_2; BX_DEBUG_INT74("entering int74_function\n"); make_farcall = 0; in_byte = inb(0x64); if ( (in_byte & 0x21) != 0x21 ) { return; } in_byte = inb(0x60); BX_DEBUG_INT74("int74: read byte %02x\n", in_byte); mouse_flags_1 = read_byte(ebda_seg, 0x0026); mouse_flags_2 = read_byte(ebda_seg, 0x0027); if ( (mouse_flags_2 & 0x80) != 0x80 ) { return; } package_count = mouse_flags_2 & 0x07; index = mouse_flags_1 & 0x07; write_byte(ebda_seg, 0x28 + index, in_byte); if ( (index+1) >= package_count ) { BX_DEBUG_INT74("int74_function: make_farcall=1\n"); status = read_byte(ebda_seg, 0x0028 + 0); X = read_byte(ebda_seg, 0x0028 + 1); Y = read_byte(ebda_seg, 0x0028 + 2); Z = 0; mouse_flags_1 = 0; // check if far call handler installed if (mouse_flags_2 & 0x80) make_farcall = 1; } else { mouse_flags_1++; } write_byte(ebda_seg, 0x0026, mouse_flags_1); } #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status) #if BX_USE_ATADRV void int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; { Bit32u lba_low, lba_high; Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u cylinder, head, sector; Bit16u segment, offset; Bit16u npc, nph, npspt, nlc, nlh, nlspt; Bit16u size, count; Bit8u device, status; BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); write_byte(0x0040, 0x008e, 0); // clear completion flag // basic check : device has to be defined if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) { BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL()); goto int13_fail; } // Get the ata channel device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]); // basic check : device has to be valid if (device >= BX_MAX_ATA_DEVICES) { BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL()); goto int13_fail; } switch (GET_AH()) { case 0x00: /* disk controller reset */ ata_reset (device); goto int13_success; break; case 0x01: /* read disk status */ status = read_byte(0x0040, 0x0074); SET_AH(status); SET_DISK_RET_STATUS(0); /* set CF if error status read */ if (status) goto int13_fail_nostatus; else goto int13_success_noah; break; case 0x02: // read disk sectors case 0x03: // write disk sectors case 0x04: // verify disk sectors count = GET_AL(); cylinder = GET_CH(); cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300; sector = (GET_CL() & 0x3f); head = GET_DH(); segment = ES; offset = BX; if ((count > 128) || (count == 0) || (sector == 0)) { BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH()); goto int13_fail; } nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders); nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads); nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt); // sanity check on cyl heads, sec if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) { BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector); goto int13_fail; } // FIXME verify if ( GET_AH() == 0x04 ) goto int13_success; nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads); npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt); // if needed, translate lchs to lba, and execute command if ( (nph != nlh) || (npspt != nlspt)) { lba_low = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1; lba_high = 0; sector = 0; // this forces the command to be lba } if ( GET_AH() == 0x02 ) status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset); else status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset); // Set nb of sector transferred SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors)); if (status != 0) { BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status); SET_AH(0x0c); goto int13_fail_noah; } goto int13_success; break; case 0x05: /* format disk track */ BX_INFO("format disk track called\n"); goto int13_success; return; break; case 0x08: /* read disk drive parameters */ // Get logical geometry from table nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders); nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads); nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt); count = read_byte(ebda_seg, &EbdaData->ata.hdcount); nlc = nlc - 2; /* 0 based , last sector not used */ SET_AL(0); SET_CH(nlc & 0xff); SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f)); SET_DH(nlh - 1); SET_DL(count); /* FIXME returns 0, 1, or n hard drives */ // FIXME should set ES & DI goto int13_success; break; case 0x10: /* check drive ready */ // should look at 40:8E also??? // Read the status from controller status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT); if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) { goto int13_success; } else { SET_AH(0xAA); goto int13_fail_noah; } break; case 0x15: /* read disk drive size */ // Get logical geometry from table nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders); nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads); nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt); // Compute sector count seen by int13 lba_low = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt; CX = lba_low >> 16; DX = lba_low & 0xffff; SET_AH(3); // hard disk accessible goto int13_success_noah; break; case 0x41: // IBM/MS installation check BX=0xaa55; // install check SET_AH(0x30); // EDD 3.0 CX=0x0007; // ext disk access and edd, removable supported goto int13_success_noah; break; case 0x42: // IBM/MS extended read case 0x43: // IBM/MS extended write case 0x44: // IBM/MS verify case 0x47: // IBM/MS extended seek count=read_word(DS, SI+(Bit16u)&Int13Ext->count); segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment); offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset); // Get 32 msb lba and check lba_high=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2); if (lba_high > read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) ) { BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH()); goto int13_fail; } // Get 32 lsb lba and check lba_low=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1); if (lba_high == read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) && lba_low >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low) ) { BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH()); goto int13_fail; } // If verify or seek if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 )) goto int13_success; // Execute the command if ( GET_AH() == 0x42 ) status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset); else status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset); count=read_word(ebda_seg, &EbdaData->ata.trsfsectors); write_word(DS, SI+(Bit16u)&Int13Ext->count, count); if (status != 0) { BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status); SET_AH(0x0c); goto int13_fail_noah; } goto int13_success; break; case 0x45: // IBM/MS lock/unlock drive case 0x49: // IBM/MS extended media change goto int13_success; // Always success for HD break; case 0x46: // IBM/MS eject media SET_AH(0xb2); // Volume Not Removable goto int13_fail_noah; // Always fail for HD break; case 0x48: // IBM/MS get drive parameters size=read_word(DS,SI+(Bit16u)&Int13DPT->size); // Buffer is too small if(size < 0x1a) goto int13_fail; // EDD 1.x if(size >= 0x1a) { Bit16u blksize; npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders); nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads); npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt); lba_low = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low); lba_high = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high); blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize); write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a); if (lba_high || (lba_low/npspt)/nph > 0x3fff) { write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff); } else { write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc); } write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph); write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt); write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba_low); write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, lba_high); write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize); } // EDD 2.x if(size >= 0x1e) { Bit8u channel, dev, irq, mode, checksum, i, translation; Bit16u iobase1, iobase2, options; write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e); write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg); write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte); // Fill in dpte channel = device / 2; iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq); mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation); options = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation options |= (1<<4); // lba translation options |= (mode==ATA_MODE_PIO32?1:0)<<7; options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9; options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9; write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1); write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC); write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 ); write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb ); write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq ); write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 ); write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 ); write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 ); write_word(ebda_seg, &EbdaData->ata.dpte.options, options); write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0); if (size >=0x42) write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11); else write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10); checksum=0; for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i); checksum = ~checksum; write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum); } // EDD 3.x if(size >= 0x42) { Bit8u channel, iface, checksum, i; Bit16u iobase1; channel = device / 2; iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface); iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42); write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd); write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24); write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0); write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0); if (iface==ATA_IFACE_ISA) { write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I'); write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S'); write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A'); write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0); } else { // FIXME PCI } write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A'); write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T'); write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A'); write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0); if (iface==ATA_IFACE_ISA) { write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1); write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0); write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L); } else { // FIXME PCI } write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2); write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0); write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0); write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L); checksum=0; for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i); checksum = ~checksum; write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum); } goto int13_success; break; case 0x4e: // // IBM/MS set hardware configuration // DMA, prefetch, PIO maximum not supported switch (GET_AL()) { case 0x01: case 0x03: case 0x04: case 0x06: goto int13_success; break; default : goto int13_fail; } break; case 0x09: /* initialize drive parameters */ case 0x0c: /* seek to specified cylinder */ case 0x0d: /* alternate disk reset */ case 0x11: /* recalibrate */ case 0x14: /* controller internal diagnostic */ BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH()); goto int13_success; break; case 0x0a: /* read disk sectors with ECC */ case 0x0b: /* write disk sectors with ECC */ case 0x18: // set media type for format case 0x50: // IBM/MS send packet command default: BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH()); goto int13_fail; break; } int13_fail: SET_AH(0x01); // defaults to invalid function in AH or invalid parameter int13_fail_noah: SET_DISK_RET_STATUS(GET_AH()); int13_fail_nostatus: SET_CF(); // error occurred return; int13_success: SET_AH(0x00); // no error int13_success_noah: SET_DISK_RET_STATUS(0x00); CLEAR_CF(); // no error return; } // --------------------------------------------------------------------------- // Start of int13 for cdrom // --------------------------------------------------------------------------- void int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit8u device, status, locks; Bit8u atacmd[12]; Bit32u lba; Bit16u count, segment, offset, i, size; BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); SET_DISK_RET_STATUS(0x00); /* basic check : device should be 0xE0+ */ if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) { BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL()); goto int13_fail; } // Get the ata channel device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]); /* basic check : device has to be valid */ if (device >= BX_MAX_ATA_DEVICES) { BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL()); goto int13_fail; } switch (GET_AH()) { // all those functions return SUCCESS case 0x00: /* disk controller reset */ case 0x09: /* initialize drive parameters */ case 0x0c: /* seek to specified cylinder */ case 0x0d: /* alternate disk reset */ case 0x10: /* check drive ready */ case 0x11: /* recalibrate */ case 0x14: /* controller internal diagnostic */ case 0x16: /* detect disk change */ goto int13_success; break; // all those functions return disk write-protected case 0x03: /* write disk sectors */ case 0x05: /* format disk track */ case 0x43: // IBM/MS extended write SET_AH(0x03); goto int13_fail_noah; break; case 0x01: /* read disk status */ status = read_byte(0x0040, 0x0074); SET_AH(status); SET_DISK_RET_STATUS(0); /* set CF if error status read */ if (status) goto int13_fail_nostatus; else goto int13_success_noah; break; case 0x15: /* read disk drive size */ SET_AH(0x02); goto int13_fail_noah; break; case 0x41: // IBM/MS installation check BX=0xaa55; // install check SET_AH(0x30); // EDD 2.1 CX=0x0007; // ext disk access, removable and edd goto int13_success_noah; break; case 0x42: // IBM/MS extended read case 0x44: // IBM/MS verify sectors case 0x47: // IBM/MS extended seek count=read_word(DS, SI+(Bit16u)&Int13Ext->count); segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment); offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset); // Can't use 64 bits lba lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2); if (lba != 0L) { BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH()); goto int13_fail; } // Get 32 bits lba lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1); // If verify or seek if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 )) goto int13_success; memsetb(get_SS(),atacmd,0,12); atacmd[0]=0x28; // READ command atacmd[7]=(count & 0xff00) >> 8; // Sectors atacmd[8]=(count & 0x00ff); // Sectors atacmd[2]=(lba & 0xff000000) >> 24; // LBA atacmd[3]=(lba & 0x00ff0000) >> 16; atacmd[4]=(lba & 0x0000ff00) >> 8; atacmd[5]=(lba & 0x000000ff); status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset); count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11); write_word(DS, SI+(Bit16u)&Int13Ext->count, count); if (status != 0) { BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status); SET_AH(0x0c); goto int13_fail_noah; } goto int13_success; break; case 0x45: // IBM/MS lock/unlock drive if (GET_AL() > 2) goto int13_fail; locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock); switch (GET_AL()) { case 0 : // lock if (locks == 0xff) { SET_AH(0xb4); SET_AL(1); goto int13_fail_noah; } write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks); SET_AL(1); break; case 1 : // unlock if (locks == 0x00) { SET_AH(0xb0); SET_AL(0); goto int13_fail_noah; } write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks); SET_AL(locks==0?0:1); break; case 2 : // status SET_AL(locks==0?0:1); break; } goto int13_success; break; case 0x46: // IBM/MS eject media locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock); if (locks != 0) { SET_AH(0xb1); // media locked goto int13_fail_noah; } // FIXME should handle 0x31 no media in device // FIXME should handle 0xb5 valid request failed // Call removable media eject ASM_START push bp mov bp, sp mov ah, #0x52 int #0x15 mov _int13_cdrom.status + 2[bp], ah jnc int13_cdrom_rme_end mov _int13_cdrom.status, #1 int13_cdrom_rme_end: pop bp ASM_END if (status != 0) { SET_AH(0xb1); // media locked goto int13_fail_noah; } goto int13_success; break; case 0x48: // IBM/MS get drive parameters size = read_word(DS,SI+(Bit16u)&Int13Ext->size); // Buffer is too small if(size < 0x1a) goto int13_fail; // EDD 1.x if(size >= 0x1a) { Bit16u cylinders, heads, spt, blksize; blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize); write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a); write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff); write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff); write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff); write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64 write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff); write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize); } // EDD 2.x if(size >= 0x1e) { Bit8u channel, dev, irq, mode, checksum, i; Bit16u iobase1, iobase2, options; write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e); write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg); write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte); // Fill in dpte channel = device / 2; iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2); irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq); mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode); // FIXME atapi device options = (1<<4); // lba translation options |= (1<<5); // removable device options |= (1<<6); // atapi device options |= (mode==ATA_MODE_PIO32?1:0<<7); write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1); write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC); write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 ); write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb ); write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq ); write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 ); write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 ); write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 ); write_word(ebda_seg, &EbdaData->ata.dpte.options, options); write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0); write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11); checksum=0; for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i); checksum = ~checksum; write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum); } // EDD 3.x if(size >= 0x42) { Bit8u channel, iface, checksum, i; Bit16u iobase1; channel = device / 2; iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface); iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1); write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42); write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd); write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24); write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0); write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0); if (iface==ATA_IFACE_ISA) { write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I'); write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S'); write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A'); write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0); } else { // FIXME PCI } write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A'); write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T'); write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A'); write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0); if (iface==ATA_IFACE_ISA) { write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1); write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0); write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L); } else { // FIXME PCI } write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2); write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0); write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0); write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L); checksum=0; for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i); checksum = ~checksum; write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum); } goto int13_success; break; case 0x49: // IBM/MS extended media change // always send changed ?? SET_AH(06); goto int13_fail_nostatus; break; case 0x4e: // // IBM/MS set hardware configuration // DMA, prefetch, PIO maximum not supported switch (GET_AL()) { case 0x01: case 0x03: case 0x04: case 0x06: goto int13_success; break; default : goto int13_fail; } break; // all those functions return unimplemented case 0x02: /* read sectors */ case 0x04: /* verify sectors */ case 0x08: /* read disk drive parameters */ case 0x0a: /* read disk sectors with ECC */ case 0x0b: /* write disk sectors with ECC */ case 0x18: /* set media type for format */ case 0x50: // ? - send packet command default: BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH()); goto int13_fail; break; } int13_fail: SET_AH(0x01); // defaults to invalid function in AH or invalid parameter int13_fail_noah: SET_DISK_RET_STATUS(GET_AH()); int13_fail_nostatus: SET_CF(); // error occurred return; int13_success: SET_AH(0x00); // no error int13_success_noah: SET_DISK_RET_STATUS(0x00); CLEAR_CF(); // no error return; } // --------------------------------------------------------------------------- // End of int13 for cdrom // --------------------------------------------------------------------------- #if BX_ELTORITO_BOOT // --------------------------------------------------------------------------- // Start of int13 for eltorito functions // --------------------------------------------------------------------------- void int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS; { Bit16u ebda_seg=read_word(0x0040,0x000E); BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI); switch (GET_AH()) { // FIXME ElTorito Various. Should be implemented case 0x4a: // ElTorito - Initiate disk emu case 0x4c: // ElTorito - Initiate disk emu and boot case 0x4d: // ElTorito - Return Boot catalog BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX); goto int13_fail; break; case 0x4b: // ElTorito - Terminate disk emu // FIXME ElTorito Hardcoded write_byte(DS,SI+0x00,0x13); write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media)); write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)); write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index)); write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba)); write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec)); write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment)); write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment)); write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count)); write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders)); write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt)); write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads)); // If we have to terminate emulation if(GET_AL() == 0x00) { // FIXME ElTorito Various. Should be handled accordingly to spec write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye } goto int13_success; break; default: BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH()); goto int13_fail; break; } int13_fail: SET_AH(0x01); // defaults to invalid function in AH or invalid parameter SET_DISK_RET_STATUS(GET_AH()); SET_CF(); // error occurred return; int13_success: SET_AH(0x00); // no error SET_DISK_RET_STATUS(0x00); CLEAR_CF(); // no error return; } // --------------------------------------------------------------------------- // End of int13 for eltorito functions // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Start of int13 when emulating a device from the cd // --------------------------------------------------------------------------- void int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit8u device, status; Bit16u vheads, vspt, vcylinders; Bit16u head, sector, cylinder, nbsectors; Bit32u vlba, ilba, slba, elba; Bit16u before, segment, offset; Bit8u atacmd[12]; BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); /* at this point, we are emulating a floppy/harddisk */ // Recompute the device number device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2; device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec); SET_DISK_RET_STATUS(0x00); /* basic checks : emulation should be active, dl should equal the emulated drive */ if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 ) || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) { BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL()); goto int13_fail; } switch (GET_AH()) { // all those functions return SUCCESS case 0x00: /* disk controller reset */ case 0x09: /* initialize drive parameters */ case 0x0c: /* seek to specified cylinder */ case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ? case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ? case 0x11: /* recalibrate */ case 0x14: /* controller internal diagnostic */ case 0x16: /* detect disk change */ goto int13_success; break; // all those functions return disk write-protected case 0x03: /* write disk sectors */ case 0x05: /* format disk track */ SET_AH(0x03); goto int13_fail_noah; break; case 0x01: /* read disk status */ status=read_byte(0x0040, 0x0074); SET_AH(status); SET_DISK_RET_STATUS(0); /* set CF if error status read */ if (status) goto int13_fail_nostatus; else goto int13_success_noah; break; case 0x02: // read disk sectors case 0x04: // verify disk sectors vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders); vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads); ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba); sector = GET_CL() & 0x003f; cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH(); head = GET_DH(); nbsectors = GET_AL(); segment = ES; offset = BX; // no sector to read ? if(nbsectors==0) goto int13_success; // sanity checks sco openserver needs this! if ((sector > vspt) || (cylinder >= vcylinders) || (head >= vheads)) { goto int13_fail; } // After controls, verify do nothing if (GET_AH() == 0x04) goto int13_success; segment = ES+(BX / 16); offset = BX % 16; // calculate the virtual lba inside the image vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1)); // In advance so we don't loose the count SET_AL(nbsectors); // start lba on cd slba = (Bit32u)vlba/4; before= (Bit16u)vlba%4; // end lba on cd elba = (Bit32u)(vlba+nbsectors-1)/4; memsetb(get_SS(),atacmd,0,12); atacmd[0]=0x28; // READ command atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA atacmd[3]=(ilba+slba & 0x00ff0000) >> 16; atacmd[4]=(ilba+slba & 0x0000ff00) >> 8; atacmd[5]=(ilba+slba & 0x000000ff); if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) { BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status); SET_AH(0x02); SET_AL(0); goto int13_fail_noah; } goto int13_success; break; case 0x08: /* read disk drive parameters */ vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1; vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1; SET_AL( 0x00 ); SET_BL( 0x00 ); SET_CH( vcylinders & 0xff ); SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f )); SET_DH( vheads ); SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2 // FIXME ElTorito Harddisk. should send the HD count switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) { case 0x01: SET_BL( 0x02 ); break; case 0x02: SET_BL( 0x04 ); break; case 0x03: SET_BL( 0x06 ); break; } ASM_START push bp mov bp, sp mov ax, #diskette_param_table2 mov _int13_cdemu.DI+2[bp], ax mov _int13_cdemu.ES+2[bp], cs pop bp ASM_END goto int13_success; break; case 0x15: /* read disk drive size */ // FIXME ElTorito Harddisk. What geometry to send ? SET_AH(0x03); goto int13_success_noah; break; // all those functions return unimplemented case 0x0a: /* read disk sectors with ECC */ case 0x0b: /* write disk sectors with ECC */ case 0x18: /* set media type for format */ case 0x41: // IBM/MS installation check // FIXME ElTorito Harddisk. Darwin would like to use EDD case 0x42: // IBM/MS extended read case 0x43: // IBM/MS extended write case 0x44: // IBM/MS verify sectors case 0x45: // IBM/MS lock/unlock drive case 0x46: // IBM/MS eject media case 0x47: // IBM/MS extended seek case 0x48: // IBM/MS get drive parameters case 0x49: // IBM/MS extended media change case 0x4e: // ? - set hardware configuration case 0x50: // ? - send packet command default: BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH()); goto int13_fail; break; } int13_fail: SET_AH(0x01); // defaults to invalid function in AH or invalid parameter int13_fail_noah: SET_DISK_RET_STATUS(GET_AH()); int13_fail_nostatus: SET_CF(); // error occurred return; int13_success: SET_AH(0x00); // no error int13_success_noah: SET_DISK_RET_STATUS(0x00); CLEAR_CF(); // no error return; } // --------------------------------------------------------------------------- // End of int13 when emulating a device from the cd // --------------------------------------------------------------------------- #endif // BX_ELTORITO_BOOT #else //BX_USE_ATADRV void outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl) Bit16u cylinder; Bit16u hd_heads; Bit16u head; Bit16u hd_sectors; Bit16u sector; Bit16u dl; { ASM_START push bp mov bp, sp push eax push ebx push edx xor eax,eax mov ax,4[bp] // cylinder xor ebx,ebx mov bl,6[bp] // hd_heads imul ebx mov bl,8[bp] // head add eax,ebx mov bl,10[bp] // hd_sectors imul ebx mov bl,12[bp] // sector add eax,ebx dec eax mov dx,#0x1f3 out dx,al mov dx,#0x1f4 mov al,ah out dx,al shr eax,#16 mov dx,#0x1f5 out dx,al and ah,#0xf mov bl,14[bp] // dl and bl,#1 shl bl,#4 or ah,bl or ah,#0xe0 mov al,ah mov dx,#0x01f6 out dx,al pop edx pop ebx pop eax pop bp ASM_END } void int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; { Bit8u drive, num_sectors, sector, head, status, mod; Bit8u drive_map; Bit8u n_drives; Bit16u cyl_mod, ax; Bit16u max_cylinder, cylinder, total_sectors; Bit16u hd_cylinders; Bit8u hd_heads, hd_sectors; Bit16u val16; Bit8u sector_count; unsigned int i; Bit16u tempbx; Bit16u dpsize; Bit16u count, segment, offset; Bit32u lba; Bit16u error; BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); write_byte(0x0040, 0x008e, 0); // clear completion flag /* at this point, DL is >= 0x80 to be passed from the floppy int13h handler code */ /* check how many disks first (cmos reg 0x12), return an error if drive not present */ drive_map = inb_cmos(0x12); drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) | (((drive_map & 0x0f)==0) ? 0 : 2); n_drives = (drive_map==0) ? 0 : ((drive_map==3) ? 2 : 1); if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */ SET_AH(0x01); SET_DISK_RET_STATUS(0x01); SET_CF(); /* error occurred */ return; } switch (GET_AH()) { case 0x00: /* disk controller reset */ BX_DEBUG_INT13_HD("int13_f00\n"); SET_AH(0); SET_DISK_RET_STATUS(0); set_diskette_ret_status(0); set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */ set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */ CLEAR_CF(); /* successful */ return; break; case 0x01: /* read disk status */ BX_DEBUG_INT13_HD("int13_f01\n"); status = read_byte(0x0040, 0x0074); SET_AH(status); SET_DISK_RET_STATUS(0); /* set CF if error status read */ if (status) SET_CF(); else CLEAR_CF(); return; break; case 0x04: // verify disk sectors case 0x02: // read disk sectors drive = GET_ELDL(); get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors); num_sectors = GET_AL(); cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH(); sector = (GET_CL() & 0x3f); head = GET_DH(); if (hd_cylinders > 1024) { if (hd_cylinders <= 2048) { cylinder <<= 1; } else if (hd_cylinders <= 4096) { cylinder <<= 2; } else if (hd_cylinders <= 8192) { cylinder <<= 3; } else { // hd_cylinders <= 16384 cylinder <<= 4; } ax = head / hd_heads; cyl_mod = ax & 0xff; head = ax >> 8; cylinder |= cyl_mod; } if ( (cylinder >= hd_cylinders) || (sector > hd_sectors) || (head >= hd_heads) ) { SET_AH(1); SET_DISK_RET_STATUS(1); SET_CF(); /* error occurred */ return; } if ( (num_sectors > 128) || (num_sectors == 0) ) BX_PANIC("int13_harddisk: num_sectors out of range!\n"); if (head > 15) BX_PANIC("hard drive BIOS:(read/verify) head > 15\n"); if ( GET_AH() == 0x04 ) { SET_AH(0); SET_DISK_RET_STATUS(0); CLEAR_CF(); return; } status = inb(0x1f7); if (status & 0x80) { BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n"); } outb(0x01f2, num_sectors); /* activate LBA? (tomv) */ if (hd_heads > 16) { BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector); outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive); } else { outb(0x01f3, sector); outb(0x01f4, cylinder & 0x00ff); outb(0x01f5, cylinder >> 8); outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f)); } outb(0x01f7, 0x20); while (1) { status = inb(0x1f7); if ( !(status & 0x80) ) break; } if (status & 0x01) { BX_PANIC("hard drive BIOS:(read/verify) read error\n"); } else if ( !(status & 0x08) ) { BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status); BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n"); } sector_count = 0; tempbx = BX; ASM_START sti ;; enable higher priority interrupts ASM_END while (1) { ASM_START ;; store temp bx in real DI register push bp mov bp, sp mov di, _int13_harddisk.tempbx + 2 [bp] pop bp ;; adjust if there will be an overrun cmp di, #0xfe00 jbe i13_f02_no_adjust i13_f02_adjust: sub di, #0x0200 ; sub 512 bytes from offset mov ax, es add ax, #0x0020 ; add 512 to segment mov es, ax i13_f02_no_adjust: mov cx, #0x0100 ;; counter (256 words = 512b) mov dx, #0x01f0 ;; AT data read port rep insw ;; CX words transfered from port(DX) to ES:[DI] i13_f02_done: ;; store real DI register back to temp bx push bp mov bp, sp mov _int13_harddisk.tempbx + 2 [bp], di pop bp ASM_END sector_count++; num_sectors--; if (num_sectors == 0) { status = inb(0x1f7); if ( (status & 0xc9) != 0x40 ) BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status); break; } else { status = inb(0x1f7); if ( (status & 0xc9) != 0x48 ) BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status); continue; } } SET_AH(0); SET_DISK_RET_STATUS(0); SET_AL(sector_count); CLEAR_CF(); /* successful */ return; break; case 0x03: /* write disk sectors */ BX_DEBUG_INT13_HD("int13_f03\n"); drive = GET_ELDL (); get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors); num_sectors = GET_AL(); cylinder = GET_CH(); cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300; sector = (GET_CL() & 0x3f); head = GET_DH(); if (hd_cylinders > 1024) { if (hd_cylinders <= 2048) { cylinder <<= 1; } else if (hd_cylinders <= 4096) { cylinder <<= 2; } else if (hd_cylinders <= 8192) { cylinder <<= 3; } else { // hd_cylinders <= 16384 cylinder <<= 4; } ax = head / hd_heads; cyl_mod = ax & 0xff; head = ax >> 8; cylinder |= cyl_mod; } if ( (cylinder >= hd_cylinders) || (sector > hd_sectors) || (head >= hd_heads) ) { SET_AH( 1); SET_DISK_RET_STATUS(1); SET_CF(); /* error occurred */ return; } if ( (num_sectors > 128) || (num_sectors == 0) ) BX_PANIC("int13_harddisk: num_sectors out of range!\n"); if (head > 15) BX_PANIC("hard drive BIOS:(read) head > 15\n"); status = inb(0x1f7); if (status & 0x80) { BX_PANIC("hard drive BIOS:(read) BUSY bit set\n"); } // should check for Drive Ready Bit also in status reg outb(0x01f2, num_sectors); /* activate LBA? (tomv) */ if (hd_heads > 16) { BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector); outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL()); } else { outb(0x01f3, sector); outb(0x01f4, cylinder & 0x00ff); outb(0x01f5, cylinder >> 8); outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f)); } outb(0x01f7, 0x30); // wait for busy bit to turn off after seeking while (1) { status = inb(0x1f7); if ( !(status & 0x80) ) break; } if ( !(status & 0x08) ) { BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status); BX_PANIC("hard drive BIOS:(write) data-request bit not set\n"); } sector_count = 0; tempbx = BX; ASM_START sti ;; enable higher priority interrupts ASM_END while (1) { ASM_START ;; store temp bx in real SI register push bp mov bp, sp mov si, _int13_harddisk.tempbx + 2 [bp] pop bp ;; adjust if there will be an overrun cmp si, #0xfe00 jbe i13_f03_no_adjust i13_f03_adjust: sub si, #0x0200 ; sub 512 bytes from offset mov ax, es add ax, #0x0020 ; add 512 to segment mov es, ax i13_f03_no_adjust: mov cx, #0x0100 ;; counter (256 words = 512b) mov dx, #0x01f0 ;; AT data read port seg ES rep outsw ;; CX words tranfered from ES:[SI] to port(DX) ;; store real SI register back to temp bx push bp mov bp, sp mov _int13_harddisk.tempbx + 2 [bp], si pop bp ASM_END sector_count++; num_sectors--; if (num_sectors == 0) { status = inb(0x1f7); if ( (status & 0xe9) != 0x40 ) BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status); break; } else { status = inb(0x1f7); if ( (status & 0xc9) != 0x48 ) BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status); continue; } } SET_AH(0); SET_DISK_RET_STATUS(0); SET_AL(sector_count); CLEAR_CF(); /* successful */ return; break; case 0x05: /* format disk track */ BX_DEBUG_INT13_HD("int13_f05\n"); BX_PANIC("format disk track called\n"); /* nop */ SET_AH(0); SET_DISK_RET_STATUS(0); CLEAR_CF(); /* successful */ return; break; case 0x08: /* read disk drive parameters */ BX_DEBUG_INT13_HD("int13_f08\n"); drive = GET_ELDL (); get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors); // translate CHS // if (hd_cylinders <= 1024) { // hd_cylinders >>= 0; // hd_heads <<= 0; } else if (hd_cylinders <= 2048) { hd_cylinders >>= 1; hd_heads <<= 1; } else if (hd_cylinders <= 4096) { hd_cylinders >>= 2; hd_heads <<= 2; } else if (hd_cylinders <= 8192) { hd_cylinders >>= 3; hd_heads <<= 3; } else { // hd_cylinders <= 16384 hd_cylinders >>= 4; hd_heads <<= 4; } max_cylinder = hd_cylinders - 2; /* 0 based */ SET_AL(0); SET_CH(max_cylinder & 0xff); SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f)); SET_DH(hd_heads - 1); SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */ SET_AH(0); SET_DISK_RET_STATUS(0); CLEAR_CF(); /* successful */ return; break; case 0x09: /* initialize drive parameters */ BX_DEBUG_INT13_HD("int13_f09\n"); SET_AH(0); SET_DISK_RET_STATUS(0); CLEAR_CF(); /* successful */ return; break; case 0x0a: /* read disk sectors with ECC */ BX_DEBUG_INT13_HD("int13_f0a\n"); case 0x0b: /* write disk sectors with ECC */ BX_DEBUG_INT13_HD("int13_f0b\n"); BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n"); return; break; case 0x0c: /* seek to specified cylinder */ BX_DEBUG_INT13_HD("int13_f0c\n"); BX_INFO("int13h function 0ch (seek) not implemented!\n"); SET_AH(0); SET_DISK_RET_STATUS(0); CLEAR_CF(); /* successful */ return; break; case 0x0d: /* alternate disk reset */ BX_DEBUG_INT13_HD("int13_f0d\n"); SET_AH(0); SET_DISK_RET_STATUS(0); CLEAR_CF(); /* successful */ return; break; case 0x10: /* check drive ready */ BX_DEBUG_INT13_HD("int13_f10\n"); //SET_AH(0); //SET_DISK_RET_STATUS(0); //CLEAR_CF(); /* successful */ //return; //break; // should look at 40:8E also??? status = inb(0x01f7); if ( (status & 0xc0) == 0x40 ) { SET_AH(0); SET_DISK_RET_STATUS(0); CLEAR_CF(); // drive ready return; } else { SET_AH(0xAA); SET_DISK_RET_STATUS(0xAA); SET_CF(); // not ready return; } break; case 0x11: /* recalibrate */ BX_DEBUG_INT13_HD("int13_f11\n"); SET_AH(0); SET_DISK_RET_STATUS(0); CLEAR_CF(); /* successful */ return; break; case 0x14: /* controller internal diagnostic */ BX_DEBUG_INT13_HD("int13_f14\n"); SET_AH(0); SET_DISK_RET_STATUS(0); CLEAR_CF(); /* successful */ SET_AL(0); return; break; case 0x15: /* read disk drive size */ drive = GET_ELDL(); get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors); ASM_START push bp mov bp, sp mov al, _int13_harddisk.hd_heads + 2 [bp] mov ah, _int13_harddisk.hd_sectors + 2 [bp] mul al, ah ;; ax = heads * sectors mov bx, _int13_harddisk.hd_cylinders + 2 [bp] dec bx ;; use (cylinders - 1) ??? mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors) ;; now we need to move the 32bit result dx:ax to what the ;; BIOS wants which is cx:dx. ;; and then into CX:DX on the stack mov _int13_harddisk.CX + 2 [bp], dx mov _int13_harddisk.DX + 2 [bp], ax pop bp ASM_END SET_AH(3); // hard disk accessible SET_DISK_RET_STATUS(0); // ??? should this be 0 CLEAR_CF(); // successful return; break; case 0x18: // set media type for format case 0x41: // IBM/MS case 0x42: // IBM/MS case 0x43: // IBM/MS case 0x44: // IBM/MS case 0x45: // IBM/MS lock/unlock drive case 0x46: // IBM/MS eject media case 0x47: // IBM/MS extended seek case 0x49: // IBM/MS extended media change case 0x50: // IBM/MS send packet command default: BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH()); SET_AH(1); // code=invalid function in AH or invalid parameter SET_DISK_RET_STATUS(1); SET_CF(); /* unsuccessful */ return; break; } } static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n"; static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n"; void get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors) Bit8u drive; Bit16u *hd_cylinders; Bit8u *hd_heads; Bit8u *hd_sectors; { Bit8u hd_type; Bit16u ss; Bit16u cylinders; Bit8u iobase; ss = get_SS(); if (drive == 0x80) { hd_type = inb_cmos(0x12) & 0xf0; if (hd_type != 0xf0) BX_INFO(panic_msg_reg12h,0); hd_type = inb_cmos(0x19); // HD0: extended type if (hd_type != 47) BX_INFO(panic_msg_reg19h,0,0x19); iobase = 0x1b; } else { hd_type = inb_cmos(0x12) & 0x0f; if (hd_type != 0x0f) BX_INFO(panic_msg_reg12h,1); hd_type = inb_cmos(0x1a); // HD1: extended type if (hd_type != 47) BX_INFO(panic_msg_reg19h,0,0x1a); iobase = 0x24; } // cylinders cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8); write_word(ss, hd_cylinders, cylinders); // heads write_byte(ss, hd_heads, inb_cmos(iobase+2)); // sectors per track write_byte(ss, hd_sectors, inb_cmos(iobase+8)); } #endif //else BX_USE_ATADRV #if BX_SUPPORT_FLOPPY ////////////////////// // FLOPPY functions // ////////////////////// void floppy_reset_controller() { Bit8u val8; // Reset controller val8 = inb(0x03f2); outb(0x03f2, val8 & ~0x04); outb(0x03f2, val8 | 0x04); // Wait for controller to come out of reset do { val8 = inb(0x3f4); } while ( (val8 & 0xc0) != 0x80 ); } void floppy_prepare_controller(drive) Bit16u drive; { Bit8u val8, dor, prev_reset; // set 40:3e bit 7 to 0 val8 = read_byte(0x0040, 0x003e); val8 &= 0x7f; write_byte(0x0040, 0x003e, val8); // turn on motor of selected drive, DMA & int enabled, normal operation prev_reset = inb(0x03f2) & 0x04; if (drive) dor = 0x20; else dor = 0x10; dor |= 0x0c; dor |= drive; outb(0x03f2, dor); // reset the disk motor timeout value of INT 08 write_byte(0x40,0x40, BX_FLOPPY_ON_CNT); // wait for drive readiness do { val8 = inb(0x3f4); } while ( (val8 & 0xc0) != 0x80 ); if (prev_reset == 0) { // turn on interrupts ASM_START sti ASM_END // wait on 40:3e bit 7 to become 1 do { val8 = read_byte(0x0040, 0x003e); } while ( (val8 & 0x80) == 0 ); val8 &= 0x7f; ASM_START cli ASM_END write_byte(0x0040, 0x003e, val8); } } bx_bool floppy_media_known(drive) Bit16u drive; { Bit8u val8; Bit16u media_state_offset; val8 = read_byte(0x0040, 0x003e); // diskette recal status if (drive) val8 >>= 1; val8 &= 0x01; if (val8 == 0) return(0); media_state_offset = 0x0090; if (drive) media_state_offset += 1; val8 = read_byte(0x0040, media_state_offset); val8 = (val8 >> 4) & 0x01; if (val8 == 0) return(0); // check pass, return KNOWN return(1); } bx_bool floppy_media_sense(drive) Bit16u drive; { bx_bool retval; Bit16u media_state_offset; Bit8u drive_type, config_data, media_state; if (floppy_drive_recal(drive) == 0) { return(0); } // for now cheat and get drive type from CMOS, // assume media is same as drive type // ** config_data ** // Bitfields for diskette media control: // Bit(s) Description (Table M0028) // 7-6 last data rate set by controller // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps // 5-4 last diskette drive step rate selected // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah // 3-2 {data rate at start of operation} // 1-0 reserved // ** media_state ** // Bitfields for diskette drive media state: // Bit(s) Description (Table M0030) // 7-6 data rate // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps // 5 double stepping required (e.g. 360kB in 1.2MB) // 4 media type established // 3 drive capable of supporting 4MB media // 2-0 on exit from BIOS, contains // 000 trying 360kB in 360kB // 001 trying 360kB in 1.2MB // 010 trying 1.2MB in 1.2MB // 011 360kB in 360kB established // 100 360kB in 1.2MB established // 101 1.2MB in 1.2MB established // 110 reserved // 111 all other formats/drives drive_type = inb_cmos(0x10); if (drive == 0) drive_type >>= 4; else drive_type &= 0x0f; if ( drive_type == 1 ) { // 360K 5.25" drive config_data = 0x00; // 0000 0000 media_state = 0x25; // 0010 0101 retval = 1; } else if ( drive_type == 2 ) { // 1.2 MB 5.25" drive config_data = 0x00; // 0000 0000 media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5) retval = 1; } else if ( drive_type == 3 ) { // 720K 3.5" drive config_data = 0x00; // 0000 0000 ??? media_state = 0x17; // 0001 0111 retval = 1; } else if ( drive_type == 4 ) { // 1.44 MB 3.5" drive config_data = 0x00; // 0000 0000 media_state = 0x17; // 0001 0111 retval = 1; } else if ( drive_type == 5 ) { // 2.88 MB 3.5" drive config_data = 0xCC; // 1100 1100 media_state = 0xD7; // 1101 0111 retval = 1; } // // Extended floppy size uses special cmos setting else if ( drive_type == 6 ) { // 160k 5.25" drive config_data = 0x00; // 0000 0000 media_state = 0x27; // 0010 0111 retval = 1; } else if ( drive_type == 7 ) { // 180k 5.25" drive config_data = 0x00; // 0000 0000 media_state = 0x27; // 0010 0111 retval = 1; } else if ( drive_type == 8 ) { // 320k 5.25" drive config_data = 0x00; // 0000 0000 media_state = 0x27; // 0010 0111 retval = 1; } else { // not recognized config_data = 0x00; // 0000 0000 media_state = 0x00; // 0000 0000 retval = 0; } if (drive == 0) media_state_offset = 0x90; else media_state_offset = 0x91; write_byte(0x0040, 0x008B, config_data); write_byte(0x0040, media_state_offset, media_state); return(retval); } bx_bool floppy_drive_recal(drive) Bit16u drive; { Bit8u val8; Bit16u curr_cyl_offset; floppy_prepare_controller(drive); // send Recalibrate command (2 bytes) to controller outb(0x03f5, 0x07); // 07: Recalibrate outb(0x03f5, drive); // 0=drive0, 1=drive1 // turn on interrupts ASM_START sti ASM_END // wait on 40:3e bit 7 to become 1 do { val8 = (read_byte(0x0040, 0x003e) & 0x80); } while ( val8 == 0 ); val8 = 0; // separate asm from while() loop // turn off interrupts ASM_START cli ASM_END // set 40:3e bit 7 to 0, and calibrated bit val8 = read_byte(0x0040, 0x003e); val8 &= 0x7f; if (drive) { val8 |= 0x02; // Drive 1 calibrated curr_cyl_offset = 0x0095; } else { val8 |= 0x01; // Drive 0 calibrated curr_cyl_offset = 0x0094; } write_byte(0x0040, 0x003e, val8); write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0 return(1); } bx_bool floppy_drive_exists(drive) Bit16u drive; { Bit8u drive_type; // check CMOS to see if drive exists drive_type = inb_cmos(0x10); if (drive == 0) drive_type >>= 4; else drive_type &= 0x0f; if ( drive_type == 0 ) return(0); else return(1); } void int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; { Bit8u drive, num_sectors, track, sector, head, status; Bit16u base_address, base_count, base_es; Bit8u page, mode_register, val8, dor; Bit8u return_status[7]; Bit8u drive_type, num_floppies, ah; Bit16u es, last_addr; BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES); ah = GET_AH(); switch ( ah ) { case 0x00: // diskette controller reset BX_DEBUG_INT13_FL("floppy f00\n"); drive = GET_ELDL(); if (drive > 1) { SET_AH(1); // invalid param set_diskette_ret_status(1); SET_CF(); return; } drive_type = inb_cmos(0x10); if (drive == 0) drive_type >>= 4; else drive_type &= 0x0f; if (drive_type == 0) { SET_AH(0x80); // drive not responding set_diskette_ret_status(0x80); SET_CF(); return; } SET_AH(0); set_diskette_ret_status(0); CLEAR_CF(); // successful set_diskette_current_cyl(drive, 0); // current cylinder return; case 0x01: // Read Diskette Status CLEAR_CF(); val8 = read_byte(0x0000, 0x0441); SET_AH(val8); if (val8) { SET_CF(); } return; case 0x02: // Read Diskette Sectors case 0x03: // Write Diskette Sectors case 0x04: // Verify Diskette Sectors num_sectors = GET_AL(); track = GET_CH(); sector = GET_CL(); head = GET_DH(); drive = GET_ELDL(); if ((drive > 1) || (head > 1) || (sector == 0) || (num_sectors == 0) || (num_sectors > 72)) { BX_INFO("int13_diskette: read/write/verify: parameter out of range\n"); SET_AH(1); set_diskette_ret_status(1); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } // see if drive exists if (floppy_drive_exists(drive) == 0) { SET_AH(0x80); // not responding set_diskette_ret_status(0x80); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } // see if media in drive, and type is known if (floppy_media_known(drive) == 0) { if (floppy_media_sense(drive) == 0) { SET_AH(0x0C); // Media type not found set_diskette_ret_status(0x0C); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } } if (ah == 0x02) { // Read Diskette Sectors //----------------------------------- // set up DMA controller for transfer //----------------------------------- // es:bx = pointer to where to place information from diskette // port 04: DMA-1 base and current address, channel 2 // port 05: DMA-1 base and current count, channel 2 page = (ES >> 12); // upper 4 bits base_es = (ES << 4); // lower 16bits contributed by ES base_address = base_es + BX; // lower 16 bits of address // contributed by ES:BX if ( base_address < base_es ) { // in case of carry, adjust page by 1 page++; } base_count = (num_sectors * 512) - 1; // check for 64K boundary overrun last_addr = base_address + base_count; if (last_addr < base_address) { SET_AH(0x09); set_diskette_ret_status(0x09); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } BX_DEBUG_INT13_FL("masking DMA-1 c2\n"); outb(0x000a, 0x06); BX_DEBUG_INT13_FL("clear flip-flop\n"); outb(0x000c, 0x00); // clear flip-flop outb(0x0004, base_address); outb(0x0004, base_address>>8); BX_DEBUG_INT13_FL("clear flip-flop\n"); outb(0x000c, 0x00); // clear flip-flop outb(0x0005, base_count); outb(0x0005, base_count>>8); // port 0b: DMA-1 Mode Register mode_register = 0x46; // single mode, increment, autoinit disable, // transfer type=write, channel 2 BX_DEBUG_INT13_FL("setting mode register\n"); outb(0x000b, mode_register); BX_DEBUG_INT13_FL("setting page register\n"); // port 81: DMA-1 Page Register, channel 2 outb(0x0081, page); BX_DEBUG_INT13_FL("unmask chan 2\n"); outb(0x000a, 0x02); // unmask channel 2 BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n"); outb(0x000a, 0x02); //-------------------------------------- // set up floppy controller for transfer //-------------------------------------- floppy_prepare_controller(drive); // send read-normal-data command (9 bytes) to controller outb(0x03f5, 0xe6); // e6: read normal data outb(0x03f5, (head << 2) | drive); // HD DR1 DR2 outb(0x03f5, track); outb(0x03f5, head); outb(0x03f5, sector); outb(0x03f5, 2); // 512 byte sector size outb(0x03f5, sector + num_sectors - 1); // last sector to read on track outb(0x03f5, 0); // Gap length outb(0x03f5, 0xff); // Gap length // turn on interrupts ASM_START sti ASM_END // wait on 40:3e bit 7 to become 1 do { val8 = read_byte(0x0040, 0x0040); if (val8 == 0) { floppy_reset_controller(); SET_AH(0x80); // drive not ready (timeout) set_diskette_ret_status(0x80); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } val8 = (read_byte(0x0040, 0x003e) & 0x80); } while ( val8 == 0 ); val8 = 0; // separate asm from while() loop // turn off interrupts ASM_START cli ASM_END // set 40:3e bit 7 to 0 val8 = read_byte(0x0040, 0x003e); val8 &= 0x7f; write_byte(0x0040, 0x003e, val8); // check port 3f4 for accessibility to status bytes val8 = inb(0x3f4); if ( (val8 & 0xc0) != 0xc0 ) BX_PANIC("int13_diskette: ctrl not ready\n"); // read 7 return status bytes from controller // using loop index broken, have to unroll... return_status[0] = inb(0x3f5); return_status[1] = inb(0x3f5); return_status[2] = inb(0x3f5); return_status[3] = inb(0x3f5); return_status[4] = inb(0x3f5); return_status[5] = inb(0x3f5); return_status[6] = inb(0x3f5); // record in BIOS Data Area write_byte(0x0040, 0x0042, return_status[0]); write_byte(0x0040, 0x0043, return_status[1]); write_byte(0x0040, 0x0044, return_status[2]); write_byte(0x0040, 0x0045, return_status[3]); write_byte(0x0040, 0x0046, return_status[4]); write_byte(0x0040, 0x0047, return_status[5]); write_byte(0x0040, 0x0048, return_status[6]); if ( (return_status[0] & 0xc0) != 0 ) { SET_AH(0x20); set_diskette_ret_status(0x20); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } // ??? should track be new val from return_status[3] ? set_diskette_current_cyl(drive, track); // AL = number of sectors read (same value as passed) SET_AH(0x00); // success CLEAR_CF(); // success return; } else if (ah == 0x03) { // Write Diskette Sectors //----------------------------------- // set up DMA controller for transfer //----------------------------------- // es:bx = pointer to where to place information from diskette // port 04: DMA-1 base and current address, channel 2 // port 05: DMA-1 base and current count, channel 2 page = (ES >> 12); // upper 4 bits base_es = (ES << 4); // lower 16bits contributed by ES base_address = base_es + BX; // lower 16 bits of address // contributed by ES:BX if ( base_address < base_es ) { // in case of carry, adjust page by 1 page++; } base_count = (num_sectors * 512) - 1; // check for 64K boundary overrun last_addr = base_address + base_count; if (last_addr < base_address) { SET_AH(0x09); set_diskette_ret_status(0x09); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } BX_DEBUG_INT13_FL("masking DMA-1 c2\n"); outb(0x000a, 0x06); outb(0x000c, 0x00); // clear flip-flop outb(0x0004, base_address); outb(0x0004, base_address>>8); outb(0x000c, 0x00); // clear flip-flop outb(0x0005, base_count); outb(0x0005, base_count>>8); // port 0b: DMA-1 Mode Register mode_register = 0x4a; // single mode, increment, autoinit disable, // transfer type=read, channel 2 outb(0x000b, mode_register); // port 81: DMA-1 Page Register, channel 2 outb(0x0081, page); BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n"); outb(0x000a, 0x02); //-------------------------------------- // set up floppy controller for transfer //-------------------------------------- floppy_prepare_controller(drive); // send write-normal-data command (9 bytes) to controller outb(0x03f5, 0xc5); // c5: write normal data outb(0x03f5, (head << 2) | drive); // HD DR1 DR2 outb(0x03f5, track); outb(0x03f5, head); outb(0x03f5, sector); outb(0x03f5, 2); // 512 byte sector size outb(0x03f5, sector + num_sectors - 1); // last sector to write on track outb(0x03f5, 0); // Gap length outb(0x03f5, 0xff); // Gap length // turn on interrupts ASM_START sti ASM_END // wait on 40:3e bit 7 to become 1 do { val8 = read_byte(0x0040, 0x0040); if (val8 == 0) { floppy_reset_controller(); SET_AH(0x80); // drive not ready (timeout) set_diskette_ret_status(0x80); SET_AL(0); // no sectors written SET_CF(); // error occurred return; } val8 = (read_byte(0x0040, 0x003e) & 0x80); } while ( val8 == 0 ); val8 = 0; // separate asm from while() loop // turn off interrupts ASM_START cli ASM_END // set 40:3e bit 7 to 0 val8 = read_byte(0x0040, 0x003e); val8 &= 0x7f; write_byte(0x0040, 0x003e, val8); // check port 3f4 for accessibility to status bytes val8 = inb(0x3f4); if ( (val8 & 0xc0) != 0xc0 ) BX_PANIC("int13_diskette: ctrl not ready\n"); // read 7 return status bytes from controller // using loop index broken, have to unroll... return_status[0] = inb(0x3f5); return_status[1] = inb(0x3f5); return_status[2] = inb(0x3f5); return_status[3] = inb(0x3f5); return_status[4] = inb(0x3f5); return_status[5] = inb(0x3f5); return_status[6] = inb(0x3f5); // record in BIOS Data Area write_byte(0x0040, 0x0042, return_status[0]); write_byte(0x0040, 0x0043, return_status[1]); write_byte(0x0040, 0x0044, return_status[2]); write_byte(0x0040, 0x0045, return_status[3]); write_byte(0x0040, 0x0046, return_status[4]); write_byte(0x0040, 0x0047, return_status[5]); write_byte(0x0040, 0x0048, return_status[6]); if ( (return_status[0] & 0xc0) != 0 ) { if ( (return_status[1] & 0x02) != 0 ) { // diskette not writable. // AH=status code=0x03 (tried to write on write-protected disk) // AL=number of sectors written=0 AX = 0x0300; SET_CF(); return; } else { BX_PANIC("int13_diskette_function: read error\n"); } } // ??? should track be new val from return_status[3] ? set_diskette_current_cyl(drive, track); // AL = number of sectors read (same value as passed) SET_AH(0x00); // success CLEAR_CF(); // success return; } else { // if (ah == 0x04) // Verify Diskette Sectors // ??? should track be new val from return_status[3] ? set_diskette_current_cyl(drive, track); // AL = number of sectors verified (same value as passed) CLEAR_CF(); // success SET_AH(0x00); // success return; } break; case 0x05: // format diskette track BX_DEBUG_INT13_FL("floppy f05\n"); num_sectors = GET_AL(); track = GET_CH(); head = GET_DH(); drive = GET_ELDL(); if ((drive > 1) || (head > 1) || (track > 79) || (num_sectors == 0) || (num_sectors > 18)) { SET_AH(1); set_diskette_ret_status(1); SET_CF(); // error occurred } // see if drive exists if (floppy_drive_exists(drive) == 0) { SET_AH(0x80); // drive not responding set_diskette_ret_status(0x80); SET_CF(); // error occurred return; } // see if media in drive, and type is known if (floppy_media_known(drive) == 0) { if (floppy_media_sense(drive) == 0) { SET_AH(0x0C); // Media type not found set_diskette_ret_status(0x0C); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } } // set up DMA controller for transfer page = (ES >> 12); // upper 4 bits base_es = (ES << 4); // lower 16bits contributed by ES base_address = base_es + BX; // lower 16 bits of address // contributed by ES:BX if ( base_address < base_es ) { // in case of carry, adjust page by 1 page++; } base_count = (num_sectors * 4) - 1; // check for 64K boundary overrun last_addr = base_address + base_count; if (last_addr < base_address) { SET_AH(0x09); set_diskette_ret_status(0x09); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } outb(0x000a, 0x06); outb(0x000c, 0x00); // clear flip-flop outb(0x0004, base_address); outb(0x0004, base_address>>8); outb(0x000c, 0x00); // clear flip-flop outb(0x0005, base_count); outb(0x0005, base_count>>8); mode_register = 0x4a; // single mode, increment, autoinit disable, // transfer type=read, channel 2 outb(0x000b, mode_register); // port 81: DMA-1 Page Register, channel 2 outb(0x0081, page); outb(0x000a, 0x02); // set up floppy controller for transfer floppy_prepare_controller(drive); // send format-track command (6 bytes) to controller outb(0x03f5, 0x4d); // 4d: format track outb(0x03f5, (head << 2) | drive); // HD DR1 DR2 outb(0x03f5, 2); // 512 byte sector size outb(0x03f5, num_sectors); // number of sectors per track outb(0x03f5, 0); // Gap length outb(0x03f5, 0xf6); // Fill byte // turn on interrupts ASM_START sti ASM_END // wait on 40:3e bit 7 to become 1 do { val8 = read_byte(0x0040, 0x0040); if (val8 == 0) { floppy_reset_controller(); SET_AH(0x80); // drive not ready (timeout) set_diskette_ret_status(0x80); SET_CF(); // error occurred return; } val8 = (read_byte(0x0040, 0x003e) & 0x80); } while ( val8 == 0 ); val8 = 0; // separate asm from while() loop // turn off interrupts ASM_START cli ASM_END // set 40:3e bit 7 to 0 val8 = read_byte(0x0040, 0x003e); val8 &= 0x7f; write_byte(0x0040, 0x003e, val8); // check port 3f4 for accessibility to status bytes val8 = inb(0x3f4); if ( (val8 & 0xc0) != 0xc0 ) BX_PANIC("int13_diskette: ctrl not ready\n"); // read 7 return status bytes from controller // using loop index broken, have to unroll... return_status[0] = inb(0x3f5); return_status[1] = inb(0x3f5); return_status[2] = inb(0x3f5); return_status[3] = inb(0x3f5); return_status[4] = inb(0x3f5); return_status[5] = inb(0x3f5); return_status[6] = inb(0x3f5); // record in BIOS Data Area write_byte(0x0040, 0x0042, return_status[0]); write_byte(0x0040, 0x0043, return_status[1]); write_byte(0x0040, 0x0044, return_status[2]); write_byte(0x0040, 0x0045, return_status[3]); write_byte(0x0040, 0x0046, return_status[4]); write_byte(0x0040, 0x0047, return_status[5]); write_byte(0x0040, 0x0048, return_status[6]); if ( (return_status[0] & 0xc0) != 0 ) { if ( (return_status[1] & 0x02) != 0 ) { // diskette not writable. // AH=status code=0x03 (tried to write on write-protected disk) // AL=number of sectors written=0 AX = 0x0300; SET_CF(); return; } else { BX_PANIC("int13_diskette_function: write error\n"); } } SET_AH(0); set_diskette_ret_status(0); set_diskette_current_cyl(drive, 0); CLEAR_CF(); // successful return; case 0x08: // read diskette drive parameters BX_DEBUG_INT13_FL("floppy f08\n"); drive = GET_ELDL(); if (drive > 1) { AX = 0; BX = 0; CX = 0; DX = 0; ES = 0; DI = 0; SET_DL(num_floppies); SET_CF(); return; } drive_type = inb_cmos(0x10); num_floppies = 0; if (drive_type & 0xf0) num_floppies++; if (drive_type & 0x0f) num_floppies++; if (drive == 0) drive_type >>= 4; else drive_type &= 0x0f; SET_BH(0); SET_BL(drive_type); SET_AH(0); SET_AL(0); SET_DL(num_floppies); switch (drive_type) { case 0: // none CX = 0; SET_DH(0); // max head # break; case 1: // 360KB, 5.25" CX = 0x2709; // 40 tracks, 9 sectors SET_DH(1); // max head # break; case 2: // 1.2MB, 5.25" CX = 0x4f0f; // 80 tracks, 15 sectors SET_DH(1); // max head # break; case 3: // 720KB, 3.5" CX = 0x4f09; // 80 tracks, 9 sectors SET_DH(1); // max head # break; case 4: // 1.44MB, 3.5" CX = 0x4f12; // 80 tracks, 18 sectors SET_DH(1); // max head # break; case 5: // 2.88MB, 3.5" CX = 0x4f24; // 80 tracks, 36 sectors SET_DH(1); // max head # break; case 6: // 160k, 5.25" CX = 0x2708; // 40 tracks, 8 sectors SET_DH(0); // max head # break; case 7: // 180k, 5.25" CX = 0x2709; // 40 tracks, 9 sectors SET_DH(0); // max head # break; case 8: // 320k, 5.25" CX = 0x2708; // 40 tracks, 8 sectors SET_DH(1); // max head # break; default: // ? BX_PANIC("floppy: int13: bad floppy type\n"); } /* set es & di to point to 11 byte diskette param table in ROM */ ASM_START push bp mov bp, sp mov ax, #diskette_param_table2 mov _int13_diskette_function.DI+2[bp], ax mov _int13_diskette_function.ES+2[bp], cs pop bp ASM_END CLEAR_CF(); // success /* disk status not changed upon success */ return; case 0x15: // read diskette drive type BX_DEBUG_INT13_FL("floppy f15\n"); drive = GET_ELDL(); if (drive > 1) { SET_AH(0); // only 2 drives supported // set_diskette_ret_status here ??? SET_CF(); return; } drive_type = inb_cmos(0x10); if (drive == 0) drive_type >>= 4; else drive_type &= 0x0f; CLEAR_CF(); // successful, not present if (drive_type==0) { SET_AH(0); // drive not present } else { SET_AH(1); // drive present, does not support change line } return; case 0x16: // get diskette change line status BX_DEBUG_INT13_FL("floppy f16\n"); drive = GET_ELDL(); if (drive > 1) { SET_AH(0x01); // invalid drive set_diskette_ret_status(0x01); SET_CF(); return; } SET_AH(0x06); // change line not supported set_diskette_ret_status(0x06); SET_CF(); return; case 0x17: // set diskette type for format(old) BX_DEBUG_INT13_FL("floppy f17\n"); /* not used for 1.44M floppies */ SET_AH(0x01); // not supported set_diskette_ret_status(1); /* not supported */ SET_CF(); return; case 0x18: // set diskette type for format(new) BX_DEBUG_INT13_FL("floppy f18\n"); SET_AH(0x01); // do later set_diskette_ret_status(1); SET_CF(); return; default: BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH()); // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) { SET_AH(0x01); // ??? set_diskette_ret_status(1); SET_CF(); return; // } } } #else // #if BX_SUPPORT_FLOPPY void int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; { Bit8u val8; switch ( GET_AH() ) { case 0x01: // Read Diskette Status CLEAR_CF(); val8 = read_byte(0x0000, 0x0441); SET_AH(val8); if (val8) { SET_CF(); } return; default: SET_CF(); write_byte(0x0000, 0x0441, 0x01); SET_AH(0x01); } } #endif // #if BX_SUPPORT_FLOPPY void set_diskette_ret_status(value) Bit8u value; { write_byte(0x0040, 0x0041, value); } void set_diskette_current_cyl(drive, cyl) Bit8u drive; Bit8u cyl; { if (drive > 1) BX_PANIC("set_diskette_current_cyl(): drive > 1\n"); write_byte(0x0040, 0x0094+drive, cyl); } void determine_floppy_media(drive) Bit16u drive; { #if 0 Bit8u val8, DOR, ctrl_info; ctrl_info = read_byte(0x0040, 0x008F); if (drive==1) ctrl_info >>= 4; else ctrl_info &= 0x0f; #if 0 if (drive == 0) { DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0 } else { DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1 } #endif if ( (ctrl_info & 0x04) != 0x04 ) { // Drive not determined means no drive exists, done. return; } #if 0 // check Main Status Register for readiness val8 = inb(0x03f4) & 0x80; // Main Status Register if (val8 != 0x80) BX_PANIC("d_f_m: MRQ bit not set\n"); // change line // existing BDA values // turn on drive motor outb(0x03f2, DOR); // Digital Output Register // #endif BX_PANIC("d_f_m: OK so far\n"); #endif } void int17_function(regs, ds, iret_addr) pusha_regs_t regs; // regs pushed from PUSHA instruction Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call { Bit16u addr,timeout; Bit8u val8; ASM_START sti ASM_END addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8); if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) { timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8; if (regs.u.r8.ah == 0) { outb(addr, regs.u.r8.al); val8 = inb(addr+2); outb(addr+2, val8 | 0x01); // send strobe ASM_START nop ASM_END outb(addr+2, val8 & ~0x01); while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) { timeout--; } } if (regs.u.r8.ah == 1) { val8 = inb(addr+2); outb(addr+2, val8 & ~0x04); // send init ASM_START nop ASM_END outb(addr+2, val8 | 0x04); } val8 = inb(addr+1); regs.u.r8.ah = (val8 ^ 0x48); if (!timeout) regs.u.r8.ah |= 0x01; ClearCF(iret_addr.flags); } else { SetCF(iret_addr.flags); // Unsupported } } void int18_function(seq_nr) Bit16u seq_nr; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u bootdev; Bit8u bootdrv; Bit8u bootchk; Bit16u bootseg; Bit16u bootip; Bit16u status; Bit16u bootfirst; ipl_entry_t e; // if BX_ELTORITO_BOOT is not defined, old behavior // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:) // 0: system boot sequence, first drive C: then A: // 1: system boot sequence, first drive A: then C: // else BX_ELTORITO_BOOT is defined // CMOS regs 0x3D and 0x38 contain the boot sequence: // CMOS reg 0x3D & 0x0f : 1st boot device // CMOS reg 0x3D & 0xf0 : 2nd boot device // CMOS reg 0x38 & 0xf0 : 3rd boot device // boot device codes: // 0x00 : not defined // 0x01 : first floppy // 0x02 : first harddrive // 0x03 : first cdrom // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot) // else : boot failure // Get the boot sequence #if BX_ELTORITO_BOOT bootdev = inb_cmos(0x3d); bootdev |= ((inb_cmos(0x38) & 0xf0) << 4); bootdev >>= 4 * seq_nr; bootdev &= 0xf; /* Read user selected device */ bootfirst = read_word(ebda_seg, IPL_BOOTFIRST_OFFSET); if (bootfirst != 0xFFFF) { bootdev = bootfirst; /* User selected device not set */ write_word(ebda_seg, IPL_BOOTFIRST_OFFSET, 0xFFFF); /* Reset boot sequence */ write_word(ebda_seg, IPL_SEQUENCE_OFFSET, 0xFFFF); } else if (bootdev == 0) { printf("\nNo bootable device.\n"); printf("Powering off in 30 seconds.\n"); ASM_START sti mov cx, #0x01c9 mov dx, #0xc380 mov ah, #0x86 ;; INT 15/86: wait CX:DX usec. int #0x15 ASM_END bios_printf(BIOS_PRINTF_HALT, ""); } /* Translate from CMOS runes to an IPL table offset by subtracting 1 */ bootdev -= 1; #else if (seq_nr ==2) BX_PANIC("No more boot devices."); if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1)) /* Boot from floppy if the bit is set or it's the second boot */ bootdev = 0x00; else bootdev = 0x01; #endif /* Read the boot device from the IPL table */ if (get_boot_vector(bootdev, &e) == 0) { BX_INFO("Invalid boot device (0x%x)\n", bootdev); return; } /* Do the loading, and set up vector as a far pointer to the boot * address, and bootdrv as the boot drive */ print_boot_device(e.type, e.description); switch(e.type) { case IPL_TYPE_FLOPPY: /* FDD */ case IPL_TYPE_HARDDISK: /* HDD */ bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00; bootseg = 0x07c0; status = 0; ASM_START push bp mov bp, sp push ax push bx push cx push dx mov dl, _int18_function.bootdrv + 2[bp] mov ax, _int18_function.bootseg + 2[bp] mov es, ax ;; segment xor bx, bx ;; offset mov ah, #0x02 ;; function 2, read diskette sector mov al, #0x01 ;; read 1 sector mov ch, #0x00 ;; track 0 mov cl, #0x01 ;; sector 1 mov dh, #0x00 ;; head 0 int #0x13 ;; read sector jnc int19_load_done mov ax, #0x0001 mov _int18_function.status + 2[bp], ax int19_load_done: pop dx pop cx pop bx pop ax pop bp ASM_END if (status != 0) { print_boot_failure(e.type, 1); return; } /* Always check the signature on a HDD boot sector; on FDD, only do * the check if the CMOS doesn't tell us to skip it */ if ((e.type != IPL_TYPE_FLOPPY) || !((inb_cmos(0x38) & 0x01))) { if (read_word(bootseg,0x1fe) != 0xaa55) { print_boot_failure(e.type, 0); return; } } #if BX_TCGBIOS tcpa_add_bootdevice((Bit32u)0L, (Bit32u)bootdrv); tcpa_ipl((Bit32u)0L,(Bit32u)bootseg,(Bit32u)0L,(Bit32u)512L); /* specs: 8.2.3 steps 4 and 5 */ #endif /* Canonicalize bootseg:bootip */ bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; break; #if BX_ELTORITO_BOOT case IPL_TYPE_CDROM: /* CD-ROM */ status = cdrom_boot(); // If failure if ( (status & 0x00ff) !=0 ) { print_cdromboot_failure(status); print_boot_failure(e.type, 1); return; } bootdrv = (Bit8u)(status>>8); bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment); /* Canonicalize bootseg:bootip */ bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; break; #endif case IPL_TYPE_BEV: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */ bootseg = e.vector >> 16; bootip = e.vector & 0xffff; break; default: return; } /* Debugging info */ BX_INFO("Booting from %x:%x\n", bootseg, bootip); /* Jump to the boot vector */ ASM_START mov bp, sp push cs push #int18_handler ;; Build an iret stack frame that will take us to the boot vector. ;; iret pops ip, then cs, then flags, so push them in the opposite order. pushf mov ax, _int18_function.bootseg + 0[bp] push ax mov ax, _int18_function.bootip + 0[bp] push ax ;; Set the magic number in ax and the boot drive in dl. mov ax, #0xaa55 mov dl, _int18_function.bootdrv + 0[bp] ;; Zero some of the other registers. xor bx, bx mov ds, bx mov es, bx mov bp, bx ;; Go! iret ASM_END } void int1a_function(regs, ds, iret_addr) pusha_regs_t regs; // regs pushed from PUSHA instruction Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call { Bit8u val8; BX_DEBUG_INT1A("int1a: AX=%04x BX=%04x CX=%04x DX=%04x DS=%04x\n", regs.u.r16.ax, regs.u.r16.bx, regs.u.r16.cx, regs.u.r16.dx, ds); ASM_START sti ASM_END switch (regs.u.r8.ah) { case 0: // get current clock count ASM_START cli ASM_END regs.u.r16.cx = BiosData->ticks_high; regs.u.r16.dx = BiosData->ticks_low; regs.u.r8.al = BiosData->midnight_flag; BiosData->midnight_flag = 0; // reset flag ASM_START sti ASM_END // AH already 0 ClearCF(iret_addr.flags); // OK break; case 1: // Set Current Clock Count ASM_START cli ASM_END BiosData->ticks_high = regs.u.r16.cx; BiosData->ticks_low = regs.u.r16.dx; BiosData->midnight_flag = 0; // reset flag ASM_START sti ASM_END regs.u.r8.ah = 0; ClearCF(iret_addr.flags); // OK break; case 2: // Read CMOS Time if (rtc_updating()) { SetCF(iret_addr.flags); break; } regs.u.r8.dh = inb_cmos(0x00); // Seconds regs.u.r8.cl = inb_cmos(0x02); // Minutes regs.u.r8.ch = inb_cmos(0x04); // Hours regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B regs.u.r8.ah = 0; regs.u.r8.al = regs.u.r8.ch; ClearCF(iret_addr.flags); // OK break; case 3: // Set CMOS Time // Using a debugger, I notice the following masking/setting // of bits in Status Register B, by setting Reg B to // a few values and getting its value after INT 1A was called. // // try#1 try#2 try#3 // before 1111 1101 0111 1101 0000 0000 // after 0110 0010 0110 0010 0000 0010 // // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 // My assumption: RegB = ((RegB & 01100000b) | 00000010b) if (rtc_updating()) { init_rtc(); // fall through as if an update were not in progress } outb_cmos(0x00, regs.u.r8.dh); // Seconds outb_cmos(0x02, regs.u.r8.cl); // Minutes outb_cmos(0x04, regs.u.r8.ch); // Hours // Set Daylight Savings time enabled bit to requested value val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01); // (reg B already selected) outb_cmos(0x0b, val8); regs.u.r8.ah = 0; regs.u.r8.al = val8; // val last written to Reg B ClearCF(iret_addr.flags); // OK break; case 4: // Read CMOS Date regs.u.r8.ah = 0; if (rtc_updating()) { SetCF(iret_addr.flags); break; } regs.u.r8.cl = inb_cmos(0x09); // Year regs.u.r8.dh = inb_cmos(0x08); // Month regs.u.r8.dl = inb_cmos(0x07); // Day of Month regs.u.r8.ch = inb_cmos(0x32); // Century regs.u.r8.al = regs.u.r8.ch; ClearCF(iret_addr.flags); // OK break; case 5: // Set CMOS Date // Using a debugger, I notice the following masking/setting // of bits in Status Register B, by setting Reg B to // a few values and getting its value after INT 1A was called. // // try#1 try#2 try#3 try#4 // before 1111 1101 0111 1101 0000 0010 0000 0000 // after 0110 1101 0111 1101 0000 0010 0000 0000 // // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 // My assumption: RegB = (RegB & 01111111b) if (rtc_updating()) { init_rtc(); SetCF(iret_addr.flags); break; } outb_cmos(0x09, regs.u.r8.cl); // Year outb_cmos(0x08, regs.u.r8.dh); // Month outb_cmos(0x07, regs.u.r8.dl); // Day of Month outb_cmos(0x32, regs.u.r8.ch); // Century val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit outb_cmos(0x0b, val8); regs.u.r8.ah = 0; regs.u.r8.al = val8; // AL = val last written to Reg B ClearCF(iret_addr.flags); // OK break; case 6: // Set Alarm Time in CMOS // Using a debugger, I notice the following masking/setting // of bits in Status Register B, by setting Reg B to // a few values and getting its value after INT 1A was called. // // try#1 try#2 try#3 // before 1101 1111 0101 1111 0000 0000 // after 0110 1111 0111 1111 0010 0000 // // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 // My assumption: RegB = ((RegB & 01111111b) | 00100000b) val8 = inb_cmos(0x0b); // Get Status Reg B regs.u.r16.ax = 0; if (val8 & 0x20) { // Alarm interrupt enabled already SetCF(iret_addr.flags); // Error: alarm in use break; } if (rtc_updating()) { init_rtc(); // fall through as if an update were not in progress } outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm outb_cmos(0x05, regs.u.r8.ch); // Hours alarm outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8 // enable Status Reg B alarm bit, clear halt clock bit outb_cmos(0x0b, (val8 & 0x7f) | 0x20); ClearCF(iret_addr.flags); // OK break; case 7: // Turn off Alarm // Using a debugger, I notice the following masking/setting // of bits in Status Register B, by setting Reg B to // a few values and getting its value after INT 1A was called. // // try#1 try#2 try#3 try#4 // before 1111 1101 0111 1101 0010 0000 0010 0010 // after 0100 0101 0101 0101 0000 0000 0000 0010 // // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 // My assumption: RegB = (RegB & 01010111b) val8 = inb_cmos(0x0b); // Get Status Reg B // clear clock-halt bit, disable alarm bit outb_cmos(0x0b, val8 & 0x57); // disable alarm bit regs.u.r8.ah = 0; regs.u.r8.al = val8; // val last written to Reg B ClearCF(iret_addr.flags); // OK break; #if BX_PCIBIOS case 0xb1: // real mode PCI BIOS functions now handled in assembler code // this C code handles the error code for information only if (regs.u.r8.bl == 0xff) { BX_INFO("PCI BIOS: PCI not present\n"); } else if (regs.u.r8.bl == 0x81) { BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al); } else if (regs.u.r8.bl == 0x83) { BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx); } else if (regs.u.r8.bl == 0x86) { if (regs.u.r8.al == 0x02) { BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si); } else { BX_INFO("no PCI device with class code 0x%02x%04x found at index %d\n", regs.u.r8.cl, regs.u.r16.dx, regs.u.r16.si); } } regs.u.r8.ah = regs.u.r8.bl; SetCF(iret_addr.flags); break; #endif default: SetCF(iret_addr.flags); // Unsupported } } void int70_function(regs, ds, iret_addr) pusha_regs_t regs; // regs pushed from PUSHA instruction Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call { // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes Bit8u registerB = 0, registerC = 0; // Check which modes are enabled and have occurred. registerB = inb_cmos( 0xB ); registerC = inb_cmos( 0xC ); if( ( registerB & 0x60 ) != 0 ) { if( ( registerC & 0x20 ) != 0 ) { // Handle Alarm Interrupt. ASM_START sti int #0x4a cli ASM_END } if( ( registerC & 0x40 ) != 0 ) { // Handle Periodic Interrupt. if( read_byte( 0x40, 0xA0 ) != 0 ) { // Wait Interval (Int 15, AH=83) active. Bit32u time, toggle; time = read_dword( 0x40, 0x9C ); // Time left in microseconds. if( time < 0x3D1 ) { // Done waiting. Bit16u segment, offset; segment = read_word( 0x40, 0x98 ); offset = read_word( 0x40, 0x9A ); write_byte( 0x40, 0xA0, 0 ); // Turn of status byte. outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt. write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte. } else { // Continue waiting. time -= 0x3D1; write_dword( 0x40, 0x9C, time ); } } } } ASM_START call eoi_both_pics ASM_END } ASM_START ;------------------------------------------ ;- INT74h : PS/2 mouse hardware interrupt - ;------------------------------------------ int74_handler: sti pusha push ds ;; save DS push #0x00 ;; placeholder for status push #0x00 ;; placeholder for X push #0x00 ;; placeholder for Y push #0x00 ;; placeholder for Z push #0x00 ;; placeholder for make_far_call boolean call _int74_function pop cx ;; remove make_far_call from stack jcxz int74_done ;; make far call to EBDA:0022 push #0x00 pop ds push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04) pop ds //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00) call far ptr[0x22] int74_done: cli call eoi_both_pics add sp, #8 ;; pop status, x, y, z pop ds ;; restore DS popa iret ;; This will perform an IRET, but will retain value of current CF ;; by altering flags on stack. Better than RETF #02. iret_modify_cf: jc carry_set push bp mov bp, sp and BYTE [bp + 0x06], #0xfe pop bp iret carry_set: push bp mov bp, sp or BYTE [bp + 0x06], #0x01 pop bp iret ;---------------------- ;- INT13h (relocated) - ;---------------------- ; ; int13_relocated is a little bit messed up since I played with it ; I have to rewrite it: ; - call a function that detect which function to call ; - make all called C function get the same parameters list ; int13_relocated: #if BX_ELTORITO_BOOT ;; check for an eltorito function cmp ah,#0x4a jb int13_not_eltorito cmp ah,#0x4d ja int13_not_eltorito pusha push es push ds push ss pop ds push #int13_out jmp _int13_eltorito ;; ELDX not used int13_not_eltorito: push ax push bx push cx push dx ;; check if emulation active call _cdemu_isactive cmp al,#0x00 je int13_cdemu_inactive ;; check if access to the emulated drive call _cdemu_emulated_drive pop dx push dx cmp al,dl ;; int13 on emulated drive jne int13_nocdemu pop dx pop cx pop bx pop ax pusha push es push ds push ss pop ds push #int13_out jmp _int13_cdemu ;; ELDX not used int13_nocdemu: and dl,#0xE0 ;; mask to get device class, including cdroms cmp al,dl ;; al is 0x00 or 0x80 jne int13_cdemu_inactive ;; inactive for device class pop dx pop cx pop bx pop ax push ax push cx push dx push bx dec dl ;; real drive is dl - 1 jmp int13_legacy int13_cdemu_inactive: pop dx pop cx pop bx pop ax #endif // BX_ELTORITO_BOOT int13_noeltorito: push ax push cx push dx push bx int13_legacy: push dx ;; push eltorito value of dx instead of sp push bp push si push di push es push ds push ss pop ds ;; now the 16-bit registers can be restored with: ;; pop ds; pop es; popa; iret ;; arguments passed to functions should be ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS test dl, #0x80 jnz int13_notfloppy push #int13_out jmp _int13_diskette_function int13_notfloppy: #if BX_USE_ATADRV cmp dl, #0xE0 jb int13_notcdrom // ebx is modified: BSD 5.2.1 boot loader problem // someone should figure out which 32 bit register that actually are used shr ebx, #16 push bx call _int13_cdrom pop bx shl ebx, #16 jmp int13_out int13_notcdrom: #endif int13_disk: ;; int13_harddisk modifies high word of EAX shr eax, #16 push ax call _int13_harddisk pop ax shl eax, #16 int13_out: pop ds pop es popa iret ;---------- ;- INT18h - ;---------- int18_handler: ;; Boot Failure recovery: try the next device. ;; Reset SP and SS mov ax, #0x0ffe mov sp, ax mov ax, #0x9e00 mov ss, ax ;; The first time we do this it will have been set to -1 so ;; we will start from device 0. xor ax, ax mov ds, ax mov bx, word ptr [0x40E] ;; EBDA segment mov ds, bx ;; Set segment mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number inc bx ;; ++ mov IPL_SEQUENCE_OFFSET, bx ;; Write it back mov ds, ax ;; and reset the segment to zero. ;; Call the C code for the next boot device push bx call _int18_function ;; Boot failed: invoke the boot recovery function... int #0x18 ;---------- ;- INT19h - ;---------- int19_relocated: ;; Boot function, relocated ;; ;; *** Warning: INT 19h resets the whole machine *** ;; ;; Because PV drivers in HVM guests detach some of the emulated devices, ;; it is not safe to do a soft reboot by just dropping to real mode and ;; invoking INT 19h -- the boot drives might have disappeared! ;; If the user asks for a soft reboot, the only thing we can do is ;; reset the whole machine. When it comes back up, the normal BIOS ;; boot sequence will start, which is more or less the required behaviour. ;; ;; Reset SP and SS mov ax, #0x0ffe mov sp, ax mov ax, #0x9e00 mov ss, ax call _machine_reset ;---------- ;- INT1Ch - ;---------- int1c_handler: ;; User Timer Tick iret ;---------------------- ;- POST: Floppy Drive - ;---------------------- floppy_drive_post: xor ax, ax mov ds, ax mov al, #0x00 mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred mov 0x043f, al ;; diskette motor status: read op, drive0, motors off mov 0x0440, al ;; diskette motor timeout counter: not active mov 0x0441, al ;; diskette controller status return code mov 0x0442, al ;; disk & diskette controller status register 0 mov 0x0443, al ;; diskette controller status register 1 mov 0x0444, al ;; diskette controller status register 2 mov 0x0445, al ;; diskette controller cylinder number mov 0x0446, al ;; diskette controller head number mov 0x0447, al ;; diskette controller sector number mov 0x0448, al ;; diskette controller bytes written mov 0x048b, al ;; diskette configuration data ;; ----------------------------------------------------------------- ;; (048F) diskette controller information ;; mov al, #0x10 ;; get CMOS diskette drive type out 0x70, AL in AL, 0x71 mov ah, al ;; save byte to AH look_drive0: shr al, #4 ;; look at top 4 bits for drive 0 jz f0_missing ;; jump if no drive0 mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line jmp look_drive1 f0_missing: mov bl, #0x00 ;; no drive0 look_drive1: mov al, ah ;; restore from AH and al, #0x0f ;; look at bottom 4 bits for drive 1 jz f1_missing ;; jump if no drive1 or bl, #0x70 ;; drive1 determined, multi-rate, has changed line f1_missing: ;; leave high bits in BL zerod mov 0x048f, bl ;; put new val in BDA (diskette controller information) ;; ----------------------------------------------------------------- mov al, #0x00 mov 0x0490, al ;; diskette 0 media state mov 0x0491, al ;; diskette 1 media state ;; diskette 0,1 operational starting state ;; drive type has not been determined, ;; has no changed detection line mov 0x0492, al mov 0x0493, al mov 0x0494, al ;; diskette 0 current cylinder mov 0x0495, al ;; diskette 1 current cylinder mov al, #0x02 out #0x0a, al ;; clear DMA-1 channel 2 mask bit SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2) SET_INT_VECTOR(0x40, #0xF000, #int13_diskette) SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6 ret ;-------------------- ;- POST: HARD DRIVE - ;-------------------- ; relocated here because the primary POST area isnt big enough. hard_drive_post: // IRQ 14 = INT 76h // INT 76h calls INT 15h function ax=9100 mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14 mov dx, #0x03f6 out dx, al xor ax, ax mov ds, ax mov 0x0474, al /* hard disk status of last operation */ mov 0x0477, al /* hard disk port offset (XT only ???) */ mov 0x048c, al /* hard disk status register */ mov 0x048d, al /* hard disk error register */ mov 0x048e, al /* hard disk task complete flag */ mov al, #0x01 mov 0x0475, al /* hard disk number attached */ mov al, #0xc0 mov 0x0476, al /* hard disk control byte */ SET_INT_VECTOR(0x13, #0xF000, #int13_handler) SET_INT_VECTOR(0x76, #0xF000, #int76_handler) ;; INT 41h: hard disk 0 configuration pointer ;; INT 46h: hard disk 1 configuration pointer SET_INT_VECTOR(0x41, word ptr [0x40E], #0x003D) /* EBDA:003D */ SET_INT_VECTOR(0x46, word ptr [0x40E], #0x004D) /* EBDA:004D */ ;; move disk geometry data from CMOS to EBDA disk parameter table(s) mov al, #0x12 out #0x70, al in al, #0x71 and al, #0xf0 cmp al, #0xf0 je post_d0_extended jmp check_for_hd1 post_d0_extended: mov al, #0x19 out #0x70, al in al, #0x71 cmp al, #47 ;; decimal 47 - user definable je post_d0_type47 HALT(__LINE__) post_d0_type47: ;; CMOS purpose param table offset ;; 1b cylinders low 0 ;; 1c cylinders high 1 ;; 1d heads 2 ;; 1e write pre-comp low 5 ;; 1f write pre-comp high 6 ;; 20 retries/bad map/heads>8 8 ;; 21 landing zone low C ;; 22 landing zone high D ;; 23 sectors/track E xor ax, ax mov ds, ax mov ax, word ptr [0x40E] ;; EBDA segment mov ds, ax ;;; Filling EBDA table for hard disk 0. mov al, #0x1f out #0x70, al in al, #0x71 mov ah, al mov al, #0x1e out #0x70, al in al, #0x71 mov (0x003d + 0x05), ax ;; write precomp word mov al, #0x20 out #0x70, al in al, #0x71 mov (0x003d + 0x08), al ;; drive control byte mov al, #0x22 out #0x70, al in al, #0x71 mov ah, al mov al, #0x21 out #0x70, al in al, #0x71 mov (0x003d + 0x0C), ax ;; landing zone word mov al, #0x1c ;; get cylinders word in AX out #0x70, al in al, #0x71 ;; high byte mov ah, al mov al, #0x1b out #0x70, al in al, #0x71 ;; low byte mov bx, ax ;; BX = cylinders mov al, #0x1d out #0x70, al in al, #0x71 mov cl, al ;; CL = heads mov al, #0x23 out #0x70, al in al, #0x71 mov dl, al ;; DL = sectors cmp bx, #1024 jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS hd0_post_physical_chs: ;; no logical CHS mapping used, just physical CHS ;; use Standard Fixed Disk Parameter Table (FDPT) mov (0x003d + 0x00), bx ;; number of physical cylinders mov (0x003d + 0x02), cl ;; number of physical heads mov (0x003d + 0x0E), dl ;; number of physical sectors jmp check_for_hd1 hd0_post_logical_chs: ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT) mov (0x003d + 0x09), bx ;; number of physical cylinders mov (0x003d + 0x0b), cl ;; number of physical heads mov (0x003d + 0x04), dl ;; number of physical sectors mov (0x003d + 0x0e), dl ;; number of logical sectors (same) mov al, #0xa0 mov (0x003d + 0x03), al ;; A0h signature, indicates translated table cmp bx, #2048 jnbe hd0_post_above_2048 ;; 1024 < c <= 2048 cylinders shr bx, #0x01 shl cl, #0x01 jmp hd0_post_store_logical hd0_post_above_2048: cmp bx, #4096 jnbe hd0_post_above_4096 ;; 2048 < c <= 4096 cylinders shr bx, #0x02 shl cl, #0x02 jmp hd0_post_store_logical hd0_post_above_4096: cmp bx, #8192 jnbe hd0_post_above_8192 ;; 4096 < c <= 8192 cylinders shr bx, #0x03 shl cl, #0x03 jmp hd0_post_store_logical hd0_post_above_8192: ;; 8192 < c <= 16384 cylinders shr bx, #0x04 shl cl, #0x04 hd0_post_store_logical: mov (0x003d + 0x00), bx ;; number of physical cylinders mov (0x003d + 0x02), cl ;; number of physical heads ;; checksum mov cl, #0x0f ;; repeat count mov si, #0x003d ;; offset to disk0 FDPT mov al, #0x00 ;; sum hd0_post_checksum_loop: add al, [si] inc si dec cl jnz hd0_post_checksum_loop not al ;; now take 2s complement inc al mov [si], al ;;; Done filling EBDA table for hard disk 0. check_for_hd1: ;; is there really a second hard disk? if not, return now mov al, #0x12 out #0x70, al in al, #0x71 and al, #0x0f jnz post_d1_exists ret post_d1_exists: ;; check that the hd type is really 0x0f. cmp al, #0x0f jz post_d1_extended HALT(__LINE__) post_d1_extended: ;; check that the extended type is 47 - user definable mov al, #0x1a out #0x70, al in al, #0x71 cmp al, #47 ;; decimal 47 - user definable je post_d1_type47 HALT(__LINE__) post_d1_type47: ;; Table for disk1. ;; CMOS purpose param table offset ;; 0x24 cylinders low 0 ;; 0x25 cylinders high 1 ;; 0x26 heads 2 ;; 0x27 write pre-comp low 5 ;; 0x28 write pre-comp high 6 ;; 0x29 heads>8 8 ;; 0x2a landing zone low C ;; 0x2b landing zone high D ;; 0x2c sectors/track E ;;; Fill EBDA table for hard disk 1. xor ax, ax mov ds, ax mov ax, word ptr [0x40E] ;; EBDA segment mov ds, ax mov al, #0x28 out #0x70, al in al, #0x71 mov ah, al mov al, #0x27 out #0x70, al in al, #0x71 mov (0x004d + 0x05), ax ;; write precomp word mov al, #0x29 out #0x70, al in al, #0x71 mov (0x004d + 0x08), al ;; drive control byte mov al, #0x2b out #0x70, al in al, #0x71 mov ah, al mov al, #0x2a out #0x70, al in al, #0x71 mov (0x004d + 0x0C), ax ;; landing zone word mov al, #0x25 ;; get cylinders word in AX out #0x70, al in al, #0x71 ;; high byte mov ah, al mov al, #0x24 out #0x70, al in al, #0x71 ;; low byte mov bx, ax ;; BX = cylinders mov al, #0x26 out #0x70, al in al, #0x71 mov cl, al ;; CL = heads mov al, #0x2c out #0x70, al in al, #0x71 mov dl, al ;; DL = sectors cmp bx, #1024 jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS hd1_post_physical_chs: ;; no logical CHS mapping used, just physical CHS ;; use Standard Fixed Disk Parameter Table (FDPT) mov (0x004d + 0x00), bx ;; number of physical cylinders mov (0x004d + 0x02), cl ;; number of physical heads mov (0x004d + 0x0E), dl ;; number of physical sectors ret hd1_post_logical_chs: ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT) mov (0x004d + 0x09), bx ;; number of physical cylinders mov (0x004d + 0x0b), cl ;; number of physical heads mov (0x004d + 0x04), dl ;; number of physical sectors mov (0x004d + 0x0e), dl ;; number of logical sectors (same) mov al, #0xa0 mov (0x004d + 0x03), al ;; A0h signature, indicates translated table cmp bx, #2048 jnbe hd1_post_above_2048 ;; 1024 < c <= 2048 cylinders shr bx, #0x01 shl cl, #0x01 jmp hd1_post_store_logical hd1_post_above_2048: cmp bx, #4096 jnbe hd1_post_above_4096 ;; 2048 < c <= 4096 cylinders shr bx, #0x02 shl cl, #0x02 jmp hd1_post_store_logical hd1_post_above_4096: cmp bx, #8192 jnbe hd1_post_above_8192 ;; 4096 < c <= 8192 cylinders shr bx, #0x03 shl cl, #0x03 jmp hd1_post_store_logical hd1_post_above_8192: ;; 8192 < c <= 16384 cylinders shr bx, #0x04 shl cl, #0x04 hd1_post_store_logical: mov (0x004d + 0x00), bx ;; number of physical cylinders mov (0x004d + 0x02), cl ;; number of physical heads ;; checksum mov cl, #0x0f ;; repeat count mov si, #0x004d ;; offset to disk0 FDPT mov al, #0x00 ;; sum hd1_post_checksum_loop: add al, [si] inc si dec cl jnz hd1_post_checksum_loop not al ;; now take 2s complement inc al mov [si], al ;;; Done filling EBDA table for hard disk 1. ret ;-------------------- ;- POST: EBDA segment ;-------------------- ; relocated here because the primary POST area isnt big enough. ebda_post: #if BX_USE_EBDA mov ax, #EBDA_SEG mov ds, ax mov byte ptr [0x0], #EBDA_SIZE #endif xor ax, ax ; mov EBDA seg into 40E mov ds, ax mov word ptr [0x40E], #EBDA_SEG ret;; ;-------------------- ;- POST: EOI + jmp via [0x40:67) ;-------------------- ; relocated here because the primary POST area isnt big enough. eoi_jmp_post: mov al, #0x20 out #0xA0, al ;; slave PIC EOI mov al, #0x20 out #0x20, al ;; master PIC EOI jmp_post_0x467: xor ax, ax mov ds, ax jmp far ptr [0x467] iret_post_0x467: xor ax, ax mov ds, ax mov sp, [0x467] mov ss, [0x469] iret retf_post_0x467: xor ax, ax mov ds, ax mov sp, [0x467] mov ss, [0x469] retf s3_post: #if BX_ROMBIOS32 call rombios32_init #endif call _s3_resume mov bl, #0x00 and ax, ax jz normal_post call _s3_resume_panic ;-------------------- eoi_both_pics: mov al, #0x20 out #0xA0, al ;; slave PIC EOI eoi_master_pic: mov al, #0x20 out #0x20, al ;; master PIC EOI ret ;-------------------- BcdToBin: ;; in: AL in BCD format ;; out: AL in binary format, AH will always be 0 ;; trashes BX mov bl, al and bl, #0x0f ;; bl has low digit shr al, #4 ;; al has high digit mov bh, #10 mul al, bh ;; multiply high digit by 10 (result in AX) add al, bl ;; then add low digit ret ;-------------------- timer_tick_post: ;; Setup the Timer Ticks Count (0x46C:dword) and ;; Timer Ticks Roller Flag (0x470:byte) ;; The Timer Ticks Count needs to be set according to ;; the current CMOS time, as if ticks have been occurring ;; at 18.2hz since midnight up to this point. Calculating ;; this is a little complicated. Here are the factors I gather ;; regarding this. 14,318,180 hz was the original clock speed, ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU ;; at the time, or 4 to drive the CGA video adapter. The div3 ;; source was divided again by 4 to feed a 1.193Mhz signal to ;; the timer. With a maximum 16bit timer count, this is again ;; divided down by 65536 to 18.2hz. ;; ;; 14,318,180 Hz clock ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU ;; /4 = 1,193,181 Hz fed to timer ;; /65536 (maximum timer count) = 18.20650736 ticks/second ;; 1 second = 18.20650736 ticks ;; 1 minute = 1092.390442 ticks ;; 1 hour = 65543.42651 ticks ;; ;; Given the values in the CMOS clock, one could calculate ;; the number of ticks by the following: ;; ticks = (BcdToBin(seconds) * 18.206507) + ;; (BcdToBin(minutes) * 1092.3904) ;; (BcdToBin(hours) * 65543.427) ;; To get a little more accuracy, since Im using integer ;; arithmatic, I use: ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 + ;; (BcdToBin(minutes) * 10923904) / 10000 + ;; (BcdToBin(hours) * 65543427) / 1000 ;; assuming DS=0000 ;; get CMOS seconds xor eax, eax ;; clear EAX mov al, #0x00 out #0x70, al in al, #0x71 ;; AL has CMOS seconds in BCD call BcdToBin ;; EAX now has seconds in binary mov edx, #18206507 mul eax, edx mov ebx, #1000000 xor edx, edx div eax, ebx mov ecx, eax ;; ECX will accumulate total ticks ;; get CMOS minutes xor eax, eax ;; clear EAX mov al, #0x02 out #0x70, al in al, #0x71 ;; AL has CMOS minutes in BCD call BcdToBin ;; EAX now has minutes in binary mov edx, #10923904 mul eax, edx mov ebx, #10000 xor edx, edx div eax, ebx add ecx, eax ;; add to total ticks ;; get CMOS hours xor eax, eax ;; clear EAX mov al, #0x04 out #0x70, al in al, #0x71 ;; AL has CMOS hours in BCD call BcdToBin ;; EAX now has hours in binary mov edx, #65543427 mul eax, edx mov ebx, #1000 xor edx, edx div eax, ebx add ecx, eax ;; add to total ticks mov 0x46C, ecx ;; Timer Ticks Count xor al, al mov 0x470, al ;; Timer Ticks Rollover Flag ret ;-------------------- int76_handler: ;; record completion in BIOS task complete flag push ax push ds mov ax, #0x0040 mov ds, ax mov 0x008E, #0xff call eoi_both_pics pop ds pop ax iret ;-------------------- #if BX_APM use32 386 #define APM_PROT32 #include "apmbios.S" use16 386 #define APM_PROT16 #include "apmbios.S" #define APM_REAL #include "apmbios.S" #endif #include "32bitgateway.c" ASM_END #include "tcgbios.c" ASM_START ;-------------------- #if BX_PCIBIOS use32 386 .align 16 bios32_structure: db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature dw bios32_entry_point, 0xf ;; 32 bit physical address db 0 ;; revision level ;; length in paragraphs and checksum stored in a word to prevent errors dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \ & 0xff) << 8) + 0x01 db 0,0,0,0,0 ;; reserved .align 16 bios32_entry_point: pushfd cmp eax, #0x49435024 ;; "$PCI" jne unknown_service mov eax, #0x80000000 mov dx, #0x0cf8 out dx, eax mov dx, #0x0cfc in eax, dx #ifdef PCI_FIXED_HOST_BRIDGE cmp eax, #PCI_FIXED_HOST_BRIDGE jne unknown_service #else ;; say ok if a device is present cmp eax, #0xffffffff je unknown_service #endif mov ebx, #0x000f0000 mov ecx, #0 mov edx, #pcibios_protected xor al, al jmp bios32_end unknown_service: mov al, #0x80 bios32_end: #ifdef BX_QEMU and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu #endif popfd retf .align 16 pcibios_protected: pushfd cli push esi push edi cmp al, #0x01 ;; installation check jne pci_pro_f02 mov bx, #0x0210 mov cx, #0 mov edx, #0x20494350 ;; "PCI " mov al, #0x01 jmp pci_pro_ok pci_pro_f02: ;; find pci device cmp al, #0x02 jne pci_pro_f03 shl ecx, #16 mov cx, dx xor bx, bx mov di, #0x00 pci_pro_devloop: call pci_pro_select_reg mov dx, #0x0cfc in eax, dx cmp eax, ecx jne pci_pro_nextdev cmp si, #0 je pci_pro_ok dec si pci_pro_nextdev: inc bx cmp bx, #0x0100 jne pci_pro_devloop mov ah, #0x86 jmp pci_pro_fail pci_pro_f03: ;; find class code cmp al, #0x03 jne pci_pro_f08 xor bx, bx mov di, #0x08 pci_pro_devloop2: call pci_pro_select_reg mov dx, #0x0cfc in eax, dx shr eax, #8 cmp eax, ecx jne pci_pro_nextdev2 cmp si, #0 je pci_pro_ok dec si pci_pro_nextdev2: inc bx cmp bx, #0x0100 jne pci_pro_devloop2 mov ah, #0x86 jmp pci_pro_fail pci_pro_f08: ;; read configuration byte cmp al, #0x08 jne pci_pro_f09 call pci_pro_select_reg push edx mov dx, di and dx, #0x03 add dx, #0x0cfc in al, dx pop edx mov cl, al jmp pci_pro_ok pci_pro_f09: ;; read configuration word cmp al, #0x09 jne pci_pro_f0a call pci_pro_select_reg push edx mov dx, di and dx, #0x02 add dx, #0x0cfc in ax, dx pop edx mov cx, ax jmp pci_pro_ok pci_pro_f0a: ;; read configuration dword cmp al, #0x0a jne pci_pro_f0b call pci_pro_select_reg push edx mov dx, #0x0cfc in eax, dx pop edx mov ecx, eax jmp pci_pro_ok pci_pro_f0b: ;; write configuration byte cmp al, #0x0b jne pci_pro_f0c call pci_pro_select_reg push edx mov dx, di and dx, #0x03 add dx, #0x0cfc mov al, cl out dx, al pop edx jmp pci_pro_ok pci_pro_f0c: ;; write configuration word cmp al, #0x0c jne pci_pro_f0d call pci_pro_select_reg push edx mov dx, di and dx, #0x02 add dx, #0x0cfc mov ax, cx out dx, ax pop edx jmp pci_pro_ok pci_pro_f0d: ;; write configuration dword cmp al, #0x0d jne pci_pro_unknown call pci_pro_select_reg push edx mov dx, #0x0cfc mov eax, ecx out dx, eax pop edx jmp pci_pro_ok pci_pro_unknown: mov ah, #0x81 pci_pro_fail: pop edi pop esi #ifdef BX_QEMU and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu #endif popfd stc retf pci_pro_ok: xor ah, ah pop edi pop esi #ifdef BX_QEMU and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu #endif popfd clc retf pci_pro_select_reg: push edx mov eax, #0x800000 mov ax, bx shl eax, #8 and di, #0xff or ax, di and al, #0xfc mov dx, #0x0cf8 out dx, eax pop edx ret use16 386 pcibios_real: push eax push dx mov eax, #0x80000000 mov dx, #0x0cf8 out dx, eax mov dx, #0x0cfc in eax, dx #ifdef PCI_FIXED_HOST_BRIDGE cmp eax, #PCI_FIXED_HOST_BRIDGE je pci_present #else ;; say ok if a device is present cmp eax, #0xffffffff jne pci_present #endif pop dx pop eax mov ah, #0xff stc ret pci_present: pop dx pop eax cmp al, #0x01 ;; installation check jne pci_real_f02 mov ax, #0x0001 mov bx, #0x0210 mov cx, #0 mov edx, #0x20494350 ;; "PCI " mov edi, #0xf0000 mov di, #pcibios_protected clc ret pci_real_f02: ;; find pci device push esi push edi cmp al, #0x02 jne pci_real_f03 shl ecx, #16 mov cx, dx xor bx, bx mov di, #0x00 pci_real_devloop: call pci_real_select_reg mov dx, #0x0cfc in eax, dx cmp eax, ecx jne pci_real_nextdev cmp si, #0 je pci_real_ok dec si pci_real_nextdev: inc bx cmp bx, #0x0100 jne pci_real_devloop mov dx, cx shr ecx, #16 mov ax, #0x8602 jmp pci_real_fail pci_real_f03: ;; find class code cmp al, #0x03 jne pci_real_f08 xor bx, bx mov di, #0x08 pci_real_devloop2: call pci_real_select_reg mov dx, #0x0cfc in eax, dx shr eax, #8 cmp eax, ecx jne pci_real_nextdev2 cmp si, #0 je pci_real_ok dec si pci_real_nextdev2: inc bx cmp bx, #0x0100 jne pci_real_devloop2 mov dx, cx shr ecx, #16 mov ax, #0x8603 jmp pci_real_fail pci_real_f08: ;; read configuration byte cmp al, #0x08 jne pci_real_f09 call pci_real_select_reg push dx mov dx, di and dx, #0x03 add dx, #0x0cfc in al, dx pop dx mov cl, al jmp pci_real_ok pci_real_f09: ;; read configuration word cmp al, #0x09 jne pci_real_f0a call pci_real_select_reg push dx mov dx, di and dx, #0x02 add dx, #0x0cfc in ax, dx pop dx mov cx, ax jmp pci_real_ok pci_real_f0a: ;; read configuration dword cmp al, #0x0a jne pci_real_f0b call pci_real_select_reg push dx mov dx, #0x0cfc in eax, dx pop dx mov ecx, eax jmp pci_real_ok pci_real_f0b: ;; write configuration byte cmp al, #0x0b jne pci_real_f0c call pci_real_select_reg push dx mov dx, di and dx, #0x03 add dx, #0x0cfc mov al, cl out dx, al pop dx jmp pci_real_ok pci_real_f0c: ;; write configuration word cmp al, #0x0c jne pci_real_f0d call pci_real_select_reg push dx mov dx, di and dx, #0x02 add dx, #0x0cfc mov ax, cx out dx, ax pop dx jmp pci_real_ok pci_real_f0d: ;; write configuration dword cmp al, #0x0d jne pci_real_f0e call pci_real_select_reg push dx mov dx, #0x0cfc mov eax, ecx out dx, eax pop dx jmp pci_real_ok pci_real_f0e: ;; get irq routing options cmp al, #0x0e jne pci_real_unknown SEG ES cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start jb pci_real_too_small SEG ES mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start pushf push ds push es push cx push si push di cld mov si, #pci_routing_table_structure_start push cs pop ds SEG ES mov cx, [di+2] SEG ES mov es, [di+4] mov di, cx mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start rep movsb pop di pop si pop cx pop es pop ds popf mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used jmp pci_real_ok pci_real_too_small: SEG ES mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start mov ah, #0x89 jmp pci_real_fail pci_real_unknown: mov ah, #0x81 pci_real_fail: pop edi pop esi stc ret pci_real_ok: xor ah, ah pop edi pop esi clc ret pci_real_select_reg: push dx mov eax, #0x800000 mov ax, bx shl eax, #8 and di, #0xff or ax, di and al, #0xfc mov dx, #0x0cf8 out dx, eax pop dx ret .align 16 pci_routing_table_structure: db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature db 0, 1 ;; version dw 32 + (6 * 16) ;; table size db 0 ;; PCI interrupt router bus db 0x08 ;; PCI interrupt router DevFunc dw 0x0000 ;; PCI exclusive IRQs dw 0x8086 ;; compatible PCI interrupt router vendor ID dw 0x122e ;; compatible PCI interrupt router device ID dw 0,0 ;; Miniport data db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved db 0x37 ;; checksum pci_routing_table_structure_start: ;; first slot entry PCI-to-ISA (embedded) db 0 ;; pci bus number db 0x08 ;; pci device number (bit 7-3) db 0x61 ;; link value INTA#: pointer into PCI2ISA config space dw 0x0c20 ;; IRQ bitmap INTA# db 0x62 ;; link value INTB# dw 0x0c20 ;; IRQ bitmap INTB# db 0x63 ;; link value INTC# dw 0x0c20 ;; IRQ bitmap INTC# db 0x60 ;; link value INTD# dw 0x0c20 ;; IRQ bitmap INTD# db 0 ;; physical slot (0 = embedded) db 0 ;; reserved ;; second slot entry: 1st PCI slot db 0 ;; pci bus number db 0x10 ;; pci device number (bit 7-3) db 0x62 ;; link value INTA# dw 0x0c20 ;; IRQ bitmap INTA# db 0x63 ;; link value INTB# dw 0x0c20 ;; IRQ bitmap INTB# db 0x60 ;; link value INTC# dw 0x0c20 ;; IRQ bitmap INTC# db 0x61 ;; link value INTD# dw 0x0c20 ;; IRQ bitmap INTD# db 1 ;; physical slot (0 = embedded) db 0 ;; reserved ;; third slot entry: 2nd PCI slot db 0 ;; pci bus number db 0x18 ;; pci device number (bit 7-3) db 0x63 ;; link value INTA# dw 0x0c20 ;; IRQ bitmap INTA# db 0x60 ;; link value INTB# dw 0x0c20 ;; IRQ bitmap INTB# db 0x61 ;; link value INTC# dw 0x0c20 ;; IRQ bitmap INTC# db 0x62 ;; link value INTD# dw 0x0c20 ;; IRQ bitmap INTD# db 2 ;; physical slot (0 = embedded) db 0 ;; reserved ;; 4th slot entry: 3rd PCI slot db 0 ;; pci bus number db 0x20 ;; pci device number (bit 7-3) db 0x60 ;; link value INTA# dw 0x0c20 ;; IRQ bitmap INTA# db 0x61 ;; link value INTB# dw 0x0c20 ;; IRQ bitmap INTB# db 0x62 ;; link value INTC# dw 0x0c20 ;; IRQ bitmap INTC# db 0x63 ;; link value INTD# dw 0x0c20 ;; IRQ bitmap INTD# db 3 ;; physical slot (0 = embedded) db 0 ;; reserved ;; 5th slot entry: 4rd PCI slot db 0 ;; pci bus number db 0x28 ;; pci device number (bit 7-3) db 0x61 ;; link value INTA# dw 0x0c20 ;; IRQ bitmap INTA# db 0x62 ;; link value INTB# dw 0x0c20 ;; IRQ bitmap INTB# db 0x63 ;; link value INTC# dw 0x0c20 ;; IRQ bitmap INTC# db 0x60 ;; link value INTD# dw 0x0c20 ;; IRQ bitmap INTD# db 4 ;; physical slot (0 = embedded) db 0 ;; reserved ;; 6th slot entry: 5rd PCI slot db 0 ;; pci bus number db 0x30 ;; pci device number (bit 7-3) db 0x62 ;; link value INTA# dw 0x0c20 ;; IRQ bitmap INTA# db 0x63 ;; link value INTB# dw 0x0c20 ;; IRQ bitmap INTB# db 0x60 ;; link value INTC# dw 0x0c20 ;; IRQ bitmap INTC# db 0x61 ;; link value INTD# dw 0x0c20 ;; IRQ bitmap INTD# db 5 ;; physical slot (0 = embedded) db 0 ;; reserved pci_routing_table_structure_end: #if !BX_ROMBIOS32 && !defined(HVMASSIST) pci_irq_list: db 11, 10, 9, 5; pcibios_init_sel_reg: push eax mov eax, #0x800000 mov ax, bx shl eax, #8 and dl, #0xfc or al, dl mov dx, #0x0cf8 out dx, eax pop eax ret pcibios_init_iomem_bases: push bp mov bp, sp mov eax, #0xe0000000 ;; base for memory init push eax mov ax, #0xc000 ;; base for i/o init push ax mov ax, #0x0010 ;; start at base address #0 push ax mov bx, #0x0008 pci_init_io_loop1: mov dl, #0x00 call pcibios_init_sel_reg mov dx, #0x0cfc in ax, dx cmp ax, #0xffff jz next_pci_dev mov dl, #0x04 ;; disable i/o and memory space access call pcibios_init_sel_reg mov dx, #0x0cfc in al, dx and al, #0xfc out dx, al pci_init_io_loop2: mov dl, [bp-8] call pcibios_init_sel_reg mov dx, #0x0cfc in eax, dx test al, #0x01 jnz init_io_base mov ecx, eax mov eax, #0xffffffff out dx, eax in eax, dx cmp eax, ecx je next_pci_base xor eax, #0xffffffff mov ecx, eax mov eax, [bp-4] out dx, eax add eax, ecx ;; calculate next free mem base add eax, #0x01000000 and eax, #0xff000000 mov [bp-4], eax jmp next_pci_base init_io_base: mov cx, ax mov ax, #0xffff out dx, ax in ax, dx cmp ax, cx je next_pci_base xor ax, #0xfffe mov cx, ax mov ax, [bp-6] out dx, ax add ax, cx ;; calculate next free i/o base add ax, #0x0100 and ax, #0xff00 mov [bp-6], ax next_pci_base: mov al, [bp-8] add al, #0x04 cmp al, #0x28 je enable_iomem_space mov byte ptr[bp-8], al jmp pci_init_io_loop2 enable_iomem_space: mov dl, #0x04 ;; enable i/o and memory space access if available call pcibios_init_sel_reg mov dx, #0x0cfc in al, dx or al, #0x07 out dx, al next_pci_dev: mov byte ptr[bp-8], #0x10 inc bx cmp bx, #0x0100 jne pci_init_io_loop1 mov sp, bp pop bp ret pcibios_init_set_elcr: push ax push cx mov dx, #0x04d0 test al, #0x08 jz is_master_pic inc dx and al, #0x07 is_master_pic: mov cl, al mov bl, #0x01 shl bl, cl in al, dx or al, bl out dx, al pop cx pop ax ret pcibios_init_irqs: push ds push bp mov ax, #0xf000 mov ds, ax mov dx, #0x04d0 ;; reset ELCR1 + ELCR2 mov al, #0x00 out dx, al inc dx out dx, al mov si, #pci_routing_table_structure mov bh, [si+8] mov bl, [si+9] mov dl, #0x00 call pcibios_init_sel_reg mov dx, #0x0cfc in eax, dx cmp eax, [si+12] ;; check irq router jne pci_init_end mov dl, [si+34] call pcibios_init_sel_reg push bx ;; save irq router bus + devfunc mov dx, #0x0cfc mov ax, #0x8080 out dx, ax ;; reset PIRQ route control add dx, #2 out dx, ax mov ax, [si+6] sub ax, #0x20 shr ax, #4 mov cx, ax add si, #0x20 ;; set pointer to 1st entry mov bp, sp mov ax, #pci_irq_list push ax xor ax, ax push ax pci_init_irq_loop1: mov bh, [si] mov bl, [si+1] pci_init_irq_loop2: mov dl, #0x00 call pcibios_init_sel_reg mov dx, #0x0cfc in ax, dx cmp ax, #0xffff jnz pci_test_int_pin test bl, #0x07 jz next_pir_entry jmp next_pci_func pci_test_int_pin: mov dl, #0x3c call pcibios_init_sel_reg mov dx, #0x0cfd in al, dx and al, #0x07 jz next_pci_func dec al ;; determine pirq reg mov dl, #0x03 mul al, dl add al, #0x02 xor ah, ah mov bx, ax mov al, [si+bx] mov dl, al mov bx, [bp] call pcibios_init_sel_reg mov dx, #0x0cfc and al, #0x03 add dl, al in al, dx cmp al, #0x80 jb pirq_found mov bx, [bp-2] ;; pci irq list pointer mov al, [bx] out dx, al inc bx mov [bp-2], bx call pcibios_init_set_elcr pirq_found: mov bh, [si] mov bl, [si+1] add bl, [bp-3] ;; pci function number mov dl, #0x3c call pcibios_init_sel_reg mov dx, #0x0cfc out dx, al next_pci_func: inc byte ptr[bp-3] inc bl test bl, #0x07 jnz pci_init_irq_loop2 next_pir_entry: add si, #0x10 mov byte ptr[bp-3], #0x00 loop pci_init_irq_loop1 mov sp, bp pop bx pci_init_end: pop bp pop ds ret #endif // !BX_ROMBIOS32 #endif // BX_PCIBIOS #if BX_ROMBIOS32 rombios32_init: ;; save a20 and enable it in al, 0x92 push ax or al, #0x02 out 0x92, al ;; save SS:SP to the BDA xor ax, ax mov ds, ax mov 0x0469, ss mov 0x0467, sp SEG CS lidt [pmode_IDT_info] SEG CS lgdt [rombios32_gdt_48] ;; set PE bit in CR0 mov eax, cr0 or al, #0x01 mov cr0, eax ;; start protected mode code: ljmpl 0x10:rombios32_init1 db 0x66, 0xea dw rombios32_05 dw 0x000f ;; high 16 bit address dw 0x0010 use32 386 rombios32_05: ;; init data segments mov eax, #0x18 mov ds, ax mov es, ax mov ss, ax xor eax, eax mov fs, ax mov gs, ax cld ;; init the stack pointer to point below EBDA mov ax, [0x040e] shl eax, #4 mov esp, #-0x10 add esp, eax ;; pass pointer to s3_resume_flag and s3_resume_vector to rombios32 push #0x04b0 push #0x04b2 ;; call rombios32 code mov eax, #0x000e0000 call eax ;; return to 16 bit protected mode first db 0xea dd rombios32_10 dw 0x20 use16 386 rombios32_10: ;; restore data segment limits to 0xffff mov ax, #0x28 mov ds, ax mov es, ax mov ss, ax mov fs, ax mov gs, ax ;; reset PE bit in CR0 mov eax, cr0 and al, #0xFE mov cr0, eax ;; far jump to flush CPU queue after transition to real mode JMP_AP(0xf000, rombios32_real_mode) rombios32_real_mode: ;; restore IDT to normal real-mode defaults SEG CS lidt [rmode_IDT_info] xor ax, ax mov ds, ax mov es, ax mov fs, ax mov gs, ax ;; restore SS:SP from the BDA mov ss, 0x0469 xor esp, esp mov sp, 0x0467 ;; restore a20 pop ax out 0x92, al ret rombios32_gdt_48: dw 0x30 dw rombios32_gdt dw 0x000f rombios32_gdt: dw 0, 0, 0, 0 dw 0, 0, 0, 0 dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10) dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18) dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff #endif // BX_ROMBIOS32 #if BX_PMM ; according to POST Memory Manager Specification Version 1.01 .align 16 pmm_structure: db 0x24,0x50,0x4d,0x4d ;; "$PMM" signature db 0x01 ;; revision db 16 ;; length db (-((pmm_entry_point>>8)+pmm_entry_point+0x20f))&0xff;; checksum dw pmm_entry_point,0xf000 ;; far call entrypoint db 0,0,0,0,0 ;; reserved pmm_entry_point: pushf pushad ; Calculate protected-mode address of PMM function args xor eax, eax mov ax, sp xor ebx, ebx mov bx, ss shl ebx, 4 lea ebx, [eax+ebx+38] ;; ebx=(ss<<4)+sp+4(far call)+2(pushf)+32(pushad) push ebx ; ; Stack layout at this point: ; ; : +0x0 +0x2 +0x4 +0x6 +0x8 +0xa +0xc +0xe ; ----------------------------------------------------------------------- ; sp : [&arg1 ][edi ][esi ][ebp ] ; sp+0x10: [esp ][ebx ][edx ][ecx ] ; sp+0x20: [eax ][flags ][ip ][cs ][arg1 ][arg2, ... ; call _pmm mov bx, sp SEG SS mov [bx+0x20], ax SEG SS mov [bx+0x18], dx pop ebx popad popf retf #endif // BX_PMM ; parallel port detection: base address in DX, index in BX, timeout in CL detect_parport: push dx add dx, #2 in al, dx and al, #0xdf ; clear input mode out dx, al pop dx mov al, #0xaa out dx, al in al, dx cmp al, #0xaa jne no_parport push bx shl bx, #1 mov [bx+0x408], dx ; Parallel I/O address pop bx mov [bx+0x478], cl ; Parallel printer timeout inc bx no_parport: ret ; serial port detection: base address in DX, index in BX, timeout in CL detect_serial: push dx inc dx mov al, #0x02 out dx, al in al, dx cmp al, #0x02 jne no_serial inc dx in al, dx cmp al, #0x02 jne no_serial dec dx xor al, al out dx, al pop dx push bx shl bx, #1 mov [bx+0x400], dx ; Serial I/O address pop bx mov [bx+0x47c], cl ; Serial timeout inc bx ret no_serial: pop dx ret rom_checksum: pusha push ds xor ax, ax xor bx, bx xor cx, cx xor dx, dx mov ch, [2] shl cx, #1 jnc checksum_loop jz checksum_loop xchg dx, cx dec cx checksum_loop: add al, [bx] inc bx loop checksum_loop test dx, dx je checksum_out add al, [bx] mov cx, dx mov dx, ds add dh, #0x10 mov ds, dx xor dx, dx xor bx, bx jmp checksum_loop checksum_out: and al, #0xff pop ds popa ret ;; We need a copy of this string, but we are not actually a PnP BIOS, ;; so make sure it is *not* aligned, so OSes will not see it if they scan. .align 16 db 0 pnp_string: .ascii "$PnP" rom_scan: ;; Scan for existence of valid expansion ROMS. ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments ;; General ROM: from 0xC8000..0xE9FFF in 2k increments ;; System ROM: only 0xF0000 ;; ;; Header: ;; Offset Value ;; 0 0x55 ;; 1 0xAA ;; 2 ROM length in 512-byte blocks ;; 3 ROM initialization entry point (FAR CALL) #if BX_TCGBIOS push ax call _tcpa_start_option_rom_scan /* specs: 3.2.3.3 + 10.4.3 */ pop ax #endif rom_scan_loop: push ax ;; Save AX mov ds, cx mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k cmp [0], #0xAA55 ;; look for signature jne rom_scan_increment call rom_checksum jnz rom_scan_increment mov al, [2] ;; change increment to ROM length in 512-byte blocks ;; We want our increment in 512-byte quantities, rounded to ;; the nearest 2k quantity, since we only scan at 2k intervals. test al, #0x03 jz block_count_rounded and al, #0xfc ;; needs rounding up add al, #0x04 block_count_rounded: #if BX_TCGBIOS push ax push ds push ecx xor ax, ax mov ds, ax and ecx, #0xffff push ecx ;; segment where option rom is located at call _tcpa_option_rom /* specs: 3.2.3.3 */ add sp, #4 ;; pop segment pop ecx ;; original ecx pop ds pop ax #endif push ax ;; Save AX push di ;; Save DI ;; Push addr of ROM entry point push cx ;; Push seg push #0x0003 ;; Push offset ;; Get the BDF into ax before invoking the option ROM mov bl, [2] mov al, bl shr al, #7 cmp al, #1 jne fetch_bdf mov ax, ds ;; Increment the DS since rom size larger than an segment add ax, #0x1000 mov ds, ax fetch_bdf: shl bx, #9 xor ax, ax mov al, [bx] ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS. ;; That should stop it grabbing INT 19h; we will use its BEV instead. mov bx, #0xf000 mov es, bx lea di, pnp_string xor bx, bx ;; Restore DS back to 0000: mov ds, bx mov bp, sp ;; Call ROM init routine using seg:off on stack db 0xff ;; call_far ss:[bp+0] db 0x5e db 0 cli ;; In case expansion ROM BIOS turns IF on add sp, #2 ;; Pop offset value pop cx ;; Pop seg value (restore CX) ;; Look at the ROM's PnP Expansion header. Properly, we're supposed ;; to init all the ROMs and then go back and build an IPL table of ;; all the bootable devices, but we can get away with one pass. mov ds, cx ;; ROM base mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains... mov ax, [bx] ;; the offset of PnP expansion header, where... cmp ax, #0x5024 ;; we look for signature "$PnP" jne no_bev mov ax, 2[bx] cmp ax, #0x506e jne no_bev mov ax, 0x16[bx] ;; 0x16 is the offset of Boot Connection Vector cmp ax, #0x0000 je no_bcv ;; Option ROM has BCV. Run it now. push cx ;; Push seg push ax ;; Push offset ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS. mov bx, #0xf000 mov es, bx lea di, pnp_string /* jump to BCV function entry pointer */ mov bp, sp ;; Call ROM BCV routine using seg:off on stack db 0xff ;; call_far ss:[bp+0] db 0x5e db 0 cli ;; In case expansion ROM BIOS turns IF on add sp, #2 ;; Pop offset value pop cx ;; Pop seg value (restore CX) jmp no_bev no_bcv: mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of... cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none. je no_bev ;; Found a device that thinks it can boot the system. Record its BEV and product name string. mov di, 0x10[bx] ;; Pointer to the product name string or zero if none xor bx, bx mov ds, bx mov bx, word ptr [0x40E] ;; EBDA segment mov ds, bx ;; Go to the segment where the IPL table lives mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far cmp bx, #IPL_TABLE_ENTRIES je no_bev ;; Get out if the table is full shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes) mov IPL_TABLE_OFFSET+0[bx], #IPL_TYPE_BEV ;; This entry is a BEV device mov IPL_TABLE_OFFSET+6[bx], cx ;; Build a far pointer from the segment... mov IPL_TABLE_OFFSET+4[bx], ax ;; and the offset cmp di, #0x0000 je no_prod_str mov 0xA[bx], cx ;; Build a far pointer from the segment... mov 8[bx], di ;; and the offset no_prod_str: shr bx, #0x4 ;; Turn the offset back into a count inc bx ;; We have one more entry now mov IPL_COUNT_OFFSET, bx ;; Remember that. no_bev: pop di ;; Restore DI pop ax ;; Restore AX rom_scan_increment: shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments ;; because the segment selector is shifted left 4 bits. add cx, ax pop ax ;; Restore AX cmp cx, ax jbe rom_scan_loop xor ax, ax ;; Restore DS back to 0000: mov ds, ax ret #ifdef HVMASSIST ; Copy the SMBIOS entry point from where hvmloader left it. ; The entry point must be somewhere in 0xf0000-0xfffff on a 16-byte boundary, ; but the tables themselves can be elsewhere. smbios_init: push ax push cx push es push ds push di push si mov cx, #0x001f ; 0x1f bytes to copy mov ax, #0xf000 mov es, ax ; destination segment is 0xf0000 mov di, #smbios_entry_point ; destination offset mov ax, #(SMBIOS_PHYSICAL_ADDRESS>>4) mov ds, ax mov si, #(SMBIOS_PHYSICAL_ADDRESS&15) cld rep movsb pop si pop di pop ds pop es pop cx pop ax ret #endif #if BX_TCGBIOS ; The section between the POST entry and the NMI entry is filling up ; and causes crashes if this code was directly there tcpa_post_part1: call _tcpa_acpi_init push dword #0 call _tcpa_initialize_tpm add sp, #4 call _tcpa_do_measure_POSTs call _tcpa_wake_event /* specs: 3.2.3.7 */ ret tcpa_post_part2: call _tcpa_calling_int19h /* specs: 8.2.3 step 1 */ call _tcpa_add_event_separators /* specs: 8.2.3 step 2 */ /* we do not call int 19h handler but keep following eventlog */ call _tcpa_returned_int19h /* specs: 8.2.3 step 3/7 */ ret #endif post_init_pic: mov al, #0x11 ; send initialisation commands out 0x20, al out 0xa0, al mov al, #0x08 out 0x21, al mov al, #0x70 out 0xa1, al mov al, #0x04 out 0x21, al mov al, #0x02 out 0xa1, al mov al, #0x01 out 0x21, al out 0xa1, al mov al, #0xb8 out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6 #if BX_USE_PS2_MOUSE mov al, #0x8f #else mov al, #0x9f #endif out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14 ret .align 16 smbios_entry_point: db 0,0,0,0,0,0,0,0 ; 8 bytes db 0,0,0,0,0,0,0,0 ; 16 bytes db 0,0,0,0,0,0,0,0 ; 24 bytes db 0,0,0,0,0,0,0 ; 31 bytes ;; the following area can be used to write dynamically generated tables .align 16 bios_table_area_start: db 0x5F, 0x5F, 0x5F, 0x48, 0x56, 0x4D, 0x4D, 0x50 ;; ___HVMMP dd bios_table_area_end - bios_table_area_start ;-------- ;- POST - ;-------- .org 0xe05b ; POST Entry Point post: xor ax, ax ;; first reset the DMA controllers out 0x0d,al out 0xda,al ;; then initialize the DMA controllers mov al, #0xC0 out 0xD6, al ; cascade mode of channel 4 enabled mov al, #0x00 out 0xD4, al ; unmask channel 4 ;; Examine CMOS shutdown status. mov AL, #0x0f out 0x70, AL in AL, 0x71 ;; backup status mov bl, al ;; Reset CMOS shutdown status. mov AL, #0x0f out 0x70, AL ; select CMOS register Fh mov AL, #0x00 out 0x71, AL ; set shutdown action to normal ;; Examine CMOS shutdown status. mov al, bl ;; 0x00, 0x09, 0x0D+ = normal startup cmp AL, #0x00 jz normal_post cmp AL, #0x0d jae normal_post cmp AL, #0x09 je normal_post ;; 0x05 = eoi + jmp via [0x40:0x67] jump cmp al, #0x05 je eoi_jmp_post ;; 0x0A = jmp via [0x40:0x67] jump cmp al, #0x0a je jmp_post_0x467 ;; 0x0B = iret via [0x40:0x67] cmp al, #0x0b je iret_post_0x467 ;; 0x0C = retf via [0x40:0x67] cmp al, #0x0c je retf_post_0x467 ;; Examine CMOS shutdown status. ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08 = Unimplemented shutdown status. push bx call _shutdown_status_panic #if 0 HALT(__LINE__) ; ;#if 0 ; 0xb0, 0x20, /* mov al, #0x20 */ ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */ ;#endif ; pop es pop ds popa iret #endif normal_post: ; case 0: normal startup cli mov ax, #0x0ffe mov sp, ax mov ax, #0x9e00 mov ss, ax ;; Save shutdown status mov 0x04b0, bl cmp bl, #0xfe jz s3_post ;; zero out BIOS data area (40:00..40:ff) mov es, ax mov cx, #0x0080 ;; 128 words mov di, #0x0400 cld rep stosw call _log_bios_start ;; set all interrupts to default handler xor bx, bx ;; offset index mov cx, #0x0100 ;; counter (256 interrupts) mov ax, #dummy_iret_handler mov dx, #0xF000 post_default_ints: mov [bx], ax add bx, #2 mov [bx], dx add bx, #2 loop post_default_ints ;; set vector 0x79 to zero ;; this is used by 'gardian angel' protection system SET_INT_VECTOR(0x79, #0, #0) ;; base memory in K 40:13 (word) mov ax, #BASE_MEM_IN_K mov 0x0413, ax ;; Manufacturing Test 40:12 ;; zerod out above ;; Warm Boot Flag 0040:0072 ;; value of 1234h = skip memory checks ;; zerod out above ;; Printer Services vector SET_INT_VECTOR(0x17, #0xF000, #int17_handler) ;; Bootstrap failure vector SET_INT_VECTOR(0x18, #0xF000, #int18_handler) ;; Bootstrap Loader vector SET_INT_VECTOR(0x19, #0xF000, #int19_handler) ;; User Timer Tick vector SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler) ;; Memory Size Check vector SET_INT_VECTOR(0x12, #0xF000, #int12_handler) ;; Equipment Configuration Check vector SET_INT_VECTOR(0x11, #0xF000, #int11_handler) ;; System Services SET_INT_VECTOR(0x15, #0xF000, #int15_handler) ;; EBDA setup call ebda_post ;; PIT setup SET_INT_VECTOR(0x08, #0xF000, #int08_handler) ;; int 1C already points at dummy_iret_handler (above) mov al, #0x34 ; timer0: binary count, 16bit count, mode 2 out 0x43, al #ifdef HVMASSIST mov al, #0x0b ; #0xe90b = 20 Hz (temporary, until we fix xen/vmx support) out 0x40, al ; lsb mov al, #0xe9 out 0x40, al ; msb #else mov al, #0x00 ; maximum count of 0000H = 18.2Hz out 0x40, al out 0x40, al #endif ;; Keyboard SET_INT_VECTOR(0x09, #0xF000, #int09_handler) SET_INT_VECTOR(0x16, #0xF000, #int16_handler) xor ax, ax mov ds, ax mov 0x0417, al /* keyboard shift flags, set 1 */ mov 0x0418, al /* keyboard shift flags, set 2 */ mov 0x0419, al /* keyboard alt-numpad work area */ mov 0x0471, al /* keyboard ctrl-break flag */ mov 0x0497, al /* keyboard status flags 4 */ mov al, #0x10 mov 0x0496, al /* keyboard status flags 3 */ /* keyboard head of buffer pointer */ mov bx, #0x001E mov 0x041A, bx /* keyboard end of buffer pointer */ mov 0x041C, bx /* keyboard pointer to start of buffer */ mov bx, #0x001E mov 0x0480, bx /* keyboard pointer to end of buffer */ mov bx, #0x003E mov 0x0482, bx /* init the keyboard */ call _keyboard_init ;; mov CMOS Equipment Byte to BDA Equipment Word mov ax, 0x0410 mov al, #0x14 out 0x70, al in al, 0x71 mov 0x0410, ax #if BX_TCGBIOS call tcpa_post_part1 #endif ;; Parallel setup SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler) xor ax, ax mov ds, ax xor bx, bx mov cl, #0x14 ; timeout value mov dx, #0x378 ; Parallel I/O address, port 1 call detect_parport mov dx, #0x278 ; Parallel I/O address, port 2 call detect_parport shl bx, #0x0e mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports and ax, #0x3fff or ax, bx ; set number of parallel ports mov 0x410, ax ;; Serial setup SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler) SET_INT_VECTOR(0x14, #0xF000, #int14_handler) xor bx, bx mov cl, #0x0a ; timeout value mov dx, #0x03f8 ; Serial I/O address, port 1 call detect_serial mov dx, #0x02f8 ; Serial I/O address, port 2 call detect_serial mov dx, #0x03e8 ; Serial I/O address, port 3 call detect_serial mov dx, #0x02e8 ; Serial I/O address, port 4 call detect_serial shl bx, #0x09 mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports and ax, #0xf1ff or ax, bx ; set number of serial port mov 0x410, ax ;; CMOS RTC SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler) SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler) SET_INT_VECTOR(0x70, #0xF000, #int70_handler) ;; BIOS DATA AREA 0x4CE ??? call timer_tick_post ;; PS/2 mouse setup SET_INT_VECTOR(0x74, #0xF000, #int74_handler) ;; IRQ13 (FPU exception) setup SET_INT_VECTOR(0x75, #0xF000, #int75_handler) ;; Video setup SET_INT_VECTOR(0x10, #0xF000, #int10_handler) ;; PIC call post_init_pic mov cx, #0xc000 ;; init vga bios mov ax, #0xc780 call rom_scan call _print_bios_banner #if BX_ROMBIOS32 call rombios32_init #else #if BX_PCIBIOS && !defined(HVMASSIST) call pcibios_init_iomem_bases call pcibios_init_irqs #endif //BX_PCIBIOS #endif ;; ;; Floppy setup ;; call floppy_drive_post ;; ;; Hard Drive setup ;; call hard_drive_post #if BX_USE_ATADRV ;; ;; ATA/ATAPI driver setup ;; call _ata_init call _ata_detect ;; #endif // BX_USE_ATADRV #if BX_ELTORITO_BOOT ;; ;; eltorito floppy/harddisk emulation from cd ;; call _cdemu_init ;; #endif // BX_ELTORITO_BOOT #ifdef HVMASSIST call _enable_rom_write_access call _clobber_entry_point call _fixup_base_mem_in_k call smbios_init #endif call _init_boot_vectors mov cx, #(OPTIONROM_PHYSICAL_ADDRESS >> 4) ;; init option roms mov ax, #(OPTIONROM_PHYSICAL_END >> 4) call rom_scan #ifdef HVMASSIST call _disable_rom_write_access #endif #if BX_ELTORITO_BOOT call _interactive_bootkey #endif // BX_ELTORITO_BOOT #if BX_TCGBIOS call tcpa_post_part2 #endif sti ;; enable interrupts ;; Start the boot sequence. See the comments in int19_relocated ;; for why we use INT 18h instead of INT 19h here. int #0x18 .org 0xe2c3 ; NMI Handler Entry Point nmi: ;; FIXME the NMI handler should not panic ;; but iret when called from int75 (fpu exception) call _nmi_handler_msg iret int75_handler: out 0xf0, al // clear irq13 call eoi_both_pics // clear interrupt int 2 // legacy nmi call iret ;------------------------------------------- ;- INT 13h Fixed Disk Services Entry Point - ;------------------------------------------- .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point int13_handler: //JMPL(int13_relocated) jmp int13_relocated .org 0xe401 ; Fixed Disk Parameter Table ;---------- ;- INT19h - ;---------- .org 0xe6f2 ; INT 19h Boot Load Service Entry Point int19_handler: jmp int19_relocated ;------------------------------------------- ;- System BIOS Configuration Data Table ;------------------------------------------- .org BIOS_CONFIG_TABLE db 0x08 ; Table size (bytes) -Lo db 0x00 ; Table size (bytes) -Hi db SYS_MODEL_ID db SYS_SUBMODEL_ID db BIOS_REVISION ; Feature byte 1 ; b7: 1=DMA channel 3 used by hard disk ; b6: 1=2 interrupt controllers present ; b5: 1=RTC present ; b4: 1=BIOS calls int 15h/4Fh every key ; b3: 1=wait for extern event supported (Int 15h/41h) ; b2: 1=extended BIOS data area used ; b1: 0=AT or ESDI bus, 1=MicroChannel ; b0: 1=Dual bus (MicroChannel + ISA) db (0 << 7) | \ (1 << 6) | \ (1 << 5) | \ (BX_CALL_INT15_4F << 4) | \ (0 << 3) | \ (BX_USE_EBDA << 2) | \ (0 << 1) | \ (0 << 0) ; Feature byte 2 ; b7: 1=32-bit DMA supported ; b6: 1=int16h, function 9 supported ; b5: 1=int15h/C6h (get POS data) supported ; b4: 1=int15h/C7h (get mem map info) supported ; b3: 1=int15h/C8h (en/dis CPU) supported ; b2: 1=non-8042 kb controller ; b1: 1=data streaming supported ; b0: reserved db (0 << 7) | \ (1 << 6) | \ (0 << 5) | \ (0 << 4) | \ (0 << 3) | \ (0 << 2) | \ (0 << 1) | \ (0 << 0) ; Feature byte 3 ; b7: not used ; b6: reserved ; b5: reserved ; b4: POST supports ROM-to-RAM enable/disable ; b3: SCSI on system board ; b2: info panel installed ; b1: Initial Machine Load (IML) system - BIOS on disk ; b0: SCSI supported in IML db 0x00 ; Feature byte 4 ; b7: IBM private ; b6: EEPROM present ; b5-3: ABIOS presence (011 = not supported) ; b2: private ; b1: memory split above 16Mb supported ; b0: POSTEXT directly supported by POST db 0x00 ; Feature byte 5 (IBM) ; b1: enhanced mouse ; b0: flash EPROM db 0x00 .org 0xe729 ; Baud Rate Generator Table ;---------- ;- INT14h - ;---------- .org 0xe739 ; INT 14h Serial Communications Service Entry Point int14_handler: push ds pusha xor ax, ax mov ds, ax call _int14_function popa pop ds iret ;---------------------------------------- ;- INT 16h Keyboard Service Entry Point - ;---------------------------------------- .org 0xe82e int16_handler: sti push ds pushf pusha cmp ah, #0x00 je int16_F00 cmp ah, #0x10 je int16_F00 mov bx, #0xf000 mov ds, bx call _int16_function popa popf pop ds jz int16_zero_set int16_zero_clear: push bp mov bp, sp //SEG SS and BYTE [bp + 0x06], #0xbf pop bp iret int16_zero_set: push bp mov bp, sp //SEG SS or BYTE [bp + 0x06], #0x40 pop bp iret int16_F00: mov bx, #0x0040 mov ds, bx int16_wait_for_key: cli mov bx, 0x001a cmp bx, 0x001c jne int16_key_found sti hlt #if 0 /* no key yet, call int 15h, function AX=9002 */ 0x50, /* push AX */ 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */ 0xcd, 0x15, /* int 15h */ 0x58, /* pop AX */ 0xeb, 0xea, /* jmp WAIT_FOR_KEY */ #endif jmp int16_wait_for_key int16_key_found: mov bx, #0xf000 mov ds, bx call _int16_function popa popf pop ds #if 0 /* notify int16 complete w/ int 15h, function AX=9102 */ 0x50, /* push AX */ 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */ 0xcd, 0x15, /* int 15h */ 0x58, /* pop AX */ #endif iret ;------------------------------------------------- ;- INT09h : Keyboard Hardware Service Entry Point - ;------------------------------------------------- .org 0xe987 int09_handler: cli push ax mov al, #0xAD ;;disable keyboard out #0x64, al mov al, #0x0B out #0x20, al in al, #0x20 and al, #0x02 jz int09_finish in al, #0x60 ;;read key from keyboard controller sti push ds pusha #ifdef BX_CALL_INT15_4F mov ah, #0x4f ;; allow for keyboard intercept stc int #0x15 jnc int09_done #endif ;; check for extended key cmp al, #0xe0 jne int09_check_pause xor ax, ax mov ds, ax mov al, BYTE [0x496] ;; mf2_state |= 0x02 or al, #0x02 mov BYTE [0x496], al jmp int09_done int09_check_pause: ;; check for pause key cmp al, #0xe1 jne int09_process_key xor ax, ax mov ds, ax mov al, BYTE [0x496] ;; mf2_state |= 0x01 or al, #0x01 mov BYTE [0x496], al jmp int09_done int09_process_key: mov bx, #0xf000 mov ds, bx call _int09_function int09_done: popa pop ds cli call eoi_master_pic int09_finish: mov al, #0xAE ;;enable keyboard out #0x64, al pop ax iret ;---------------------------------------- ;- INT 13h Diskette Service Entry Point - ;---------------------------------------- .org 0xec59 int13_diskette: jmp int13_noeltorito ;--------------------------------------------- ;- INT 0Eh Diskette Hardware ISR Entry Point - ;--------------------------------------------- .org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point int0e_handler: push ax push dx mov dx, #0x03f4 in al, dx and al, #0xc0 cmp al, #0xc0 je int0e_normal mov dx, #0x03f5 mov al, #0x08 ; sense interrupt status out dx, al int0e_loop1: mov dx, #0x03f4 in al, dx and al, #0xc0 cmp al, #0xc0 jne int0e_loop1 int0e_loop2: mov dx, #0x03f5 in al, dx mov dx, #0x03f4 in al, dx and al, #0xc0 cmp al, #0xc0 je int0e_loop2 int0e_normal: push ds xor ax, ax ;; segment 0000 mov ds, ax call eoi_master_pic mov al, 0x043e or al, #0x80 ;; diskette interrupt has occurred mov 0x043e, al pop ds pop dx pop ax iret .org 0xefc7 ; Diskette Controller Parameter Table diskette_param_table: ;; Since no provisions are made for multiple drive types, most ;; values in this table are ignored. I set parameters for 1.44M ;; floppy here db 0xAF db 0x02 ;; head load time 0000001, DMA used db 0x25 db 0x02 db 18 db 0x1B db 0xFF db 0x6C db 0xF6 db 0x0F db 0x08 ;---------------------------------------- ;- INT17h : Printer Service Entry Point - ;---------------------------------------- .org 0xefd2 int17_handler: push ds pusha xor ax, ax mov ds, ax call _int17_function popa pop ds iret diskette_param_table2: ;; New diskette parameter table adding 3 parameters from IBM ;; Since no provisions are made for multiple drive types, most ;; values in this table are ignored. I set parameters for 1.44M ;; floppy here db 0xAF db 0x02 ;; head load time 0000001, DMA used db 0x25 db 0x02 db 18 db 0x1B db 0xFF db 0x6C db 0xF6 db 0x0F db 0x08 db 79 ;; maximum track db 0 ;; data transfer rate db 4 ;; drive type in cmos .org 0xf045 ; INT 10 Functions 0-Fh Entry Point HALT(__LINE__) iret ;---------- ;- INT10h - ;---------- .org 0xf065 ; INT 10h Video Support Service Entry Point int10_handler: ;; dont do anything, since the VGA BIOS handles int10h requests iret .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh) ;---------- ;- INT12h - ;---------- .org 0xf841 ; INT 12h Memory Size Service Entry Point ; ??? different for Pentium (machine check)? int12_handler: push ds mov ax, #0x0040 mov ds, ax mov ax, 0x0013 pop ds iret ;---------- ;- INT11h - ;---------- .org 0xf84d ; INT 11h Equipment List Service Entry Point int11_handler: push ds mov ax, #0x0040 mov ds, ax mov ax, 0x0010 pop ds iret ;---------- ;- INT15h - ;---------- .org 0xf859 ; INT 15h System Services Entry Point int15_handler: pushf #if BX_APM cmp ah, #0x53 je apm_call #endif push ds push es cmp ah, #0x86 je int15_handler32 cmp ah, #0xE8 je int15_handler32 pusha #if BX_USE_PS2_MOUSE cmp ah, #0xC2 je int15_handler_mouse #endif call _int15_function int15_handler_mouse_ret: popa int15_handler32_ret: pop es pop ds popf jmp iret_modify_cf #if BX_APM apm_call: jmp _apmreal_entry #endif #if BX_USE_PS2_MOUSE int15_handler_mouse: call _int15_function_mouse jmp int15_handler_mouse_ret #endif int15_handler32: pushad call _int15_function32 popad jmp int15_handler32_ret ;; Protected mode IDT descriptor ;; ;; I just make the limit 0, so the machine will shutdown ;; if an exception occurs during protected mode memory ;; transfers. ;; ;; Set base to f0000 to correspond to beginning of BIOS, ;; in case I actually define an IDT later ;; Set limit to 0 pmode_IDT_info: dw 0x0000 ;; limit 15:00 dw 0x0000 ;; base 15:00 db 0x0f ;; base 23:16 ;; Real mode IDT descriptor ;; ;; Set to typical real-mode values. ;; base = 000000 ;; limit = 03ff rmode_IDT_info: dw 0x03ff ;; limit 15:00 dw 0x0000 ;; base 15:00 db 0x00 ;; base 23:16 ;---------- ;- INT1Ah - ;---------- .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point int1a_handler: #if BX_TCGBIOS cmp ah, #0xbb jne no_tcg pushf push ds push es pushad call _int1a_function32 popad pop es pop ds popf iret no_tcg: #endif #if BX_PCIBIOS cmp ah, #0xb1 jne int1a_normal call pcibios_real jc pcibios_error retf 2 pcibios_error: mov bl, ah mov ah, #0xb1 push ds pusha mov ax, ss ; set readable descriptor to ds, for calling pcibios mov ds, ax ; on 16bit protected mode. jmp int1a_callfunction int1a_normal: #endif push ds pusha xor ax, ax mov ds, ax int1a_callfunction: call _int1a_function popa pop ds iret ;; ;; int70h: IRQ8 - CMOS RTC ;; int70_handler: push ds pushad xor ax, ax mov ds, ax call _int70_function popad pop ds iret ;--------- ;- INT08 - ;--------- .org 0xfea5 ; INT 08h System Timer ISR Entry Point int08_handler: sti push eax push ds xor ax, ax mov ds, ax ;; time to turn off drive(s)? mov al,0x0440 or al,al jz int08_floppy_off dec al mov 0x0440,al jnz int08_floppy_off ;; turn motor(s) off push dx mov dx,#0x03f2 in al,dx and al,#0xcf out dx,al pop dx int08_floppy_off: mov eax, 0x046c ;; get ticks dword inc eax ;; compare eax to one days worth of timer ticks at 18.2 hz cmp eax, #0x001800B0 jb int08_store_ticks ;; there has been a midnight rollover at this point xor eax, eax ;; zero out counter inc BYTE 0x0470 ;; increment rollover flag int08_store_ticks: mov 0x046c, eax ;; store new ticks dword ;; chain to user timer tick INT #0x1c //pushf //;; call_ep [ds:loc] //CALL_EP( 0x1c << 2 ) int #0x1c cli call eoi_master_pic pop ds pop eax iret .org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST .org 0xff00 .ascii BIOS_COPYRIGHT_STRING ;------------------------------------------------ ;- IRET Instruction for Dummy Interrupt Handler - ;------------------------------------------------ .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler dummy_iret_handler: iret .org 0xff54 ; INT 05h Print Screen Service Entry Point HALT(__LINE__) iret #ifdef HVMTEST .org 0xffe0 jmp 0xf000:post; #endif .org 0xfff0 ; Power-up Entry Point #ifdef HVMTEST jmp 0xd000:0x0003; #else jmp 0xf000:post #endif .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY .ascii BIOS_BUILD_DATE .org 0xfffe ; System Model ID db SYS_MODEL_ID db 0x00 ; filler .org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters) ASM_END /* * This font comes from the fntcol16.zip package (c) by Joseph Gil * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip * This font is public domain */ static Bit8u vgafont8[128*8]= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, }; ASM_START .org 0xcff0 bios_table_area_end: // bcc-generated data will be placed here ASM_END xen-4.9.2/tools/firmware/rombios/biossums.c0000664000175000017500000003002013256712137017106 0ustar smbsmb/* biossums.c --- written by Eike W. */ #include #include typedef unsigned char byte; void check( int value, char* message ); #define LEN_BIOS_DATA 0x10000 #define MAX_OFFSET (LEN_BIOS_DATA - 1) #define BIOS_OFFSET 0xFFFF long chksum_bios_get_offset( byte* data, long offset ); byte chksum_bios_calc_value( byte* data, long offset ); byte chksum_bios_get_value( byte* data, long offset ); void chksum_bios_set_value( byte* data, long offset, byte value ); #define _32__LEN 9 #define _32__CHKSUM 10 #define _32__MINHDR 16 long chksum__32__get_offset( byte* data, long offset ); byte chksum__32__calc_value( byte* data, long offset ); byte chksum__32__get_value( byte* data, long offset ); void chksum__32__set_value( byte* data, long offset, byte value ); #define _MP__LEN 8 #define _MP__CHKSUM 10 #define _MP__MINHDR 16 long chksum__mp__get_offset( byte* data, long offset ); byte chksum__mp__calc_value( byte* data, long offset ); byte chksum__mp__get_value( byte* data, long offset ); void chksum__mp__set_value( byte* data, long offset, byte value ); #define PCMP_BASELEN 4 #define PCMP_CHKSUM 7 #define PCMP_EXT_LEN 40 #define PCMP_EXT_CHKSUM 42 #define PCMP_MINHDR 42 long chksum_pcmp_get_offset( byte* data, long offset ); byte chksum_pcmp_calc_value( byte* data, long offset ); byte chksum_pcmp_get_value( byte* data, long offset ); void chksum_pcmp_set_value( byte* data, long offset, byte value ); #define _PIR_LEN 6 #define _PIR_CHKSUM 31 #define _PIR_MINHDR 32 long chksum__pir_get_offset( byte *data, long offset ); byte chksum__pir_calc_value( byte* data, long offset ); byte chksum__pir_get_value( byte* data, long offset ); void chksum__pir_set_value( byte* data, long offset, byte value ); byte bios_data[LEN_BIOS_DATA]; int main( int argc, char* argv[] ) { FILE* stream; long offset, tmp_offset; byte cur_val = 0, new_val = 0; int hits; if( argc != 2 ) { printf( "Error. Need a file-name as an argument.\n" ); exit( EXIT_FAILURE ); } if(( stream = fopen( argv[1], "rb" )) == NULL ) { printf( "Error opening %s for reading.\n", argv[1] ); exit( EXIT_FAILURE ); } if( fread( bios_data, 1, LEN_BIOS_DATA, stream ) < LEN_BIOS_DATA ) { printf( "Error reading 64KBytes from %s.\n", argv[1] ); fclose( stream ); exit( EXIT_FAILURE ); } fclose( stream ); hits = 0; offset = 0L; while( (tmp_offset = chksum__32__get_offset( bios_data, offset )) != -1L ) { offset = tmp_offset; cur_val = chksum__32__get_value( bios_data, offset ); new_val = chksum__32__calc_value( bios_data, offset ); printf( "\n\nPCI-Bios header at: 0x%4lX\n", offset ); printf( "Current checksum: 0x%02X\n", cur_val ); printf( "Calculated checksum: 0x%02X ", new_val ); hits++; } if( hits == 1 && cur_val != new_val ) { printf( "Setting checksum." ); chksum__32__set_value( bios_data, offset, new_val ); } if( hits >= 2 ) { printf( "Multiple PCI headers! No checksum set." ); } if( hits ) { printf( "\n" ); } hits = 0; offset = 0L; while( (tmp_offset = chksum__mp__get_offset( bios_data, offset )) != -1L ) { offset = tmp_offset; cur_val = chksum__mp__get_value( bios_data, offset ); new_val = chksum__mp__calc_value( bios_data, offset ); printf( "\n\nMP header at: 0x%4lX\n", offset ); printf( "Current checksum: 0x%02X\n", cur_val ); printf( "Calculated checksum: 0x%02X ", new_val ); hits++; } if( hits == 1 && cur_val != new_val ) { printf( "Setting checksum." ); chksum__mp__set_value( bios_data, offset, new_val ); } if( hits >= 2 ) { printf( "Warning! Multiple MP headers. No checksum set." ); } if( hits ) { printf( "\n" ); } hits = 0; offset = 0L; while( (tmp_offset = chksum_pcmp_get_offset( bios_data, offset )) != -1L ) { offset = tmp_offset; cur_val = chksum_pcmp_get_value( bios_data, offset ); new_val = chksum_pcmp_calc_value( bios_data, offset ); printf( "\n\nPCMP header at: 0x%4lX\n", offset ); printf( "Current checksum: 0x%02X\n", cur_val ); printf( "Calculated checksum: 0x%02X ", new_val ); hits++; } if( hits == 1 && cur_val != new_val ) { printf( "Setting checksum." ); chksum_pcmp_set_value( bios_data, offset, new_val ); } if( hits >= 2 ) { printf( "Warning! Multiple PCMP headers. No checksum set." ); } if( hits ) { printf( "\n" ); } hits = 0; offset = 0L; while( (tmp_offset = chksum__pir_get_offset( bios_data, offset )) != -1L ) { offset = tmp_offset; cur_val = chksum__pir_get_value( bios_data, offset ); new_val = chksum__pir_calc_value( bios_data, offset ); printf( "\n\n$PIR header at: 0x%4lX\n", offset ); printf( "Current checksum: 0x%02X\n", cur_val ); printf( "Calculated checksum: 0x%02X\n ", new_val ); hits++; } if( hits == 1 && cur_val != new_val ) { printf( "Setting checksum." ); chksum__pir_set_value( bios_data, offset, new_val ); } if( hits >= 2 ) { printf( "Warning! Multiple $PIR headers. No checksum set." ); } if( hits ) { printf( "\n" ); } offset = 0L; offset = chksum_bios_get_offset( bios_data, offset ); cur_val = chksum_bios_get_value( bios_data, offset ); new_val = chksum_bios_calc_value( bios_data, offset ); printf( "\n\nBios checksum at: 0x%4lX\n", offset ); printf( "Current checksum: 0x%02X\n", cur_val ); printf( "Calculated checksum: 0x%02X ", new_val ); if( cur_val != new_val ) { printf( "Setting checksum." ); chksum_bios_set_value( bios_data, offset, new_val ); } printf( "\n" ); if(( stream = fopen( argv[1], "wb" )) == NULL ) { printf( "Error opening %s for writing.\n", argv[1] ); exit( EXIT_FAILURE ); } if( fwrite( bios_data, 1, LEN_BIOS_DATA, stream ) < LEN_BIOS_DATA ) { printf( "Error writing 64KBytes to %s.\n", argv[1] ); fclose( stream ); exit( EXIT_FAILURE ); } fclose( stream ); return( EXIT_SUCCESS ); } void check( int okay, char* message ) { if( !okay ) { printf( "\n\nError. %s.\n", message ); exit( EXIT_FAILURE ); } } long chksum_bios_get_offset( byte* data, long offset ) { return( BIOS_OFFSET ); } byte chksum_bios_calc_value( byte* data, long offset ) { int i; byte sum; sum = 0; for( i = 0; i < MAX_OFFSET; i++ ) { sum = sum + *( data + i ); } sum = -sum; /* iso ensures -s + s == 0 on unsigned types */ return( sum ); } byte chksum_bios_get_value( byte* data, long offset ) { return( *( data + BIOS_OFFSET ) ); } void chksum_bios_set_value( byte* data, long offset, byte value ) { *( data + BIOS_OFFSET ) = value; } byte chksum__32__calc_value( byte* data, long offset ) { int i; int len; byte sum; check( offset + _32__MINHDR <= MAX_OFFSET, "_32_ header out of bounds" ); len = *( data + offset + _32__LEN ) << 4; check( offset + len <= MAX_OFFSET, "_32_ header-length out of bounds" ); sum = 0; for( i = 0; i < len; i++ ) { if( i != _32__CHKSUM ) { sum = sum + *( data + offset + i ); } } sum = -sum; return( sum ); } long chksum__32__get_offset( byte* data, long offset ) { long result = -1L; offset = offset + 0x0F; offset = offset & ~( 0x0F ); while( offset + 16 < MAX_OFFSET ) { offset = offset + 16; if( *( data + offset + 0 ) == '_' && \ *( data + offset + 1 ) == '3' && \ *( data + offset + 2 ) == '2' && \ *( data + offset + 3 ) == '_' ) { result = offset; break; } } return( result ); } byte chksum__32__get_value( byte* data, long offset ) { check( offset + _32__CHKSUM <= MAX_OFFSET, "PCI-Bios checksum out of bounds" ); return( *( data + offset + _32__CHKSUM ) ); } void chksum__32__set_value( byte* data, long offset, byte value ) { check( offset + _32__CHKSUM <= MAX_OFFSET, "PCI-Bios checksum out of bounds" ); *( data + offset + _32__CHKSUM ) = value; } byte chksum__mp__calc_value( byte* data, long offset ) { int i; int len; byte sum; check( offset + _MP__MINHDR <= MAX_OFFSET, "_MP_ header out of bounds" ); len = *( data + offset + _MP__LEN ) << 4; check( offset + len <= MAX_OFFSET, "_MP_ header-length out of bounds" ); sum = 0; for( i = 0; i < len; i++ ) { if( i != _MP__CHKSUM ) { sum = sum + *( data + offset + i ); } } sum = -sum; return( sum ); } long chksum__mp__get_offset( byte* data, long offset ) { long result = -1L; offset = offset + 0x0F; offset = offset & ~( 0x0F ); while( offset + 16 < MAX_OFFSET ) { offset = offset + 16; if( *( data + offset + 0 ) == '_' && \ *( data + offset + 1 ) == 'M' && \ *( data + offset + 2 ) == 'P' && \ *( data + offset + 3 ) == '_' ) { result = offset; break; } } return( result ); } byte chksum__mp__get_value( byte* data, long offset ) { check( offset + _MP__CHKSUM <= MAX_OFFSET, "MP checksum out of bounds" ); return( *( data + offset + _MP__CHKSUM ) ); } void chksum__mp__set_value( byte* data, long offset, byte value ) { check( offset + _MP__CHKSUM <= MAX_OFFSET, "MP checksum out of bounds" ); *( data + offset + _MP__CHKSUM ) = value; } byte chksum_pcmp_calc_value( byte* data, long offset ) { int i; int len; byte sum; check( offset + PCMP_MINHDR <= MAX_OFFSET, "PCMP header out of bounds" ); len = *( data + offset + PCMP_BASELEN ) + \ ( *( data + offset + PCMP_BASELEN + 1 ) << 8 ); check( offset + len <= MAX_OFFSET, "PCMP header-length out of bounds" ); if( *( data + offset + PCMP_EXT_LEN ) | \ *( data + offset + PCMP_EXT_LEN + 1 ) | \ *( data + offset + PCMP_EXT_CHKSUM ) ) { check( 0, "PCMP header indicates extended tables (unsupported)" ); } sum = 0; for( i = 0; i < len; i++ ) { if( i != PCMP_CHKSUM ) { sum = sum + *( data + offset + i ); } } sum = -sum; return( sum ); } long chksum_pcmp_get_offset( byte* data, long offset ) { long result = -1L; offset = offset + 0x0F; offset = offset & ~( 0x0F ); while( offset + 16 < MAX_OFFSET ) { offset = offset + 16; if( *( data + offset + 0 ) == 'P' && \ *( data + offset + 1 ) == 'C' && \ *( data + offset + 2 ) == 'M' && \ *( data + offset + 3 ) == 'P' ) { result = offset; break; } } return( result ); } byte chksum_pcmp_get_value( byte* data, long offset ) { check( offset + PCMP_CHKSUM <= MAX_OFFSET, "PCMP checksum out of bounds" ); return( *( data + offset + PCMP_CHKSUM ) ); } void chksum_pcmp_set_value( byte* data, long offset, byte value ) { check( offset + PCMP_CHKSUM <= MAX_OFFSET, "PCMP checksum out of bounds" ); *( data + offset + PCMP_CHKSUM ) = value; } byte chksum__pir_calc_value( byte* data, long offset ) { int i; int len; byte sum; check( offset + _PIR_MINHDR <= MAX_OFFSET, "$PIR header out of bounds" ); len = *( data + offset + _PIR_LEN ) + \ ( *( data + offset + _PIR_LEN + 1 ) << 8 ); check( offset + len <= MAX_OFFSET, "$PIR header-length out of bounds" ); sum = 0; for( i = 0; i < len; i++ ) { if( i != _PIR_CHKSUM ) { sum = sum + *( data + offset + i ); } } sum = -sum; return( sum ); } long chksum__pir_get_offset( byte* data, long offset ) { long result = -1L; offset = offset + 0x0F; offset = offset & ~( 0x0F ); while( offset + 16 < MAX_OFFSET ) { offset = offset + 16; if( *( data + offset + 0 ) == '$' && \ *( data + offset + 1 ) == 'P' && \ *( data + offset + 2 ) == 'I' && \ *( data + offset + 3 ) == 'R' ) { result = offset; break; } } return( result ); } byte chksum__pir_get_value( byte* data, long offset ) { check( offset + _PIR_CHKSUM <= MAX_OFFSET, "$PIR checksum out of bounds" ); return( *( data + offset + _PIR_CHKSUM ) ); } void chksum__pir_set_value( byte* data, long offset, byte value ) { check( offset + _PIR_CHKSUM <= MAX_OFFSET, "$PIR checksum out of bounds" ); *( data + offset + _PIR_CHKSUM ) = value; } xen-4.9.2/tools/firmware/rombios/rombios.h0000664000175000017500000000455613256712137016740 0ustar smbsmb///////////////////////////////////////////////////////////////////////// // $Id: rombios.h,v 1.8 2008/12/04 18:48:33 sshwarts Exp $ ///////////////////////////////////////////////////////////////////////// // // Copyright (C) 2006 Volker Ruppert // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; If not, see . /* define it to include QEMU specific code */ //#define BX_QEMU #define LEGACY #ifndef LEGACY # define BX_ROMBIOS32 1 #else # define BX_ROMBIOS32 0 #endif #define DEBUG_ROMBIOS 0 #define PANIC_PORT 0x400 #define PANIC_PORT2 0x401 #define INFO_PORT 0x402 #define DEBUG_PORT 0x403 #define BIOS_PRINTF_HALT 1 #define BIOS_PRINTF_SCREEN 2 #define BIOS_PRINTF_INFO 4 #define BIOS_PRINTF_DEBUG 8 #define BIOS_PRINTF_ALL (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO) #define BIOS_PRINTF_DEBHALT (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT) #define printf(format, p...) bios_printf(BIOS_PRINTF_SCREEN, format, ##p) // Defines the output macros. // BX_DEBUG goes to INFO port until we can easily choose debug info on a // per-device basis. Debug info are sent only in debug mode #if DEBUG_ROMBIOS # define BX_DEBUG(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p) # define BX_INFO(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p) #else # define BX_DEBUG(format, p...) # define BX_INFO(format, p...) #endif #define BX_PANIC(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p) #define ACPI_DATA_SIZE 0x00010000L #define PM_IO_BASE 0xb000 #define SMB_IO_BASE 0xb100 // Define the application NAME #if define HVMASSIST # define BX_APPNAME "HVMAssist" #elif defined(BX_QEMU) # define BX_APPNAME "QEMU" #elif defined(PLEX86) # define BX_APPNAME "Plex86" #else # define BX_APPNAME "Bochs" #endif xen-4.9.2/tools/firmware/rombios/Makefile0000664000175000017500000000154113256712137016544 0ustar smbsmbXEN_ROOT = $(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk SUBDIRS := 32bit .PHONY: all all: subdirs-all $(MAKE) BIOS-bochs-latest .PHONY: clean clean: subdirs-clean rm -f *.o *.a *.s rombios.bin _rombios*_.c rm -f as86-sym.txt ld86-sym.txt rm -f rombios*.txt rombios*.sym usage biossums rm -f BIOS-bochs-* rm -f $(DEPS) .PHONY: distclean distclean: clean BIOS-bochs-latest: rombios.c biossums 32bitgateway.c tcgbios.c gcc -DBX_SMP_PROCESSORS=1 -E -P $< > _rombios_.c bcc -o rombios.s -C-c -D__i86__ -0 -S _rombios_.c sed -e 's/^\.text//' -e 's/^\.data//' rombios.s > _rombios_.s as86 _rombios_.s -b tmp.bin -u- -w- -g -0 -j -O -l rombios.txt -perl makesym.perl < rombios.txt > rombios.sym mv tmp.bin BIOS-bochs-latest ./biossums BIOS-bochs-latest rm -f _rombios_.s biossums: biossums.c gcc -o biossums biossums.c -include $(DEPS) xen-4.9.2/tools/firmware/rombios/tcgbios.c0000664000175000017500000000453013256712137016703 0ustar smbsmb/* * Implementation of stub functions for calls to the TCG BIOS * extension in 32bit memory area. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (C) IBM Corporation, 2006 * * Author: Stefan Berger */ /******************************************************************* Support for TCPA ACPI logging ******************************************************************/ ASM_START MACRO POST_MEASURE push word #0x000f push #?2 push word #0x000f push #?1 call _tcpa_measure_post add sp, #8 MEND ASM_END void tcpa_do_measure_POSTs() { ASM_START POST_MEASURE(post, nmi) POST_MEASURE(floppy_drive_post, hard_drive_post) POST_MEASURE(hard_drive_post, ebda_post) POST_MEASURE(ebda_post, eoi_jmp_post) POST_MEASURE(eoi_jmp_post, timer_tick_post) POST_MEASURE(timer_tick_post, int76_handler) ret ASM_END } /* * C-dispatcher for the TCG BIOS functions */ #define TCG_MAGIC 0x41504354L void int1a_function32(regs, ES, DS, FLAGS) pushad_regs_t regs; Bit16u ES, DS, FLAGS; { Bit16u rc; BX_DEBUG_INT1A("int1a_32: AX=%04x\n", regs.u.r16.ax); switch (regs.u.r8.ah) { case 0xbb: /* * all functions except for TCG_StatusCheck need to have the * TCG_MAGIC in 'ebx'. */ if (regs.u.r8.al != 0 && regs.u.r32.ebx != TCG_MAGIC) { SET_CF(); return; } switch(regs.u.r8.al) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: TCGInterruptHandler(((Bit32u)get_SS() << 4) + (Bit32u)®s, ES, DS, ((Bit32u)get_SS() << 4) + (Bit32u)&FLAGS); break; default: SET_CF(); } break; default: SET_CF(); break; } BX_DEBUG_INT1A("int1a_32: FLAGS=%04x\n", FLAGS); } xen-4.9.2/tools/firmware/rombios/e820.h0000664000175000017500000000054513256712137015736 0ustar smbsmb#ifndef __ROMBIOS_E820_H__ #define __ROMBIOS_E820_H__ /* * PC BIOS standard E820 types and structure. */ #define E820_RAM 1 #define E820_RESERVED 2 #define E820_ACPI 3 #define E820_NVS 4 struct e820entry { uint64_t addr; uint64_t size; uint32_t type; } __attribute__((packed)); #endif /* __ROMBIOS_E820_H__ */ xen-4.9.2/tools/firmware/rombios/32bitgateway.c0000664000175000017500000001067213256712137017562 0ustar smbsmb/* * Implementation of a gateway into 32bit space. Stub functions * can be called from Bochs BIOS which call functions with a compatible * signature in 32bit space. All interrupts are disabled while in * 32 bit mode. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . * * Copyright (C) IBM Corporation, 2006 * Copyright (c) 2008, Citrix Systems, Inc. * * Author: Stefan Berger * Author: Keir Fraser */ /* * Note: * BCC's ABI does not require to preserve any 16bit registers ax, bx, cs, dx * by a called function. So these registers need not be preserved while * calling a function in 32bit space, either. * * When bcc calls a function with 16bit parameters it pushes 2 bytes onto * the stack for such a parameter. GCC, however, expects 32bit parameters * (4 bytes) even for uint16_t, so casting to 32bit from bcc is a good idea. */ /* At most 32 bytes in argument list to a 32-bit function. */ #define MAX_ARG_BYTES 32 #define REAL_MODE_CODE_OFFSET 0xf0000 /* Definitions of code/data segment descriptors. */ #define PM_32BIT_CS (gdt_entry_pm_32bit_cs - gdt_base) #define PM_16BIT_CS (gdt_entry_pm_16bit_cs - gdt_base) #define PM_32BIT_DS (gdt_entry_pm_32bit_ds - gdt_base) #define PM_16BIT_DS (gdt_entry_pm_16bit_ds - gdt_base) .align 16 gdt_base: .word 0,0 .byte 0,0,0,0 gdt_entry_pm_32bit_cs: .word 0xffff, 0x0000 .byte 0x00, 0x9b, 0xcf, 0x00 gdt_entry_pm_16bit_cs: .word 0xffff, 0x0000 .byte REAL_MODE_CODE_OFFSET >> 16, 0x9b, 0x8f, 0x0 gdt_entry_pm_32bit_ds: .word 0xffff, 0x0000 .byte 0x0, 0x93, 0xcf, 0x0 gdt_entry_pm_16bit_ds: .word 0xffff, 0x0000 .byte 0x0, 0x93, 0x8f, 0x0 gdt_entry_end: protmode_gdtdesc: .word (gdt_entry_end - gdt_base) - 1 .long gdt_base | REAL_MODE_CODE_OFFSET realmode_gdtdesc: .word 0xffff .long 0x0 Upcall: ; Do an upcall into 32 bit space ; ; Input: ; bx: index of function to call ; Ouput: ; dx, ax: 32 bit result of call (even if 'void' is expected) ; Save caller state, stack frame offsets listed below #define esp_off 0 #define ss_off 4 #define es_off 6 #define ds_off 8 #define flags_off 10 #define retaddr_off 12 #define args_off 14 pushf cli push ds push es push ss push esp ; Calculate protected-mode esp from ss:sp and esp, #0xffff xor eax, eax mov ax, ss shl eax, #4 add esp, eax ; Switch to protected mode seg cs lgdt protmode_gdtdesc mov eax, cr0 or al, #0x1 ; protected mode on mov cr0, eax jmpf DWORD (REAL_MODE_CODE_OFFSET|upcall1), #PM_32BIT_CS upcall1: USE32 mov ax, #PM_32BIT_DS mov ds, ax mov es, ax mov ss, ax ; Marshal arguments and call 32-bit function mov ecx, #MAX_ARG_BYTES/4 upcall2: push MAX_ARG_BYTES-4+args_off[esp] loop upcall2 mov eax, [BIOS_INFO_PHYSICAL_ADDRESS + BIOSINFO_OFF_bios32_entry] call eax add esp, #MAX_ARG_BYTES mov ecx, eax ; Result in ecx ; Restore real-mode stack pointer xor eax, eax mov ax, ss_off[esp] mov bx, ax ; Real-mode ss in bx shl eax, 4 sub esp, eax ; Return to real mode jmpf upcall3, #PM_16BIT_CS upcall3: USE16 mov ax, #PM_16BIT_DS mov ds, ax mov es, ax mov ss, ax mov eax, cr0 and al, #0xfe ; protected mode off mov cr0, eax jmpf upcall4, #REAL_MODE_CODE_OFFSET>>4 upcall4: seg cs lgdt realmode_gdtdesc ; Restore real-mode ss mov ss, bx ; Convert result into dx:ax format mov eax, ecx ror eax, #16 mov dx, ax ror eax, #16 ; Restore caller state and return pop esp pop bx ; skip ss pop es pop ds popf ret MACRO DoUpcall mov bx, #?1 jmp Upcall MEND #define X(idx, ret, fn, args...) _ ## fn: DoUpcall(idx) #include "32bitprotos.h" #undef X xen-4.9.2/tools/firmware/seabios-config0000664000175000017500000000340113256712137016242 0ustar smbsmb# # SeaBIOS Configuration # # # General Features # # CONFIG_COREBOOT is not set CONFIG_QEMU=y # CONFIG_CSM is not set CONFIG_QEMU_HARDWARE=y CONFIG_XEN=y CONFIG_THREADS=y CONFIG_RELOCATE_INIT=y CONFIG_BOOTMENU=y CONFIG_BOOTSPLASH=y CONFIG_BOOTORDER=y CONFIG_ENTRY_EXTRASTACK=y CONFIG_MALLOC_UPPERMEMORY=y CONFIG_ROM_SIZE=0 # # Hardware support # CONFIG_ATA=y CONFIG_ATA_DMA=y CONFIG_ATA_PIO32=y CONFIG_AHCI=y CONFIG_SDCARD=y CONFIG_VIRTIO_BLK=y CONFIG_VIRTIO_SCSI=y CONFIG_PVSCSI=y CONFIG_ESP_SCSI=y CONFIG_LSI_SCSI=y CONFIG_MEGASAS=y CONFIG_MPT_SCSI=y CONFIG_FLOPPY=y CONFIG_FLASH_FLOPPY=y CONFIG_PS2PORT=y CONFIG_USB=y CONFIG_USB_UHCI=y CONFIG_USB_OHCI=y CONFIG_USB_EHCI=y CONFIG_USB_XHCI=y CONFIG_USB_MSC=y CONFIG_USB_UAS=y CONFIG_USB_HUB=y CONFIG_USB_KEYBOARD=y CONFIG_USB_MOUSE=y CONFIG_SERIAL=y CONFIG_LPT=y CONFIG_RTC_TIMER=y CONFIG_HARDWARE_IRQ=y CONFIG_USE_SMM=y CONFIG_CALL32_SMM=y CONFIG_MTRR_INIT=y CONFIG_PMTIMER=y CONFIG_TSC_TIMER=y # # BIOS interfaces # CONFIG_DRIVES=y CONFIG_CDROM_BOOT=y CONFIG_CDROM_EMU=y CONFIG_PCIBIOS=y CONFIG_APMBIOS=y CONFIG_PNPBIOS=y CONFIG_OPTIONROMS=y CONFIG_PMM=y CONFIG_BOOT=y CONFIG_KEYBOARD=y CONFIG_KBD_CALL_INT15_4F=y CONFIG_MOUSE=y CONFIG_S3_RESUME=y CONFIG_VGAHOOKS=y # CONFIG_DISABLE_A20 is not set # CONFIG_WRITABLE_UPPERMEMORY is not set CONFIG_TCGBIOS=y # # BIOS Tables # CONFIG_PIRTABLE=y CONFIG_MPTABLE=y CONFIG_SMBIOS=y CONFIG_ACPI=y CONFIG_ACPI_DSDT=y CONFIG_FW_ROMFILE_LOAD=y # # VGA ROM # CONFIG_NO_VGABIOS=y # CONFIG_VGA_STANDARD_VGA is not set # CONFIG_VGA_CIRRUS is not set # CONFIG_VGA_BOCHS is not set # CONFIG_VGA_GEODEGX2 is not set # CONFIG_VGA_GEODELX is not set # CONFIG_BUILD_VGABIOS is not set CONFIG_VGA_EXTRA_STACK_SIZE=512 # # Debugging # CONFIG_DEBUG_LEVEL=1 # CONFIG_DEBUG_SERIAL is not set CONFIG_DEBUG_IO=y xen-4.9.2/tools/firmware/ovmf-makefile0000664000175000017500000000062213256712137016076 0ustar smbsmbXEN_ROOT=$(CURDIR)/../../.. include $(XEN_ROOT)/tools/Rules.mk ifeq ($(debug),y) TARGET=DEBUG else TARGET=RELEASE endif # OVMF build system has its own parallel building support. .NOTPARALLEL: MAKEFLAGS += -j1 .PHONY: all all: build .PHONY: build build: OvmfPkg/build.sh -a X64 -b $(TARGET) -n 4 cp Build/OvmfX64/$(TARGET)_GCC*/FV/OVMF.fd ovmf.bin .PHONY: clean clean: rm -rf ovmf.bin Build/* xen-4.9.2/tools/firmware/vgabios/0000775000175000017500000000000013256712137015063 5ustar smbsmbxen-4.9.2/tools/firmware/vgabios/vgabios.c0000664000175000017500000025012113256712137016662 0ustar smbsmb// ============================================================================================ /* * vgabios.c */ // ============================================================================================ // // Copyright (C) 2001-2008 the LGPL VGABios developers Team // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; If not, see . // // ============================================================================================ // // This VGA Bios is specific to the plex86/bochs Emulated VGA card. // You can NOT drive any physical vga card with it. // // ============================================================================================ // // This file contains code ripped from : // - rombios.c of plex86 // // This VGA Bios contains fonts from : // - fntcol16.zip (c) by Joseph Gil avalable at : // ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip // These fonts are public domain // // This VGA Bios is based on information taken from : // - Kevin Lawton's vga card emulation for bochs/plex86 // - Ralf Brown's interrupts list available at http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html // - Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/ // - Michael Abrash's Graphics Programming Black Book // - Francois Gervais' book "programmation des cartes graphiques cga-ega-vga" edited by sybex // - DOSEMU 1.0.1 source code for several tables values and formulas // // Thanks for patches, comments and ideas to : // - techt@pikeonline.net // // ============================================================================================ #include "vgabios.h" #ifdef VBE #include "vbe.h" #endif #define USE_BX_INFO /* Declares */ static Bit8u read_byte(); static Bit16u read_word(); static void write_byte(); static void write_word(); static Bit8u inb(); static Bit16u inw(); static void outb(); static void outw(); static Bit16u get_SS(); // Output static void printf(); static void unimplemented(); static void unknown(); static Bit8u find_vga_entry(); static void memsetb(); static void memsetw(); static void memcpyb(); static void memcpyw(); static void biosfn_set_video_mode(); static void biosfn_set_cursor_shape(); static void biosfn_set_cursor_pos(); static void biosfn_get_cursor_pos(); static void biosfn_set_active_page(); static void biosfn_scroll(); static void biosfn_read_char_attr(); static void biosfn_write_char_attr(); static void biosfn_write_char_only(); static void biosfn_write_pixel(); static void biosfn_read_pixel(); static void biosfn_write_teletype(); static void biosfn_perform_gray_scale_summing(); static void biosfn_load_text_user_pat(); static void biosfn_load_text_8_14_pat(); static void biosfn_load_text_8_8_pat(); static void biosfn_load_text_8_16_pat(); static void biosfn_load_gfx_8_8_chars(); static void biosfn_load_gfx_user_chars(); static void biosfn_load_gfx_8_14_chars(); static void biosfn_load_gfx_8_8_dd_chars(); static void biosfn_load_gfx_8_16_chars(); static void biosfn_get_font_info(); static void biosfn_alternate_prtsc(); static void biosfn_switch_video_interface(); static void biosfn_enable_video_refresh_control(); static void biosfn_write_string(); static void biosfn_read_state_info(); static void biosfn_read_video_state_size(); static Bit16u biosfn_save_video_state(); static Bit16u biosfn_restore_video_state(); extern Bit8u video_save_pointer_table[]; // This is for compiling with gcc2 and gcc3 #define ASM_START #asm #define ASM_END #endasm ASM_START MACRO SET_INT_VECTOR push ds xor ax, ax mov ds, ax mov ax, ?3 mov ?1*4, ax mov ax, ?2 mov ?1*4+2, ax pop ds MEND ASM_END ASM_START .text .rom .org 0 use16 386 vgabios_start: .byte 0x55, 0xaa /* BIOS signature, required for BIOS extensions */ .byte 0x40 /* BIOS extension length in units of 512 bytes */ vgabios_entry_point: jmp vgabios_init_func #ifdef PCIBIOS .org 0x18 .word vgabios_pci_data #endif // Info from Bart Oldeman .org 0x1e .ascii "IBM" .byte 0x00 vgabios_name: .ascii "Plex86/Bochs VGABios" #ifdef PCIBIOS .ascii " (PCI)" #endif .ascii " " .byte 0x00 vgabios_version: #ifndef VGABIOS_VERS .ascii "current-cvs" #else .ascii VGABIOS_VERS #endif .ascii " " vgabios_date: .ascii VGABIOS_DATE .byte 0x0a,0x0d .byte 0x00 vgabios_copyright: .ascii "(C) 2008 the LGPL VGABios developers Team" .byte 0x0a,0x0d .byte 0x00 vgabios_license: .ascii "This VGA/VBE Bios is released under the GNU LGPL" .byte 0x0a,0x0d .byte 0x0a,0x0d .byte 0x00 vgabios_website: .ascii "Please visit :" .byte 0x0a,0x0d ;;.ascii " . http://www.plex86.org" ;;.byte 0x0a,0x0d .ascii " . http://bochs.sourceforge.net" .byte 0x0a,0x0d .ascii " . http://www.nongnu.org/vgabios" .byte 0x0a,0x0d .byte 0x0a,0x0d .byte 0x00 #ifdef PCIBIOS vgabios_pci_data: .ascii "PCIR" #ifdef CIRRUS .word 0x1013 .word 0x00b8 // CLGD5446 #else #error "Unknown PCI vendor and device id" #endif .word 0 // reserved .word 0x18 // dlen .byte 0 // revision .byte 0x0 // class,hi: vga display .word 0x300 // class,lo: vga display .word 0x40 // bios size .word 1 // revision .byte 0 // intel x86 data .byte 0x80 // last image .word 0 // reserved #endif ;; ============================================================================================ ;; ;; Init Entry point ;; ;; ============================================================================================ vgabios_init_func: ;; init vga card call init_vga_card ;; init basic bios vars call init_bios_area #ifdef VBE ;; init vbe functions call vbe_init #endif ;; set int10 vect SET_INT_VECTOR(0x10, #0xC000, #vgabios_int10_handler) #ifdef CIRRUS call cirrus_init #endif ;; display splash screen call _display_splash_screen ;; init video mode and clear the screen mov ax,#0x0003 int #0x10 ;; show info call _display_info #ifdef VBE ;; show vbe info call vbe_display_info #endif #ifdef CIRRUS ;; show cirrus info call cirrus_display_info #endif retf ASM_END /* * int10 handled here */ ASM_START vgabios_int10_handler: pushf #ifdef DEBUG push es push ds pusha mov bx, #0xc000 mov ds, bx call _int10_debugmsg popa pop ds pop es #endif cmp ah, #0x0f jne int10_test_1A call biosfn_get_video_mode jmp int10_end int10_test_1A: cmp ah, #0x1a jne int10_test_0B call biosfn_group_1A jmp int10_end int10_test_0B: cmp ah, #0x0b jne int10_test_1103 call biosfn_group_0B jmp int10_end int10_test_1103: cmp ax, #0x1103 jne int10_test_12 call biosfn_set_text_block_specifier jmp int10_end int10_test_12: cmp ah, #0x12 jne int10_test_101B cmp bl, #0x10 jne int10_test_BL30 call biosfn_get_ega_info jmp int10_end int10_test_BL30: cmp bl, #0x30 jne int10_test_BL31 call biosfn_select_vert_res jmp int10_end int10_test_BL31: cmp bl, #0x31 jne int10_test_BL32 call biosfn_enable_default_palette_loading jmp int10_end int10_test_BL32: cmp bl, #0x32 jne int10_test_BL33 call biosfn_enable_video_addressing jmp int10_end int10_test_BL33: cmp bl, #0x33 jne int10_test_BL34 call biosfn_enable_grayscale_summing jmp int10_end int10_test_BL34: cmp bl, #0x34 jne int10_normal call biosfn_enable_cursor_emulation jmp int10_end int10_test_101B: cmp ax, #0x101b je int10_normal cmp ah, #0x10 #ifndef VBE jne int10_normal #else jne int10_test_4F #endif call biosfn_group_10 jmp int10_end #ifdef VBE int10_test_4F: cmp ah, #0x4f jne int10_normal cmp al, #0x03 jne int10_test_vbe_05 call vbe_biosfn_return_current_mode jmp int10_end int10_test_vbe_05: cmp al, #0x05 jne int10_test_vbe_06 call vbe_biosfn_display_window_control jmp int10_end int10_test_vbe_06: cmp al, #0x06 jne int10_test_vbe_07 call vbe_biosfn_set_get_logical_scan_line_length jmp int10_end int10_test_vbe_07: cmp al, #0x07 jne int10_test_vbe_08 call vbe_biosfn_set_get_display_start jmp int10_end int10_test_vbe_08: cmp al, #0x08 jne int10_test_vbe_0A call vbe_biosfn_set_get_dac_palette_format jmp int10_end int10_test_vbe_0A: cmp al, #0x0A jne int10_normal call vbe_biosfn_return_protected_mode_interface jmp int10_end #endif int10_normal: push es push ds pusha ;; We have to set ds to access the right data segment mov bx, #0xc000 mov ds, bx call _int10_func popa pop ds pop es int10_end: popf iret ASM_END #include "vgatables.h" #include "vgafonts.h" /* * Boot time harware inits */ ASM_START init_vga_card: ;; switch to color mode and enable CPU access 480 lines mov dx, #0x3C2 mov al, #0xC3 outb dx,al ;; more than 64k 3C4/04 mov dx, #0x3C4 mov al, #0x04 outb dx,al mov dx, #0x3C5 mov al, #0x02 outb dx,al #if defined(USE_BX_INFO) || defined(DEBUG) mov bx, #msg_vga_init push bx call _printf #endif inc sp inc sp ret #if defined(USE_BX_INFO) || defined(DEBUG) msg_vga_init: .ascii "VGABios $Id: vgabios.c,v 1.67 2008/01/27 09:44:12 vruppert Exp $" .byte 0x0d,0x0a,0x00 #endif ASM_END // -------------------------------------------------------------------------------------------- /* * Boot time bios area inits */ ASM_START init_bios_area: push ds mov ax, # BIOSMEM_SEG mov ds, ax ;; init detected hardware BIOS Area mov bx, # BIOSMEM_INITIAL_MODE mov ax, [bx] and ax, #0xffcf ;; set 80x25 color (not clear from RBIL but usual) or ax, #0x0020 mov [bx], ax ;; Just for the first int10 find its children ;; the default char height mov bx, # BIOSMEM_CHAR_HEIGHT mov al, #0x10 mov [bx], al ;; Clear the screen mov bx, # BIOSMEM_VIDEO_CTL mov al, #0x60 mov [bx], al ;; Set the basic screen we have mov bx, # BIOSMEM_SWITCHES mov al, #0xf9 mov [bx], al ;; Set the basic modeset options mov bx, # BIOSMEM_MODESET_CTL mov al, #0x51 mov [bx], al ;; Set the default MSR mov bx, # BIOSMEM_CURRENT_MSR mov al, #0x09 mov [bx], al pop ds ret _video_save_pointer_table: .word _video_param_table .word 0xc000 .word 0 /* XXX: fill it */ .word 0 .word 0 /* XXX: fill it */ .word 0 .word 0 /* XXX: fill it */ .word 0 .word 0 /* XXX: fill it */ .word 0 .word 0 /* XXX: fill it */ .word 0 .word 0 /* XXX: fill it */ .word 0 ASM_END // -------------------------------------------------------------------------------------------- /* * Boot time Splash screen */ static void display_splash_screen() { } // -------------------------------------------------------------------------------------------- /* * Tell who we are */ static void display_info() { ASM_START mov ax,#0xc000 mov ds,ax mov si,#vgabios_name call _display_string mov si,#vgabios_version call _display_string ;;mov si,#vgabios_copyright ;;call _display_string ;;mov si,#crlf ;;call _display_string mov si,#vgabios_license call _display_string mov si,#vgabios_website call _display_string ASM_END } static void display_string() { // Get length of string ASM_START mov ax,ds mov es,ax mov di,si xor cx,cx not cx xor al,al cld repne scasb not cx dec cx push cx mov ax,#0x0300 mov bx,#0x0000 int #0x10 pop cx mov ax,#0x1301 mov bx,#0x000b mov bp,si int #0x10 ASM_END } // -------------------------------------------------------------------------------------------- #ifdef DEBUG static void int10_debugmsg(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS) Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS; { // 0E is write char... if(GET_AH()!=0x0E) printf("vgabios call ah%02x al%02x bx%04x cx%04x dx%04x\n",GET_AH(),GET_AL(),BX,CX,DX); } #endif // -------------------------------------------------------------------------------------------- /* * int10 main dispatcher */ static void int10_func(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS) Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS; { // BIOS functions switch(GET_AH()) { case 0x00: biosfn_set_video_mode(GET_AL()); switch(GET_AL()&0x7F) {case 6: SET_AL(0x3F); break; case 0: case 1: case 2: case 3: case 4: case 5: case 7: SET_AL(0x30); break; default: SET_AL(0x20); } break; case 0x01: biosfn_set_cursor_shape(GET_CH(),GET_CL()); break; case 0x02: biosfn_set_cursor_pos(GET_BH(),DX); break; case 0x03: biosfn_get_cursor_pos(GET_BH(),&CX,&DX); break; case 0x04: // Read light pen pos (unimplemented) #ifdef DEBUG unimplemented(); #endif AX=0x00; BX=0x00; CX=0x00; DX=0x00; break; case 0x05: biosfn_set_active_page(GET_AL()); break; case 0x06: biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_UP); break; case 0x07: biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_DOWN); break; case 0x08: biosfn_read_char_attr(GET_BH(),&AX); break; case 0x09: biosfn_write_char_attr(GET_AL(),GET_BH(),GET_BL(),CX); break; case 0x0A: biosfn_write_char_only(GET_AL(),GET_BH(),GET_BL(),CX); break; case 0x0C: biosfn_write_pixel(GET_BH(),GET_AL(),CX,DX); break; case 0x0D: biosfn_read_pixel(GET_BH(),CX,DX,&AX); break; case 0x0E: // Ralf Brown Interrupt list is WRONG on bh(page) // We do output only on the current page ! biosfn_write_teletype(GET_AL(),0xff,GET_BL(),NO_ATTR); break; case 0x10: // All other functions of group AH=0x10 rewritten in assembler biosfn_perform_gray_scale_summing(BX,CX); break; case 0x11: switch(GET_AL()) { case 0x00: case 0x10: biosfn_load_text_user_pat(GET_AL(),ES,BP,CX,DX,GET_BL(),GET_BH()); break; case 0x01: case 0x11: biosfn_load_text_8_14_pat(GET_AL(),GET_BL()); break; case 0x02: case 0x12: biosfn_load_text_8_8_pat(GET_AL(),GET_BL()); break; case 0x04: case 0x14: biosfn_load_text_8_16_pat(GET_AL(),GET_BL()); break; case 0x20: biosfn_load_gfx_8_8_chars(ES,BP); break; case 0x21: biosfn_load_gfx_user_chars(ES,BP,CX,GET_BL(),GET_DL()); break; case 0x22: biosfn_load_gfx_8_14_chars(GET_BL()); break; case 0x23: biosfn_load_gfx_8_8_dd_chars(GET_BL()); break; case 0x24: biosfn_load_gfx_8_16_chars(GET_BL()); break; case 0x30: biosfn_get_font_info(GET_BH(),&ES,&BP,&CX,&DX); break; #ifdef DEBUG default: unknown(); #endif } break; case 0x12: switch(GET_BL()) { case 0x20: biosfn_alternate_prtsc(); break; case 0x35: biosfn_switch_video_interface(GET_AL(),ES,DX); SET_AL(0x12); break; case 0x36: biosfn_enable_video_refresh_control(GET_AL()); SET_AL(0x12); break; #ifdef DEBUG default: unknown(); #endif } break; case 0x13: biosfn_write_string(GET_AL(),GET_BH(),GET_BL(),CX,GET_DH(),GET_DL(),ES,BP); break; case 0x1B: biosfn_read_state_info(BX,ES,DI); SET_AL(0x1B); break; case 0x1C: switch(GET_AL()) { case 0x00: biosfn_read_video_state_size(CX,&BX); break; case 0x01: biosfn_save_video_state(CX,ES,BX); break; case 0x02: biosfn_restore_video_state(CX,ES,BX); break; #ifdef DEBUG default: unknown(); #endif } SET_AL(0x1C); break; #ifdef VBE case 0x4f: if (vbe_has_vbe_display()) { switch(GET_AL()) { case 0x00: vbe_biosfn_return_controller_information(&AX,ES,DI); break; case 0x01: vbe_biosfn_return_mode_information(&AX,CX,ES,DI); break; case 0x02: vbe_biosfn_set_mode(&AX,BX,ES,DI); break; case 0x04: vbe_biosfn_save_restore_state(&AX, CX, DX, ES, &BX); break; case 0x09: //FIXME #ifdef DEBUG unimplemented(); #endif // function failed AX=0x100; break; case 0x0A: //FIXME #ifdef DEBUG unimplemented(); #endif // function failed AX=0x100; break; default: #ifdef DEBUG unknown(); #endif // function failed AX=0x100; } } else { // No VBE display AX=0x0100; } break; #endif #ifdef DEBUG default: unknown(); #endif } } // ============================================================================================ // // BIOS functions // // ============================================================================================ static void biosfn_set_video_mode(mode) Bit8u mode; {// mode: Bit 7 is 1 if no clear screen // Should we clear the screen ? Bit8u noclearmem=mode&0x80; Bit8u line,mmask,*palette,vpti; Bit16u i,twidth,theightm1,cheight; Bit8u modeset_ctl,video_ctl,vga_switches; Bit16u crtc_addr; #ifdef VBE if (vbe_has_vbe_display()) { dispi_set_enable(VBE_DISPI_DISABLED); } #endif // def VBE // The real mode mode=mode&0x7f; // find the entry in the video modes line=find_vga_entry(mode); #ifdef DEBUG printf("mode search %02x found line %02x\n",mode,line); #endif if(line==0xFF) return; vpti=line_to_vpti[line]; twidth=video_param_table[vpti].twidth; theightm1=video_param_table[vpti].theightm1; cheight=video_param_table[vpti].cheight; // Read the bios vga control video_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL); // Read the bios vga switches vga_switches=read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES); // Read the bios mode set control modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL); // Then we know the number of lines // FIXME // if palette loading (bit 3 of modeset ctl = 0) if((modeset_ctl&0x08)==0) {// Set the PEL mask outb(VGAREG_PEL_MASK,vga_modes[line].pelmask); // Set the whole dac always, from 0 outb(VGAREG_DAC_WRITE_ADDRESS,0x00); // From which palette switch(vga_modes[line].dacmodel) {case 0: palette=&palette0; break; case 1: palette=&palette1; break; case 2: palette=&palette2; break; case 3: palette=&palette3; break; } // Always 256*3 values for(i=0;i<0x0100;i++) {if(i<=dac_regs[vga_modes[line].dacmodel]) {outb(VGAREG_DAC_DATA,palette[(i*3)+0]); outb(VGAREG_DAC_DATA,palette[(i*3)+1]); outb(VGAREG_DAC_DATA,palette[(i*3)+2]); } else {outb(VGAREG_DAC_DATA,0); outb(VGAREG_DAC_DATA,0); outb(VGAREG_DAC_DATA,0); } } if((modeset_ctl&0x02)==0x02) { biosfn_perform_gray_scale_summing(0x00, 0x100); } } // Reset Attribute Ctl flip-flop inb(VGAREG_ACTL_RESET); // Set Attribute Ctl for(i=0;i<=0x13;i++) {outb(VGAREG_ACTL_ADDRESS,i); outb(VGAREG_ACTL_WRITE_DATA,video_param_table[vpti].actl_regs[i]); } outb(VGAREG_ACTL_ADDRESS,0x14); outb(VGAREG_ACTL_WRITE_DATA,0x00); // Set Sequencer Ctl outb(VGAREG_SEQU_ADDRESS,0); outb(VGAREG_SEQU_DATA,0x03); for(i=1;i<=4;i++) {outb(VGAREG_SEQU_ADDRESS,i); outb(VGAREG_SEQU_DATA,video_param_table[vpti].sequ_regs[i - 1]); } // Set Grafx Ctl for(i=0;i<=8;i++) {outb(VGAREG_GRDC_ADDRESS,i); outb(VGAREG_GRDC_DATA,video_param_table[vpti].grdc_regs[i]); } // Set CRTC address VGA or MDA crtc_addr=vga_modes[line].memmodel==MTEXT?VGAREG_MDA_CRTC_ADDRESS:VGAREG_VGA_CRTC_ADDRESS; // Disable CRTC write protection outw(crtc_addr,0x0011); // Set CRTC regs for(i=0;i<=0x18;i++) {outb(crtc_addr,i); outb(crtc_addr+1,video_param_table[vpti].crtc_regs[i]); } // Set the misc register outb(VGAREG_WRITE_MISC_OUTPUT,video_param_table[vpti].miscreg); // Enable video outb(VGAREG_ACTL_ADDRESS,0x20); inb(VGAREG_ACTL_RESET); if(noclearmem==0x00) { if(vga_modes[line].class==TEXT) { memsetw(vga_modes[line].sstart,0,0x0720,0x4000); // 32k } else { if(mode<0x0d) { memsetw(vga_modes[line].sstart,0,0x0000,0x4000); // 32k } else { outb( VGAREG_SEQU_ADDRESS, 0x02 ); mmask = inb( VGAREG_SEQU_DATA ); outb( VGAREG_SEQU_DATA, 0x0f ); // all planes memsetw(vga_modes[line].sstart,0,0x0000,0x8000); // 64k outb( VGAREG_SEQU_DATA, mmask ); } } } // Set the BIOS mem write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE,mode); write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS,twidth); write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,*(Bit16u *)&video_param_table[vpti].slength_l); write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS,crtc_addr); write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS,theightm1); write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,cheight); write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60|noclearmem)); write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES,0xF9); write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)&0x7f); // FIXME We nearly have the good tables. to be reworked write_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX,0x08); // 8 is VGA should be ok for now write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER, video_save_pointer_table); write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER+2, 0xc000); // FIXME write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x00); // Unavailable on vanilla vga, but... write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,0x00); // Unavailable on vanilla vga, but... // Set cursor shape if(vga_modes[line].class==TEXT) { biosfn_set_cursor_shape(0x06,0x07); } // Set cursor pos for page 0..7 for(i=0;i<8;i++) biosfn_set_cursor_pos(i,0x0000); // Set active page 0 biosfn_set_active_page(0x00); // Write the fonts in memory if(vga_modes[line].class==TEXT) { ASM_START ;; copy and activate 8x16 font mov ax, #0x1104 mov bl, #0x00 int #0x10 mov ax, #0x1103 mov bl, #0x00 int #0x10 ASM_END } // Set the ints 0x1F and 0x43 ASM_START SET_INT_VECTOR(0x1f, #0xC000, #_vgafont8+128*8) ASM_END switch(cheight) {case 8: ASM_START SET_INT_VECTOR(0x43, #0xC000, #_vgafont8) ASM_END break; case 14: ASM_START SET_INT_VECTOR(0x43, #0xC000, #_vgafont14) ASM_END break; case 16: ASM_START SET_INT_VECTOR(0x43, #0xC000, #_vgafont16) ASM_END break; } } // -------------------------------------------------------------------------------------------- static void biosfn_set_cursor_shape (CH,CL) Bit8u CH;Bit8u CL; {Bit16u cheight,curs,crtc_addr; Bit8u modeset_ctl; CH&=0x3f; CL&=0x1f; curs=(CH<<8)+CL; write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE,curs); modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL); cheight = read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); if((modeset_ctl&0x01) && (cheight>8) && (CL<8) && (CH<0x20)) { if(CL!=(CH+1)) { CH = ((CH+1) * cheight / 8) -1; } else { CH = ((CL+1) * cheight / 8) - 2; } CL = ((CL+1) * cheight / 8) - 1; } // CTRC regs 0x0a and 0x0b crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); outb(crtc_addr,0x0a); outb(crtc_addr+1,CH); outb(crtc_addr,0x0b); outb(crtc_addr+1,CL); } // -------------------------------------------------------------------------------------------- static void biosfn_set_cursor_pos (page, cursor) Bit8u page;Bit16u cursor; { Bit8u xcurs,ycurs,current; Bit16u nbcols,nbrows,address,crtc_addr; // Should not happen... if(page>7)return; // Bios cursor pos write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*page, cursor); // Set the hardware cursor current=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); if(page==current) { // Get the dimensions nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; // Calculate the address knowing nbcols nbrows and page num address=SCREEN_IO_START(nbcols,nbrows,page)+xcurs+ycurs*nbcols; // CRTC regs 0x0e and 0x0f crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); outb(crtc_addr,0x0e); outb(crtc_addr+1,(address&0xff00)>>8); outb(crtc_addr,0x0f); outb(crtc_addr+1,address&0x00ff); } } // -------------------------------------------------------------------------------------------- static void biosfn_get_cursor_pos (page,shape, pos) Bit8u page;Bit16u *shape;Bit16u *pos; { Bit16u ss=get_SS(); // Default write_word(ss, shape, 0); write_word(ss, pos, 0); if(page>7)return; // FIXME should handle VGA 14/16 lines write_word(ss,shape,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE)); write_word(ss,pos,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2)); } // -------------------------------------------------------------------------------------------- static void biosfn_set_active_page (page) Bit8u page; { Bit16u cursor,dummy,crtc_addr; Bit16u nbcols,nbrows,address; Bit8u mode,line; if(page>7)return; // Get the mode mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); line=find_vga_entry(mode); if(line==0xFF)return; // Get pos curs pos for the right page biosfn_get_cursor_pos(page,&dummy,&cursor); if(vga_modes[line].class==TEXT) { // Get the dimensions nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; // Calculate the address knowing nbcols nbrows and page num address=SCREEN_MEM_START(nbcols,nbrows,page); write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START,address); // Start address address=SCREEN_IO_START(nbcols,nbrows,page); } else { address = page * (*(Bit16u *)&video_param_table[line_to_vpti[line]].slength_l); } // CRTC regs 0x0c and 0x0d crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); outb(crtc_addr,0x0c); outb(crtc_addr+1,(address&0xff00)>>8); outb(crtc_addr,0x0d); outb(crtc_addr+1,address&0x00ff); // And change the BIOS page write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE,page); #ifdef DEBUG printf("Set active page %02x address %04x\n",page,address); #endif // Display the cursor, now the page is active biosfn_set_cursor_pos(page,cursor); } // -------------------------------------------------------------------------------------------- static void vgamem_copy_pl4(xstart,ysrc,ydest,cols,nbcols,cheight) Bit8u xstart;Bit8u ysrc;Bit8u ydest;Bit8u cols;Bit8u nbcols;Bit8u cheight; { Bit16u src,dest; Bit8u i; src=ysrc*cheight*nbcols+xstart; dest=ydest*cheight*nbcols+xstart; outw(VGAREG_GRDC_ADDRESS, 0x0105); for(i=0;i>1)+xstart; dest=((ydest*cheight*nbcols)>>1)+xstart; for(i=0;i>1)*nbcols,0xb800,0x2000+src+(i>>1)*nbcols,cols); else memcpyb(0xb800,dest+(i>>1)*nbcols,0xb800,src+(i>>1)*nbcols,cols); } } // -------------------------------------------------------------------------------------------- static void vgamem_fill_cga(xstart,ystart,cols,nbcols,cheight,attr) Bit8u xstart;Bit8u ystart;Bit8u cols;Bit8u nbcols;Bit8u cheight;Bit8u attr; { Bit16u dest; Bit8u i; dest=((ystart*cheight*nbcols)>>1)+xstart; for(i=0;i>1)*nbcols,attr,cols); else memsetb(0xb800,dest+(i>>1)*nbcols,attr,cols); } } // -------------------------------------------------------------------------------------------- static void biosfn_scroll (nblines,attr,rul,cul,rlr,clr,page,dir) Bit8u nblines;Bit8u attr;Bit8u rul;Bit8u cul;Bit8u rlr;Bit8u clr;Bit8u page;Bit8u dir; { // page == 0xFF if current Bit8u mode,line,cheight,bpp,cols; Bit16u nbcols,nbrows,i; Bit16u address; if(rul>rlr)return; if(cul>clr)return; // Get the mode mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); line=find_vga_entry(mode); if(line==0xFF)return; // Get the dimensions nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); // Get the current page if(page==0xFF) page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); if(rlr>=nbrows)rlr=nbrows-1; if(clr>=nbcols)clr=nbcols-1; if(nblines>nbrows)nblines=0; cols=clr-cul+1; if(vga_modes[line].class==TEXT) { // Compute the address address=SCREEN_MEM_START(nbcols,nbrows,page); #ifdef DEBUG printf("Scroll, address %04x (%04x %04x %02x)\n",address,nbrows,nbcols,page); #endif if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1) { memsetw(vga_modes[line].sstart,address,(Bit16u)attr*0x100+' ',nbrows*nbcols); } else {// if Scroll up if(dir==SCROLL_UP) {for(i=rul;i<=rlr;i++) { if((i+nblines>rlr)||(nblines==0)) memsetw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols); else memcpyw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,vga_modes[line].sstart,((i+nblines)*nbcols+cul)*2,cols); } } else {for(i=rlr;i>=rul;i--) { if((irlr) break; } } } } else { // FIXME gfx mode not complete cheight=video_param_table[line_to_vpti[line]].cheight; switch(vga_modes[line].memmodel) { case PLANAR4: case PLANAR1: if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1) { outw(VGAREG_GRDC_ADDRESS, 0x0205); memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight); outw(VGAREG_GRDC_ADDRESS, 0x0005); } else {// if Scroll up if(dir==SCROLL_UP) {for(i=rul;i<=rlr;i++) { if((i+nblines>rlr)||(nblines==0)) vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr); else vgamem_copy_pl4(cul,i+nblines,i,cols,nbcols,cheight); } } else {for(i=rlr;i>=rul;i--) { if((irlr) break; } } } break; case CGA: bpp=vga_modes[line].pixbits; if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1) { memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight*bpp); } else { if(bpp==2) { cul<<=1; cols<<=1; nbcols<<=1; } // if Scroll up if(dir==SCROLL_UP) {for(i=rul;i<=rlr;i++) { if((i+nblines>rlr)||(nblines==0)) vgamem_fill_cga(cul,i,cols,nbcols,cheight,attr); else vgamem_copy_cga(cul,i+nblines,i,cols,nbcols,cheight); } } else {for(i=rlr;i>=rul;i--) { if((irlr) break; } } } break; #ifdef DEBUG default: printf("Scroll in graphics mode "); unimplemented(); #endif } } } // -------------------------------------------------------------------------------------------- static void biosfn_read_char_attr (page,car) Bit8u page;Bit16u *car; {Bit16u ss=get_SS(); Bit8u xcurs,ycurs,mode,line; Bit16u nbcols,nbrows,address; Bit16u cursor,dummy; // Get the mode mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); line=find_vga_entry(mode); if(line==0xFF)return; // Get the cursor pos for the page biosfn_get_cursor_pos(page,&dummy,&cursor); xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; // Get the dimensions nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); if(vga_modes[line].class==TEXT) { // Compute the address address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; write_word(ss,car,read_word(vga_modes[line].sstart,address)); } else { // FIXME gfx mode #ifdef DEBUG unimplemented(); #endif } } // -------------------------------------------------------------------------------------------- static void write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight) Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u cheight; { Bit8u i,j,mask; Bit8u *fdata; Bit16u addr,dest,src; switch(cheight) {case 14: fdata = &vgafont14; break; case 16: fdata = &vgafont16; break; default: fdata = &vgafont8; } addr=xcurs+ycurs*cheight*nbcols; src = car * cheight; outw(VGAREG_SEQU_ADDRESS, 0x0f02); outw(VGAREG_GRDC_ADDRESS, 0x0205); if(attr&0x80) { outw(VGAREG_GRDC_ADDRESS, 0x1803); } else { outw(VGAREG_GRDC_ADDRESS, 0x0003); } for(i=0;i>j; outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08); read_byte(0xa000,dest); if(fdata[src+i]&mask) { write_byte(0xa000,dest,attr&0x0f); } else { write_byte(0xa000,dest,0x00); } } } ASM_START mov dx, # VGAREG_GRDC_ADDRESS mov ax, #0xff08 out dx, ax mov ax, #0x0005 out dx, ax mov ax, #0x0003 out dx, ax ASM_END } // -------------------------------------------------------------------------------------------- static void write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp) Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u bpp; { Bit8u i,j,mask,data; Bit8u *fdata; Bit16u addr,dest,src; fdata = &vgafont8; addr=(xcurs*bpp)+ycurs*320; src = car * 8; for(i=0;i<8;i++) { dest=addr+(i>>1)*80; if (i & 1) dest += 0x2000; mask = 0x80; if (bpp == 1) { if (attr & 0x80) { data = read_byte(0xb800,dest); } else { data = 0x00; } for(j=0;j<8;j++) { if (fdata[src+i] & mask) { if (attr & 0x80) { data ^= (attr & 0x01) << (7-j); } else { data |= (attr & 0x01) << (7-j); } } mask >>= 1; } write_byte(0xb800,dest,data); } else { while (mask > 0) { if (attr & 0x80) { data = read_byte(0xb800,dest); } else { data = 0x00; } for(j=0;j<4;j++) { if (fdata[src+i] & mask) { if (attr & 0x80) { data ^= (attr & 0x03) << ((3-j)*2); } else { data |= (attr & 0x03) << ((3-j)*2); } } mask >>= 1; } write_byte(0xb800,dest,data); dest += 1; } } } } // -------------------------------------------------------------------------------------------- static void write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols) Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols; { Bit8u i,j,mask,data; Bit8u *fdata; Bit16u addr,dest,src; fdata = &vgafont8; addr=xcurs*8+ycurs*nbcols*64; src = car * 8; for(i=0;i<8;i++) { dest=addr+i*nbcols*8; mask = 0x80; for(j=0;j<8;j++) { data = 0x00; if (fdata[src+i] & mask) { data = attr; } write_byte(0xa000,dest+j,data); mask >>= 1; } } } // -------------------------------------------------------------------------------------------- static void biosfn_write_char_attr (car,page,attr,count) Bit8u car;Bit8u page;Bit8u attr;Bit16u count; { Bit8u cheight,xcurs,ycurs,mode,line,bpp; Bit16u nbcols,nbrows,address; Bit16u cursor,dummy; // Get the mode mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); line=find_vga_entry(mode); if(line==0xFF)return; // Get the cursor pos for the page biosfn_get_cursor_pos(page,&dummy,&cursor); xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; // Get the dimensions nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); if(vga_modes[line].class==TEXT) { // Compute the address address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; dummy=((Bit16u)attr<<8)+car; memsetw(vga_modes[line].sstart,address,dummy,count); } else { // FIXME gfx mode not complete cheight=video_param_table[line_to_vpti[line]].cheight; bpp=vga_modes[line].pixbits; while((count-->0) && (xcurs>8; // Get the dimensions nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); if(vga_modes[line].class==TEXT) { // Compute the address address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; while(count-->0) {write_byte(vga_modes[line].sstart,address,car); address+=2; } } else { // FIXME gfx mode not complete cheight=video_param_table[line_to_vpti[line]].cheight; bpp=vga_modes[line].pixbits; while((count-->0) && (xcurs> (CX & 0x07); outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08); outw(VGAREG_GRDC_ADDRESS, 0x0205); data = read_byte(0xa000,addr); if (AL & 0x80) { outw(VGAREG_GRDC_ADDRESS, 0x1803); } write_byte(0xa000,addr,AL); ASM_START mov dx, # VGAREG_GRDC_ADDRESS mov ax, #0xff08 out dx, ax mov ax, #0x0005 out dx, ax mov ax, #0x0003 out dx, ax ASM_END break; case CGA: if(vga_modes[line].pixbits==2) { addr=(CX>>2)+(DX>>1)*80; } else { addr=(CX>>3)+(DX>>1)*80; } if (DX & 1) addr += 0x2000; data = read_byte(0xb800,addr); if(vga_modes[line].pixbits==2) { attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2); mask = 0x03 << ((3 - (CX & 0x03)) * 2); } else { attr = (AL & 0x01) << (7 - (CX & 0x07)); mask = 0x01 << (7 - (CX & 0x07)); } if (AL & 0x80) { data ^= attr; } else { data &= ~mask; data |= attr; } write_byte(0xb800,addr,data); break; case LINEAR8: addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8); write_byte(0xa000,addr,AL); break; #ifdef DEBUG default: unimplemented(); #endif } } // -------------------------------------------------------------------------------------------- static void biosfn_read_pixel (BH,CX,DX,AX) Bit8u BH;Bit16u CX;Bit16u DX;Bit16u *AX; { Bit8u mode,line,mask,attr,data,i; Bit16u addr; Bit16u ss=get_SS(); // Get the mode mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); line=find_vga_entry(mode); if(line==0xFF)return; if(vga_modes[line].class==TEXT)return; switch(vga_modes[line].memmodel) { case PLANAR4: case PLANAR1: addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); mask = 0x80 >> (CX & 0x07); attr = 0x00; for(i=0;i<4;i++) { outw(VGAREG_GRDC_ADDRESS, (i << 8) | 0x04); data = read_byte(0xa000,addr) & mask; if (data > 0) attr |= (0x01 << i); } break; case CGA: addr=(CX>>2)+(DX>>1)*80; if (DX & 1) addr += 0x2000; data = read_byte(0xb800,addr); if(vga_modes[line].pixbits==2) { attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03; } else { attr = (data >> (7 - (CX & 0x07))) & 0x01; } break; case LINEAR8: addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8); attr=read_byte(0xa000,addr); break; default: #ifdef DEBUG unimplemented(); #endif attr = 0; } write_word(ss,AX,(read_word(ss,AX) & 0xff00) | attr); } // -------------------------------------------------------------------------------------------- static void biosfn_write_teletype (car, page, attr, flag) Bit8u car;Bit8u page;Bit8u attr;Bit8u flag; {// flag = WITH_ATTR / NO_ATTR Bit8u cheight,xcurs,ycurs,mode,line,bpp; Bit16u nbcols,nbrows,address; Bit16u cursor,dummy; // special case if page is 0xff, use current page if(page==0xff) page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); // Get the mode mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE); line=find_vga_entry(mode); if(line==0xFF)return; // Get the cursor pos for the page biosfn_get_cursor_pos(page,&dummy,&cursor); xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; // Get the dimensions nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); switch(car) { case 7: //FIXME should beep break; case 8: if(xcurs>0)xcurs--; break; case '\r': xcurs=0; break; case '\n': ycurs++; break; case '\t': do { biosfn_write_teletype(' ',page,attr,flag); biosfn_get_cursor_pos(page,&dummy,&cursor); xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; }while(xcurs%8==0); break; default: if(vga_modes[line].class==TEXT) { // Compute the address address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; // Write the char write_byte(vga_modes[line].sstart,address,car); if(flag==WITH_ATTR) write_byte(vga_modes[line].sstart,address+1,attr); } else { // FIXME gfx mode not complete cheight=video_param_table[line_to_vpti[line]].cheight; bpp=vga_modes[line].pixbits; switch(vga_modes[line].memmodel) { case PLANAR4: case PLANAR1: write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight); break; case CGA: write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp); break; case LINEAR8: write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols); break; #ifdef DEBUG default: unimplemented(); #endif } } xcurs++; } // Do we need to wrap ? if(xcurs==nbcols) {xcurs=0; ycurs++; } // Do we need to scroll ? if(ycurs==nbrows) { if(vga_modes[line].class==TEXT) { biosfn_scroll(0x01,0x07,0,0,nbrows-1,nbcols-1,page,SCROLL_UP); } else { biosfn_scroll(0x01,0x00,0,0,nbrows-1,nbcols-1,page,SCROLL_UP); } ycurs-=1; } // Set the cursor for the page cursor=ycurs; cursor<<=8; cursor+=xcurs; biosfn_set_cursor_pos(page,cursor); } // -------------------------------------------------------------------------------------------- ASM_START biosfn_get_video_mode: push ds mov ax, # BIOSMEM_SEG mov ds, ax push bx mov bx, # BIOSMEM_CURRENT_PAGE mov al, [bx] pop bx mov bh, al push bx mov bx, # BIOSMEM_VIDEO_CTL mov ah, [bx] and ah, #0x80 mov bx, # BIOSMEM_CURRENT_MODE mov al, [bx] or al, ah mov bx, # BIOSMEM_NB_COLS mov ah, [bx] pop bx pop ds ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_group_10: cmp al, #0x00 jne int10_test_1001 jmp biosfn_set_single_palette_reg int10_test_1001: cmp al, #0x01 jne int10_test_1002 jmp biosfn_set_overscan_border_color int10_test_1002: cmp al, #0x02 jne int10_test_1003 jmp biosfn_set_all_palette_reg int10_test_1003: cmp al, #0x03 jne int10_test_1007 jmp biosfn_toggle_intensity int10_test_1007: cmp al, #0x07 jne int10_test_1008 jmp biosfn_get_single_palette_reg int10_test_1008: cmp al, #0x08 jne int10_test_1009 jmp biosfn_read_overscan_border_color int10_test_1009: cmp al, #0x09 jne int10_test_1010 jmp biosfn_get_all_palette_reg int10_test_1010: cmp al, #0x10 jne int10_test_1012 jmp biosfn_set_single_dac_reg int10_test_1012: cmp al, #0x12 jne int10_test_1013 jmp biosfn_set_all_dac_reg int10_test_1013: cmp al, #0x13 jne int10_test_1015 jmp biosfn_select_video_dac_color_page int10_test_1015: cmp al, #0x15 jne int10_test_1017 jmp biosfn_read_single_dac_reg int10_test_1017: cmp al, #0x17 jne int10_test_1018 jmp biosfn_read_all_dac_reg int10_test_1018: cmp al, #0x18 jne int10_test_1019 jmp biosfn_set_pel_mask int10_test_1019: cmp al, #0x19 jne int10_test_101A jmp biosfn_read_pel_mask int10_test_101A: cmp al, #0x1a jne int10_group_10_unknown jmp biosfn_read_video_dac_state int10_group_10_unknown: #ifdef DEBUG call _unknown #endif ret biosfn_set_single_palette_reg: cmp bl, #0x14 ja no_actl_reg1 push ax push dx mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, bl out dx, al mov al, bh out dx, al mov al, #0x20 out dx, al pop dx pop ax no_actl_reg1: ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_set_overscan_border_color: push bx mov bl, #0x11 call biosfn_set_single_palette_reg pop bx ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_set_all_palette_reg: push ax push bx push cx push dx mov bx, dx mov dx, # VGAREG_ACTL_RESET in al, dx mov cl, #0x00 mov dx, # VGAREG_ACTL_ADDRESS set_palette_loop: mov al, cl out dx, al seg es mov al, [bx] out dx, al inc bx inc cl cmp cl, #0x10 jne set_palette_loop mov al, #0x11 out dx, al seg es mov al, [bx] out dx, al mov al, #0x20 out dx, al pop dx pop cx pop bx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_toggle_intensity: push ax push bx push dx mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x10 out dx, al mov dx, # VGAREG_ACTL_READ_DATA in al, dx and al, #0xf7 and bl, #0x01 shl bl, 3 or al, bl mov dx, # VGAREG_ACTL_ADDRESS out dx, al mov al, #0x20 out dx, al pop dx pop bx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_get_single_palette_reg: cmp bl, #0x14 ja no_actl_reg2 push ax push dx mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, bl out dx, al mov dx, # VGAREG_ACTL_READ_DATA in al, dx mov bh, al mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x20 out dx, al pop dx pop ax no_actl_reg2: ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_read_overscan_border_color: push ax push bx mov bl, #0x11 call biosfn_get_single_palette_reg mov al, bh pop bx mov bh, al pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_get_all_palette_reg: push ax push bx push cx push dx mov bx, dx mov cl, #0x00 get_palette_loop: mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, cl out dx, al mov dx, # VGAREG_ACTL_READ_DATA in al, dx seg es mov [bx], al inc bx inc cl cmp cl, #0x10 jne get_palette_loop mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x11 out dx, al mov dx, # VGAREG_ACTL_READ_DATA in al, dx seg es mov [bx], al mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x20 out dx, al pop dx pop cx pop bx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_set_single_dac_reg: push ax push dx mov dx, # VGAREG_DAC_WRITE_ADDRESS mov al, bl out dx, al mov dx, # VGAREG_DAC_DATA pop ax push ax mov al, ah out dx, al mov al, ch out dx, al mov al, cl out dx, al pop dx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_set_all_dac_reg: push ax push bx push cx push dx mov dx, # VGAREG_DAC_WRITE_ADDRESS mov al, bl out dx, al pop dx push dx mov bx, dx mov dx, # VGAREG_DAC_DATA set_dac_loop: seg es mov al, [bx] out dx, al inc bx seg es mov al, [bx] out dx, al inc bx seg es mov al, [bx] out dx, al inc bx dec cx jnz set_dac_loop pop dx pop cx pop bx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_select_video_dac_color_page: push ax push bx push dx mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x10 out dx, al mov dx, # VGAREG_ACTL_READ_DATA in al, dx and bl, #0x01 jnz set_dac_page and al, #0x7f shl bh, 7 or al, bh mov dx, # VGAREG_ACTL_ADDRESS out dx, al jmp set_actl_normal set_dac_page: push ax mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x14 out dx, al pop ax and al, #0x80 jnz set_dac_16_page shl bh, 2 set_dac_16_page: and bh, #0x0f mov al, bh out dx, al set_actl_normal: mov al, #0x20 out dx, al pop dx pop bx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_read_single_dac_reg: push ax push dx mov dx, # VGAREG_DAC_READ_ADDRESS mov al, bl out dx, al pop ax mov ah, al mov dx, # VGAREG_DAC_DATA in al, dx xchg al, ah push ax in al, dx mov ch, al in al, dx mov cl, al pop dx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_read_all_dac_reg: push ax push bx push cx push dx mov dx, # VGAREG_DAC_READ_ADDRESS mov al, bl out dx, al pop dx push dx mov bx, dx mov dx, # VGAREG_DAC_DATA read_dac_loop: in al, dx seg es mov [bx], al inc bx in al, dx seg es mov [bx], al inc bx in al, dx seg es mov [bx], al inc bx dec cx jnz read_dac_loop pop dx pop cx pop bx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_set_pel_mask: push ax push dx mov dx, # VGAREG_PEL_MASK mov al, bl out dx, al pop dx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_read_pel_mask: push ax push dx mov dx, # VGAREG_PEL_MASK in al, dx mov bl, al pop dx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- ASM_START biosfn_read_video_dac_state: push ax push dx mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x10 out dx, al mov dx, # VGAREG_ACTL_READ_DATA in al, dx mov bl, al shr bl, 7 mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x14 out dx, al mov dx, # VGAREG_ACTL_READ_DATA in al, dx mov bh, al and bh, #0x0f test bl, #0x01 jnz get_dac_16_page shr bh, 2 get_dac_16_page: mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x20 out dx, al pop dx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- static void biosfn_perform_gray_scale_summing (start,count) Bit16u start;Bit16u count; {Bit8u r,g,b; Bit16u i; Bit16u index; inb(VGAREG_ACTL_RESET); outb(VGAREG_ACTL_ADDRESS,0x00); for( index = 0; index < count; index++ ) { // set read address and switch to read mode outb(VGAREG_DAC_READ_ADDRESS,start); // get 6-bit wide RGB data values r=inb( VGAREG_DAC_DATA ); g=inb( VGAREG_DAC_DATA ); b=inb( VGAREG_DAC_DATA ); // intensity = ( 0.3 * Red ) + ( 0.59 * Green ) + ( 0.11 * Blue ) i = ( ( 77*r + 151*g + 28*b ) + 0x80 ) >> 8; if(i>0x3f)i=0x3f; // set write address and switch to write mode outb(VGAREG_DAC_WRITE_ADDRESS,start); // write new intensity value outb( VGAREG_DAC_DATA, i&0xff ); outb( VGAREG_DAC_DATA, i&0xff ); outb( VGAREG_DAC_DATA, i&0xff ); start++; } inb(VGAREG_ACTL_RESET); outb(VGAREG_ACTL_ADDRESS,0x20); } // -------------------------------------------------------------------------------------------- static void get_font_access() { ASM_START mov dx, # VGAREG_SEQU_ADDRESS mov ax, #0x0100 out dx, ax mov ax, #0x0402 out dx, ax mov ax, #0x0704 out dx, ax mov ax, #0x0300 out dx, ax mov dx, # VGAREG_GRDC_ADDRESS mov ax, #0x0204 out dx, ax mov ax, #0x0005 out dx, ax mov ax, #0x0406 out dx, ax ASM_END } static void release_font_access() { ASM_START mov dx, # VGAREG_SEQU_ADDRESS mov ax, #0x0100 out dx, ax mov ax, #0x0302 out dx, ax mov ax, #0x0304 out dx, ax mov ax, #0x0300 out dx, ax mov dx, # VGAREG_READ_MISC_OUTPUT in al, dx and al, #0x01 shl al, 2 or al, #0x0a mov ah, al mov al, #0x06 mov dx, # VGAREG_GRDC_ADDRESS out dx, ax mov ax, #0x0004 out dx, ax mov ax, #0x1005 out dx, ax ASM_END } ASM_START idiv_u: xor dx,dx div bx ret ASM_END static void set_scan_lines(lines) Bit8u lines; { Bit16u crtc_addr,cols,page,vde; Bit8u crtc_r9,ovl,rows; crtc_addr = read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); outb(crtc_addr, 0x09); crtc_r9 = inb(crtc_addr+1); crtc_r9 = (crtc_r9 & 0xe0) | (lines - 1); outb(crtc_addr+1, crtc_r9); if(lines==8) { biosfn_set_cursor_shape(0x06,0x07); } else { biosfn_set_cursor_shape(lines-4,lines-3); } write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, lines); outb(crtc_addr, 0x12); vde = inb(crtc_addr+1); outb(crtc_addr, 0x07); ovl = inb(crtc_addr+1); vde += (((ovl & 0x02) << 7) + ((ovl & 0x40) << 3) + 1); rows = vde / lines; write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, rows-1); cols = read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, rows * cols * 2); } static void biosfn_load_text_user_pat (AL,ES,BP,CX,DX,BL,BH) Bit8u AL;Bit16u ES;Bit16u BP;Bit16u CX;Bit16u DX;Bit8u BL;Bit8u BH; { Bit16u blockaddr,dest,i,src; get_font_access(); blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11); for(i=0;i=0x10) { set_scan_lines(BH); } } static void biosfn_load_text_8_14_pat (AL,BL) Bit8u AL;Bit8u BL; { Bit16u blockaddr,dest,i,src; get_font_access(); blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11); for(i=0;i<0x100;i++) { src = i * 14; dest = blockaddr + i * 32; memcpyb(0xA000, dest, 0xC000, vgafont14+src, 14); } release_font_access(); if(AL>=0x10) { set_scan_lines(14); } } static void biosfn_load_text_8_8_pat (AL,BL) Bit8u AL;Bit8u BL; { Bit16u blockaddr,dest,i,src; get_font_access(); blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11); for(i=0;i<0x100;i++) { src = i * 8; dest = blockaddr + i * 32; memcpyb(0xA000, dest, 0xC000, vgafont8+src, 8); } release_font_access(); if(AL>=0x10) { set_scan_lines(8); } } // -------------------------------------------------------------------------------------------- ASM_START biosfn_set_text_block_specifier: push ax push dx mov dx, # VGAREG_SEQU_ADDRESS mov ah, bl mov al, #0x03 out dx, ax pop dx pop ax ret ASM_END // -------------------------------------------------------------------------------------------- static void biosfn_load_text_8_16_pat (AL,BL) Bit8u AL;Bit8u BL; { Bit16u blockaddr,dest,i,src; get_font_access(); blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11); for(i=0;i<0x100;i++) { src = i * 16; dest = blockaddr + i * 32; memcpyb(0xA000, dest, 0xC000, vgafont16+src, 16); } release_font_access(); if(AL>=0x10) { set_scan_lines(16); } } static void biosfn_load_gfx_8_8_chars (ES,BP) Bit16u ES;Bit16u BP; { #ifdef DEBUG unimplemented(); #endif } static void biosfn_load_gfx_user_chars (ES,BP,CX,BL,DL) Bit16u ES;Bit16u BP;Bit16u CX;Bit8u BL;Bit8u DL; { #ifdef DEBUG unimplemented(); #endif } static void biosfn_load_gfx_8_14_chars (BL) Bit8u BL; { #ifdef DEBUG unimplemented(); #endif } static void biosfn_load_gfx_8_8_dd_chars (BL) Bit8u BL; { #ifdef DEBUG unimplemented(); #endif } static void biosfn_load_gfx_8_16_chars (BL) Bit8u BL; { #ifdef DEBUG unimplemented(); #endif } // -------------------------------------------------------------------------------------------- static void biosfn_get_font_info (BH,ES,BP,CX,DX) Bit8u BH;Bit16u *ES;Bit16u *BP;Bit16u *CX;Bit16u *DX; {Bit16u ss=get_SS(); switch(BH) {case 0x00: write_word(ss,ES,read_word(0x00,0x1f*4)); write_word(ss,BP,read_word(0x00,(0x1f*4)+2)); break; case 0x01: write_word(ss,ES,read_word(0x00,0x43*4)); write_word(ss,BP,read_word(0x00,(0x43*4)+2)); break; case 0x02: write_word(ss,ES,0xC000); write_word(ss,BP,vgafont14); break; case 0x03: write_word(ss,ES,0xC000); write_word(ss,BP,vgafont8); break; case 0x04: write_word(ss,ES,0xC000); write_word(ss,BP,vgafont8+128*8); break; case 0x05: write_word(ss,ES,0xC000); write_word(ss,BP,vgafont14alt); break; case 0x06: write_word(ss,ES,0xC000); write_word(ss,BP,vgafont16); break; case 0x07: write_word(ss,ES,0xC000); write_word(ss,BP,vgafont16alt); break; default: #ifdef DEBUG printf("Get font info BH(%02x) was discarded\n",BH); #endif return; } // Set byte/char of on screen font write_word(ss,CX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)); // Set Highest char row write_word(ss,DX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)); } // -------------------------------------------------------------------------------------------- ASM_START biosfn_get_ega_info: push ds push ax mov ax, # BIOSMEM_SEG mov ds, ax xor ch, ch mov bx, # BIOSMEM_SWITCHES mov cl, [bx] and cl, #0x0f mov bx, # BIOSMEM_CRTC_ADDRESS mov ax, [bx] mov bx, #0x0003 cmp ax, # VGAREG_MDA_CRTC_ADDRESS jne mode_ega_color mov bh, #0x01 mode_ega_color: pop ax pop ds ret ASM_END // -------------------------------------------------------------------------------------------- static void biosfn_alternate_prtsc() { #ifdef DEBUG unimplemented(); #endif } // -------------------------------------------------------------------------------------------- ASM_START biosfn_select_vert_res: ; res : 00 200 lines, 01 350 lines, 02 400 lines push ds push bx push dx mov dl, al mov ax, # BIOSMEM_SEG mov ds, ax mov bx, # BIOSMEM_MODESET_CTL mov al, [bx] mov bx, # BIOSMEM_SWITCHES mov ah, [bx] cmp dl, #0x01 je vert_res_350 jb vert_res_200 cmp dl, #0x02 je vert_res_400 #ifdef DEBUG mov al, dl xor ah, ah push ax mov bx, #msg_vert_res push bx call _printf add sp, #4 #endif jmp set_retcode vert_res_400: ; reset modeset ctl bit 7 and set bit 4 ; set switches bit 3-0 to 0x09 and al, #0x7f or al, #0x10 and ah, #0xf0 or ah, #0x09 jnz set_vert_res vert_res_350: ; reset modeset ctl bit 7 and bit 4 ; set switches bit 3-0 to 0x09 and al, #0x6f and ah, #0xf0 or ah, #0x09 jnz set_vert_res vert_res_200: ; set modeset ctl bit 7 and reset bit 4 ; set switches bit 3-0 to 0x08 and al, #0xef or al, #0x80 and ah, #0xf0 or ah, #0x08 set_vert_res: mov bx, # BIOSMEM_MODESET_CTL mov [bx], al mov bx, # BIOSMEM_SWITCHES mov [bx], ah set_retcode: mov ax, #0x1212 pop dx pop bx pop ds ret #ifdef DEBUG msg_vert_res: .ascii "Select vert res (%02x) was discarded" .byte 0x0d,0x0a,0x00 #endif biosfn_enable_default_palette_loading: push ds push bx push dx mov dl, al and dl, #0x01 shl dl, 3 mov ax, # BIOSMEM_SEG mov ds, ax mov bx, # BIOSMEM_MODESET_CTL mov al, [bx] and al, #0xf7 or al, dl mov [bx], al mov ax, #0x1212 pop dx pop bx pop ds ret biosfn_enable_video_addressing: push bx push dx mov bl, al and bl, #0x01 xor bl, #0x01 shl bl, 1 mov dx, # VGAREG_READ_MISC_OUTPUT in al, dx and al, #0xfd or al, bl mov dx, # VGAREG_WRITE_MISC_OUTPUT out dx, al mov ax, #0x1212 pop dx pop bx ret biosfn_enable_grayscale_summing: push ds push bx push dx mov dl, al and dl, #0x01 xor dl, #0x01 shl dl, 1 mov ax, # BIOSMEM_SEG mov ds, ax mov bx, # BIOSMEM_MODESET_CTL mov al, [bx] and al, #0xfd or al, dl mov [bx], al mov ax, #0x1212 pop dx pop bx pop ds ret biosfn_enable_cursor_emulation: push ds push bx push dx mov dl, al and dl, #0x01 xor dl, #0x01 mov ax, # BIOSMEM_SEG mov ds, ax mov bx, # BIOSMEM_MODESET_CTL mov al, [bx] and al, #0xfe or al, dl mov [bx], al mov ax, #0x1212 pop dx pop bx pop ds ret ASM_END // -------------------------------------------------------------------------------------------- static void biosfn_switch_video_interface (AL,ES,DX) Bit8u AL;Bit16u ES;Bit16u DX; { #ifdef DEBUG unimplemented(); #endif } static void biosfn_enable_video_refresh_control (AL) Bit8u AL; { #ifdef DEBUG unimplemented(); #endif } // -------------------------------------------------------------------------------------------- static void biosfn_write_string (flag,page,attr,count,row,col,seg,offset) Bit8u flag;Bit8u page;Bit8u attr;Bit16u count;Bit8u row;Bit8u col;Bit16u seg;Bit16u offset; { Bit16u newcurs,oldcurs,dummy; Bit8u car,carattr; // Read curs info for the page biosfn_get_cursor_pos(page,&dummy,&oldcurs); // if row=0xff special case : use current cursor position if(row==0xff) {col=oldcurs&0x00ff; row=(oldcurs&0xff00)>>8; } newcurs=row; newcurs<<=8; newcurs+=col; biosfn_set_cursor_pos(page,newcurs); while(count--!=0) { car=read_byte(seg,offset++); if((flag&0x02)!=0) attr=read_byte(seg,offset++); biosfn_write_teletype(car,page,attr,WITH_ATTR); } // Set back curs pos if((flag&0x01)==0) biosfn_set_cursor_pos(page,oldcurs); } // -------------------------------------------------------------------------------------------- ASM_START biosfn_group_1A: cmp al, #0x00 je biosfn_read_display_code cmp al, #0x01 je biosfn_set_display_code #ifdef DEBUG call _unknown #endif ret biosfn_read_display_code: push ds push ax mov ax, # BIOSMEM_SEG mov ds, ax mov bx, # BIOSMEM_DCC_INDEX mov al, [bx] mov bl, al xor bh, bh pop ax mov al, ah pop ds ret biosfn_set_display_code: push ds push ax push bx mov ax, # BIOSMEM_SEG mov ds, ax mov ax, bx mov bx, # BIOSMEM_DCC_INDEX mov [bx], al #ifdef DEBUG mov al, ah xor ah, ah push ax mov bx, #msg_alt_dcc push bx call _printf add sp, #4 #endif pop bx pop ax mov al, ah pop ds ret #ifdef DEBUG msg_alt_dcc: .ascii "Alternate Display code (%02x) was discarded" .byte 0x0d,0x0a,0x00 #endif ASM_END // -------------------------------------------------------------------------------------------- static void biosfn_read_state_info (BX,ES,DI) Bit16u BX;Bit16u ES;Bit16u DI; { // Address of static functionality table write_word(ES,DI+0x00,&static_functionality); write_word(ES,DI+0x02,0xC000); // Hard coded copy from BIOS area. Should it be cleaner ? memcpyb(ES,DI+0x04,BIOSMEM_SEG,0x49,30); memcpyb(ES,DI+0x22,BIOSMEM_SEG,0x84,3); write_byte(ES,DI+0x25,read_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX)); write_byte(ES,DI+0x26,0); write_byte(ES,DI+0x27,16); write_byte(ES,DI+0x28,0); write_byte(ES,DI+0x29,8); write_byte(ES,DI+0x2a,2); write_byte(ES,DI+0x2b,0); write_byte(ES,DI+0x2c,0); write_byte(ES,DI+0x31,3); write_byte(ES,DI+0x32,0); memsetb(ES,DI+0x33,0,13); } // -------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------- static Bit16u biosfn_read_video_state_size2 (CX) Bit16u CX; { Bit16u size; size = 0; if (CX & 1) { size += 0x46; } if (CX & 2) { size += (5 + 8 + 5) * 2 + 6; } if (CX & 4) { size += 3 + 256 * 3 + 1; } return size; } static void biosfn_read_video_state_size (CX, BX) Bit16u CX; Bit16u *BX; { Bit16u ss=get_SS(); write_word(ss, BX, biosfn_read_video_state_size2(CX)); } static Bit16u biosfn_save_video_state (CX,ES,BX) Bit16u CX;Bit16u ES;Bit16u BX; { Bit16u i, v, crtc_addr, ar_index; crtc_addr = read_word(BIOSMEM_SEG, BIOSMEM_CRTC_ADDRESS); if (CX & 1) { write_byte(ES, BX, inb(VGAREG_SEQU_ADDRESS)); BX++; write_byte(ES, BX, inb(crtc_addr)); BX++; write_byte(ES, BX, inb(VGAREG_GRDC_ADDRESS)); BX++; inb(VGAREG_ACTL_RESET); ar_index = inb(VGAREG_ACTL_ADDRESS); write_byte(ES, BX, ar_index); BX++; write_byte(ES, BX, inb(VGAREG_READ_FEATURE_CTL)); BX++; for(i=1;i<=4;i++){ outb(VGAREG_SEQU_ADDRESS, i); write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++; } outb(VGAREG_SEQU_ADDRESS, 0); write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++; for(i=0;i<=0x18;i++) { outb(crtc_addr,i); write_byte(ES, BX, inb(crtc_addr+1)); BX++; } for(i=0;i<=0x13;i++) { inb(VGAREG_ACTL_RESET); outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20)); write_byte(ES, BX, inb(VGAREG_ACTL_READ_DATA)); BX++; } inb(VGAREG_ACTL_RESET); for(i=0;i<=8;i++) { outb(VGAREG_GRDC_ADDRESS,i); write_byte(ES, BX, inb(VGAREG_GRDC_DATA)); BX++; } write_word(ES, BX, crtc_addr); BX+= 2; /* XXX: read plane latches */ write_byte(ES, BX, 0); BX++; write_byte(ES, BX, 0); BX++; write_byte(ES, BX, 0); BX++; write_byte(ES, BX, 0); BX++; } if (CX & 2) { write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)); BX++; write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)); BX += 2; write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE)); BX += 2; write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS)); BX += 2; write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)); BX++; write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)); BX += 2; write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL)); BX++; write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES)); BX++; write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)); BX++; write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE)); BX += 2; for(i=0;i<8;i++) { write_word(ES, BX, read_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i)); BX += 2; } write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START)); BX += 2; write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)); BX++; /* current font */ write_word(ES, BX, read_word(0, 0x1f * 4)); BX += 2; write_word(ES, BX, read_word(0, 0x1f * 4 + 2)); BX += 2; write_word(ES, BX, read_word(0, 0x43 * 4)); BX += 2; write_word(ES, BX, read_word(0, 0x43 * 4 + 2)); BX += 2; } if (CX & 4) { /* XXX: check this */ write_byte(ES, BX, inb(VGAREG_DAC_STATE)); BX++; /* read/write mode dac */ write_byte(ES, BX, inb(VGAREG_DAC_WRITE_ADDRESS)); BX++; /* pix address */ write_byte(ES, BX, inb(VGAREG_PEL_MASK)); BX++; // Set the whole dac always, from 0 outb(VGAREG_DAC_WRITE_ADDRESS,0x00); for(i=0;i<256*3;i++) { write_byte(ES, BX, inb(VGAREG_DAC_DATA)); BX++; } write_byte(ES, BX, 0); BX++; /* color select register */ } return BX; } static Bit16u biosfn_restore_video_state (CX,ES,BX) Bit16u CX;Bit16u ES;Bit16u BX; { Bit16u i, crtc_addr, v, addr1, ar_index; if (CX & 1) { // Reset Attribute Ctl flip-flop inb(VGAREG_ACTL_RESET); crtc_addr = read_word(ES, BX + 0x40); addr1 = BX; BX += 5; for(i=1;i<=4;i++){ outb(VGAREG_SEQU_ADDRESS, i); outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++; } outb(VGAREG_SEQU_ADDRESS, 0); outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++; // Disable CRTC write protection outw(crtc_addr,0x0011); // Set CRTC regs for(i=0;i<=0x18;i++) { if (i != 0x11) { outb(crtc_addr,i); outb(crtc_addr+1, read_byte(ES, BX)); } BX++; } // select crtc base address v = inb(VGAREG_READ_MISC_OUTPUT) & ~0x01; if (crtc_addr = 0x3d4) v |= 0x01; outb(VGAREG_WRITE_MISC_OUTPUT, v); // enable write protection if needed outb(crtc_addr, 0x11); outb(crtc_addr+1, read_byte(ES, BX - 0x18 + 0x11)); // Set Attribute Ctl ar_index = read_byte(ES, addr1 + 0x03); inb(VGAREG_ACTL_RESET); for(i=0;i<=0x13;i++) { outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20)); outb(VGAREG_ACTL_WRITE_DATA, read_byte(ES, BX)); BX++; } outb(VGAREG_ACTL_ADDRESS, ar_index); inb(VGAREG_ACTL_RESET); for(i=0;i<=8;i++) { outb(VGAREG_GRDC_ADDRESS,i); outb(VGAREG_GRDC_DATA, read_byte(ES, BX)); BX++; } BX += 2; /* crtc_addr */ BX += 4; /* plane latches */ outb(VGAREG_SEQU_ADDRESS, read_byte(ES, addr1)); addr1++; outb(crtc_addr, read_byte(ES, addr1)); addr1++; outb(VGAREG_GRDC_ADDRESS, read_byte(ES, addr1)); addr1++; addr1++; outb(crtc_addr - 0x4 + 0xa, read_byte(ES, addr1)); addr1++; } if (CX & 2) { write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE, read_byte(ES, BX)); BX++; write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS, read_word(ES, BX)); BX += 2; write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, read_word(ES, BX)); BX += 2; write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS, read_word(ES, BX)); BX += 2; write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, read_byte(ES, BX)); BX++; write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, read_word(ES, BX)); BX += 2; write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL, read_byte(ES, BX)); BX++; write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES, read_byte(ES, BX)); BX++; write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL, read_byte(ES, BX)); BX++; write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE, read_word(ES, BX)); BX += 2; for(i=0;i<8;i++) { write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i, read_word(ES, BX)); BX += 2; } write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START, read_word(ES, BX)); BX += 2; write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE, read_byte(ES, BX)); BX++; /* current font */ write_word(0, 0x1f * 4, read_word(ES, BX)); BX += 2; write_word(0, 0x1f * 4 + 2, read_word(ES, BX)); BX += 2; write_word(0, 0x43 * 4, read_word(ES, BX)); BX += 2; write_word(0, 0x43 * 4 + 2, read_word(ES, BX)); BX += 2; } if (CX & 4) { BX++; v = read_byte(ES, BX); BX++; outb(VGAREG_PEL_MASK, read_byte(ES, BX)); BX++; // Set the whole dac always, from 0 outb(VGAREG_DAC_WRITE_ADDRESS,0x00); for(i=0;i<256*3;i++) { outb(VGAREG_DAC_DATA, read_byte(ES, BX)); BX++; } BX++; outb(VGAREG_DAC_WRITE_ADDRESS, v); } return BX; } // ============================================================================================ // // Video Utils // // ============================================================================================ // -------------------------------------------------------------------------------------------- static Bit8u find_vga_entry(mode) Bit8u mode; { Bit8u i,line=0xFF; for(i=0;i<=MODE_MAX;i++) if(vga_modes[i].svgamode==mode) {line=i; break; } return line; } /* =========================================================== */ /* * Misc Utils */ /* =========================================================== */ // -------------------------------------------------------------------------------------------- static void memsetb(seg,offset,value,count) Bit16u seg; Bit16u offset; Bit16u value; Bit16u count; { ASM_START push bp mov bp, sp push ax push cx push es push di mov cx, 10[bp] ; count cmp cx, #0x00 je memsetb_end mov ax, 4[bp] ; segment mov es, ax mov ax, 6[bp] ; offset mov di, ax mov al, 8[bp] ; value cld rep stosb memsetb_end: pop di pop es pop cx pop ax pop bp ASM_END } // -------------------------------------------------------------------------------------------- static void memsetw(seg,offset,value,count) Bit16u seg; Bit16u offset; Bit16u value; Bit16u count; { ASM_START push bp mov bp, sp push ax push cx push es push di mov cx, 10[bp] ; count cmp cx, #0x00 je memsetw_end mov ax, 4[bp] ; segment mov es, ax mov ax, 6[bp] ; offset mov di, ax mov ax, 8[bp] ; value cld rep stosw memsetw_end: pop di pop es pop cx pop ax pop bp ASM_END } // -------------------------------------------------------------------------------------------- static void memcpyb(dseg,doffset,sseg,soffset,count) Bit16u dseg; Bit16u doffset; Bit16u sseg; Bit16u soffset; Bit16u count; { ASM_START push bp mov bp, sp push ax push cx push es push di push ds push si mov cx, 12[bp] ; count cmp cx, #0x0000 je memcpyb_end mov ax, 4[bp] ; dsegment mov es, ax mov ax, 6[bp] ; doffset mov di, ax mov ax, 8[bp] ; ssegment mov ds, ax mov ax, 10[bp] ; soffset mov si, ax cld rep movsb memcpyb_end: pop si pop ds pop di pop es pop cx pop ax pop bp ASM_END } // -------------------------------------------------------------------------------------------- static void memcpyw(dseg,doffset,sseg,soffset,count) Bit16u dseg; Bit16u doffset; Bit16u sseg; Bit16u soffset; Bit16u count; { ASM_START push bp mov bp, sp push ax push cx push es push di push ds push si mov cx, 12[bp] ; count cmp cx, #0x0000 je memcpyw_end mov ax, 4[bp] ; dsegment mov es, ax mov ax, 6[bp] ; doffset mov di, ax mov ax, 8[bp] ; ssegment mov ds, ax mov ax, 10[bp] ; soffset mov si, ax cld rep movsw memcpyw_end: pop si pop ds pop di pop es pop cx pop ax pop bp ASM_END } /* =========================================================== */ /* * These functions where ripped from Kevin's rombios.c */ /* =========================================================== */ // -------------------------------------------------------------------------------------------- static Bit8u read_byte(seg, offset) Bit16u seg; Bit16u offset; { ASM_START push bp mov bp, sp push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov al, [bx] ;; al = return value (byte) pop ds pop bx pop bp ASM_END } // -------------------------------------------------------------------------------------------- static Bit16u read_word(seg, offset) Bit16u seg; Bit16u offset; { ASM_START push bp mov bp, sp push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov ax, [bx] ;; ax = return value (word) pop ds pop bx pop bp ASM_END } // -------------------------------------------------------------------------------------------- static void write_byte(seg, offset, data) Bit16u seg; Bit16u offset; Bit8u data; { ASM_START push bp mov bp, sp push ax push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov al, 8[bp] ; data byte mov [bx], al ; write data byte pop ds pop bx pop ax pop bp ASM_END } // -------------------------------------------------------------------------------------------- static void write_word(seg, offset, data) Bit16u seg; Bit16u offset; Bit16u data; { ASM_START push bp mov bp, sp push ax push bx push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov ax, 8[bp] ; data word mov [bx], ax ; write data word pop ds pop bx pop ax pop bp ASM_END } // -------------------------------------------------------------------------------------------- Bit8u inb(port) Bit16u port; { ASM_START push bp mov bp, sp push dx mov dx, 4[bp] in al, dx pop dx pop bp ASM_END } Bit16u inw(port) Bit16u port; { ASM_START push bp mov bp, sp push dx mov dx, 4[bp] in ax, dx pop dx pop bp ASM_END } // -------------------------------------------------------------------------------------------- void outb(port, val) Bit16u port; Bit8u val; { ASM_START push bp mov bp, sp push ax push dx mov dx, 4[bp] mov al, 6[bp] out dx, al pop dx pop ax pop bp ASM_END } // -------------------------------------------------------------------------------------------- void outw(port, val) Bit16u port; Bit16u val; { ASM_START push bp mov bp, sp push ax push dx mov dx, 4[bp] mov ax, 6[bp] out dx, ax pop dx pop ax pop bp ASM_END } Bit16u get_SS() { ASM_START mov ax, ss ASM_END } #ifdef DEBUG void unimplemented() { printf("--> Unimplemented\n"); } void unknown() { printf("--> Unknown int10\n"); } #endif // -------------------------------------------------------------------------------------------- #if defined(USE_BX_INFO) || defined(DEBUG) || defined(CIRRUS_DEBUG) void printf(s) Bit8u *s; { Bit8u c, format_char; Boolean in_format; unsigned format_width, i; Bit16u *arg_ptr; Bit16u arg_seg, arg, digit, nibble, shift_count; arg_ptr = &s; arg_seg = get_SS(); in_format = 0; format_width = 0; while (c = read_byte(0xc000, s)) { if ( c == '%' ) { in_format = 1; format_width = 0; } else if (in_format) { if ( (c>='0') && (c<='9') ) { format_width = (format_width * 10) + (c - '0'); } else if (c == 'x') { arg_ptr++; // increment to next arg arg = read_word(arg_seg, arg_ptr); if (format_width == 0) format_width = 4; i = 0; digit = format_width - 1; for (i=0; i> (4 * digit)) & 0x000f; if (nibble <= 9) outb(0xe9, nibble + '0'); else outb(0xe9, (nibble - 10) + 'A'); digit--; } in_format = 0; } //else if (c == 'd') { // in_format = 0; // } } else { outb(0xe9, c); } s ++; } } #endif #ifdef VBE #include "vbe.c" #endif #ifdef CIRRUS #include "clext.c" #endif // -------------------------------------------------------------------------------------------- ASM_START ;; DATA_SEG_DEFS_HERE ASM_END ASM_START .ascii "vgabios ends here" .byte 0x00 vgabios_end: .byte 0xCB ;; BLOCK_STRINGS_BEGIN ASM_END xen-4.9.2/tools/firmware/vgabios/Notes0000664000175000017500000000045713256712137016104 0ustar smbsmbDevelopment notes ----------------- - need to split video init function 1. set bios variables 2. do the real init with io based on bios variables - characters format switching will set the bios variables and call function #2 above - need to rework the tables as explained in Interrupt list xen-4.9.2/tools/firmware/vgabios/vbe.h0000664000175000017500000002662213256712137016020 0ustar smbsmb#ifndef vbe_h_included #define vbe_h_included #include "vgabios.h" // DISPI helper function void dispi_set_enable(enable); /** VBE int10 API * * See the function descriptions in vbe.c for more information */ Boolean vbe_has_vbe_display(); void vbe_biosfn_return_controller_information(AX, ES, DI); void vbe_biosfn_return_mode_information(AX, CX, ES, DI); void vbe_biosfn_set_mode(AX, BX, ES, DI); void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX); void vbe_biosfn_set_get_palette_data(AX); void vbe_biosfn_return_protected_mode_interface(AX); // The official VBE Information Block typedef struct VbeInfoBlock { Bit8u VbeSignature[4]; Bit16u VbeVersion; Bit16u OemStringPtr_Off; Bit16u OemStringPtr_Seg; Bit8u Capabilities[4]; Bit16u VideoModePtr_Off; Bit16u VideoModePtr_Seg; Bit16u TotalMemory; Bit16u OemSoftwareRev; Bit16u OemVendorNamePtr_Off; Bit16u OemVendorNamePtr_Seg; Bit16u OemProductNamePtr_Off; Bit16u OemProductNamePtr_Seg; Bit16u OemProductRevPtr_Off; Bit16u OemProductRevPtr_Seg; Bit16u Reserved[111]; // used for dynamicly generated mode list Bit8u OemData[256]; } VbeInfoBlock; // This one is for compactly storing a static list of mode info blocks // this saves us 189 bytes per block typedef struct ModeInfoBlockCompact { // Mandatory information for all VBE revisions Bit16u ModeAttributes; Bit8u WinAAttributes; Bit8u WinBAttributes; Bit16u WinGranularity; Bit16u WinSize; Bit16u WinASegment; Bit16u WinBSegment; Bit32u WinFuncPtr; Bit16u BytesPerScanLine; // Mandatory information for VBE 1.2 and above Bit16u XResolution; Bit16u YResolution; Bit8u XCharSize; Bit8u YCharSize; Bit8u NumberOfPlanes; Bit8u BitsPerPixel; Bit8u NumberOfBanks; Bit8u MemoryModel; Bit8u BankSize; Bit8u NumberOfImagePages; Bit8u Reserved_page; // Direct Color fields (required for direct/6 and YUV/7 memory models) Bit8u RedMaskSize; Bit8u RedFieldPosition; Bit8u GreenMaskSize; Bit8u GreenFieldPosition; Bit8u BlueMaskSize; Bit8u BlueFieldPosition; Bit8u RsvdMaskSize; Bit8u RsvdFieldPosition; Bit8u DirectColorModeInfo; // Mandatory information for VBE 2.0 and above Bit32u PhysBasePtr; Bit32u OffScreenMemOffset; Bit16u OffScreenMemSize; // Mandatory information for VBE 3.0 and above Bit16u LinBytesPerScanLine; Bit8u BnkNumberOfPages; Bit8u LinNumberOfPages; Bit8u LinRedMaskSize; Bit8u LinRedFieldPosition; Bit8u LinGreenMaskSize; Bit8u LinGreenFieldPosition; Bit8u LinBlueMaskSize; Bit8u LinBlueFieldPosition; Bit8u LinRsvdMaskSize; Bit8u LinRsvdFieldPosition; Bit32u MaxPixelClock; // Bit8u Reserved[189]; // DO NOT PUT THIS IN HERE because of Compact Mode Info storage in bios } ModeInfoBlockCompact; typedef struct ModeInfoBlock { // Mandatory information for all VBE revisions Bit16u ModeAttributes; Bit8u WinAAttributes; Bit8u WinBAttributes; Bit16u WinGranularity; Bit16u WinSize; Bit16u WinASegment; Bit16u WinBSegment; Bit32u WinFuncPtr; Bit16u BytesPerScanLine; // Mandatory information for VBE 1.2 and above Bit16u XResolution; Bit16u YResolution; Bit8u XCharSize; Bit8u YCharSize; Bit8u NumberOfPlanes; Bit8u BitsPerPixel; Bit8u NumberOfBanks; Bit8u MemoryModel; Bit8u BankSize; Bit8u NumberOfImagePages; Bit8u Reserved_page; // Direct Color fields (required for direct/6 and YUV/7 memory models) Bit8u RedMaskSize; Bit8u RedFieldPosition; Bit8u GreenMaskSize; Bit8u GreenFieldPosition; Bit8u BlueMaskSize; Bit8u BlueFieldPosition; Bit8u RsvdMaskSize; Bit8u RsvdFieldPosition; Bit8u DirectColorModeInfo; // Mandatory information for VBE 2.0 and above Bit32u PhysBasePtr; Bit32u OffScreenMemOffset; Bit16u OffScreenMemSize; // Mandatory information for VBE 3.0 and above Bit16u LinBytesPerScanLine; Bit8u BnkNumberOfPages; Bit8u LinNumberOfPages; Bit8u LinRedMaskSize; Bit8u LinRedFieldPosition; Bit8u LinGreenMaskSize; Bit8u LinGreenFieldPosition; Bit8u LinBlueMaskSize; Bit8u LinBlueFieldPosition; Bit8u LinRsvdMaskSize; Bit8u LinRsvdFieldPosition; Bit32u MaxPixelClock; Bit8u Reserved[189]; } ModeInfoBlock; typedef struct ModeInfoListItem { Bit16u mode; ModeInfoBlockCompact info; } ModeInfoListItem; // VBE Return Status Info // AL #define VBE_RETURN_STATUS_SUPPORTED 0x4F #define VBE_RETURN_STATUS_UNSUPPORTED 0x00 // AH #define VBE_RETURN_STATUS_SUCCESSFULL 0x00 #define VBE_RETURN_STATUS_FAILED 0x01 #define VBE_RETURN_STATUS_NOT_SUPPORTED 0x02 #define VBE_RETURN_STATUS_INVALID 0x03 // VBE Mode Numbers #define VBE_MODE_VESA_DEFINED 0x0100 #define VBE_MODE_REFRESH_RATE_USE_CRTC 0x0800 #define VBE_MODE_LINEAR_FRAME_BUFFER 0x4000 #define VBE_MODE_PRESERVE_DISPLAY_MEMORY 0x8000 // VBE GFX Mode Number #define VBE_VESA_MODE_640X400X8 0x100 #define VBE_VESA_MODE_640X480X8 0x101 #define VBE_VESA_MODE_800X600X4 0x102 #define VBE_VESA_MODE_800X600X8 0x103 #define VBE_VESA_MODE_1024X768X4 0x104 #define VBE_VESA_MODE_1024X768X8 0x105 #define VBE_VESA_MODE_1280X1024X4 0x106 #define VBE_VESA_MODE_1280X1024X8 0x107 #define VBE_VESA_MODE_320X200X1555 0x10D #define VBE_VESA_MODE_320X200X565 0x10E #define VBE_VESA_MODE_320X200X888 0x10F #define VBE_VESA_MODE_640X480X1555 0x110 #define VBE_VESA_MODE_640X480X565 0x111 #define VBE_VESA_MODE_640X480X888 0x112 #define VBE_VESA_MODE_800X600X1555 0x113 #define VBE_VESA_MODE_800X600X565 0x114 #define VBE_VESA_MODE_800X600X888 0x115 #define VBE_VESA_MODE_1024X768X1555 0x116 #define VBE_VESA_MODE_1024X768X565 0x117 #define VBE_VESA_MODE_1024X768X888 0x118 #define VBE_VESA_MODE_1280X1024X1555 0x119 #define VBE_VESA_MODE_1280X1024X565 0x11A #define VBE_VESA_MODE_1280X1024X888 0x11B #define VBE_VESA_MODE_1600X1200X8 0x11C #define VBE_VESA_MODE_1600X1200X1555 0x11D #define VBE_VESA_MODE_1600X1200X565 0x11E #define VBE_VESA_MODE_1600X1200X888 0x11F // BOCHS/PLEX86 'own' mode numbers #define VBE_OWN_MODE_320X200X8888 0x140 #define VBE_OWN_MODE_640X400X8888 0x141 #define VBE_OWN_MODE_640X480X8888 0x142 #define VBE_OWN_MODE_800X600X8888 0x143 #define VBE_OWN_MODE_1024X768X8888 0x144 #define VBE_OWN_MODE_1280X1024X8888 0x145 #define VBE_OWN_MODE_320X200X8 0x146 #define VBE_OWN_MODE_1600X1200X8888 0x147 #define VBE_OWN_MODE_1152X864X8 0x148 #define VBE_OWN_MODE_1152X864X1555 0x149 #define VBE_OWN_MODE_1152X864X565 0x14a #define VBE_OWN_MODE_1152X864X888 0x14b #define VBE_OWN_MODE_1152X864X8888 0x14c #define VBE_VESA_MODE_END_OF_LIST 0xFFFF // Capabilities #define VBE_CAPABILITY_8BIT_DAC 0x0001 #define VBE_CAPABILITY_NOT_VGA_COMPATIBLE 0x0002 #define VBE_CAPABILITY_RAMDAC_USE_BLANK_BIT 0x0004 #define VBE_CAPABILITY_STEREOSCOPIC_SUPPORT 0x0008 #define VBE_CAPABILITY_STEREO_VIA_VESA_EVC 0x0010 // Mode Attributes #define VBE_MODE_ATTRIBUTE_SUPPORTED 0x0001 #define VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE 0x0002 #define VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT 0x0004 #define VBE_MODE_ATTRIBUTE_COLOR_MODE 0x0008 #define VBE_MODE_ATTRIBUTE_GRAPHICS_MODE 0x0010 #define VBE_MODE_ATTRIBUTE_NOT_VGA_COMPATIBLE 0x0020 #define VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW 0x0040 #define VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE 0x0080 #define VBE_MODE_ATTRIBUTE_DOUBLE_SCAN_MODE 0x0100 #define VBE_MODE_ATTRIBUTE_INTERLACE_MODE 0x0200 #define VBE_MODE_ATTRIBUTE_HARDWARE_TRIPLE_BUFFER 0x0400 #define VBE_MODE_ATTRIBUTE_HARDWARE_STEREOSCOPIC_DISPLAY 0x0800 #define VBE_MODE_ATTRIBUTE_DUAL_DISPLAY_START_ADDRESS 0x1000 #define VBE_MODE_ATTTRIBUTE_LFB_ONLY ( VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW | VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE ) // Window attributes #define VBE_WINDOW_ATTRIBUTE_RELOCATABLE 0x01 #define VBE_WINDOW_ATTRIBUTE_READABLE 0x02 #define VBE_WINDOW_ATTRIBUTE_WRITEABLE 0x04 // Memory model #define VBE_MEMORYMODEL_TEXT_MODE 0x00 #define VBE_MEMORYMODEL_CGA_GRAPHICS 0x01 #define VBE_MEMORYMODEL_HERCULES_GRAPHICS 0x02 #define VBE_MEMORYMODEL_PLANAR 0x03 #define VBE_MEMORYMODEL_PACKED_PIXEL 0x04 #define VBE_MEMORYMODEL_NON_CHAIN_4_256 0x05 #define VBE_MEMORYMODEL_DIRECT_COLOR 0x06 #define VBE_MEMORYMODEL_YUV 0x07 // DirectColorModeInfo #define VBE_DIRECTCOLOR_COLOR_RAMP_PROGRAMMABLE 0x01 #define VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE 0x02 // GUEST <-> HOST Communication API // FIXME: either dynamicly ask host for this or put somewhere high in physical memory // like 0xE0000000 #define VBE_DISPI_BANK_ADDRESS 0xA0000 #define VBE_DISPI_BANK_SIZE_KB 64 #define VBE_DISPI_MAX_XRES 2560 #define VBE_DISPI_MAX_YRES 1600 #define VBE_DISPI_IOPORT_INDEX 0x01CE #define VBE_DISPI_IOPORT_DATA 0x01CF #define VBE_DISPI_INDEX_ID 0x0 #define VBE_DISPI_INDEX_XRES 0x1 #define VBE_DISPI_INDEX_YRES 0x2 #define VBE_DISPI_INDEX_BPP 0x3 #define VBE_DISPI_INDEX_ENABLE 0x4 #define VBE_DISPI_INDEX_BANK 0x5 #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 #define VBE_DISPI_INDEX_X_OFFSET 0x8 #define VBE_DISPI_INDEX_Y_OFFSET 0x9 #define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa #define VBE_DISPI_INDEX_LFB_ADDRESS_H 0xb #define VBE_DISPI_INDEX_LFB_ADDRESS_L 0xc #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 #define VBE_DISPI_ID0 0xB0C0 #define VBE_DISPI_ID1 0xB0C1 #define VBE_DISPI_ID2 0xB0C2 #define VBE_DISPI_ID3 0xB0C3 #define VBE_DISPI_ID4 0xB0C4 #define VBE_DISPI_DISABLED 0x00 #define VBE_DISPI_ENABLED 0x01 #define VBE_DISPI_GETCAPS 0x02 #define VBE_DISPI_8BIT_DAC 0x20 #define VBE_DISPI_LFB_ENABLED 0x40 #define VBE_DISPI_NOCLEARMEM 0x80 #endif xen-4.9.2/tools/firmware/vgabios/COPYING0000664000175000017500000006336713256712137016135 0ustar smbsmb GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see . Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! xen-4.9.2/tools/firmware/vgabios/biossums.c0000664000175000017500000001615713256712137017105 0ustar smbsmb/* biossums.c --- written by Eike W. for the Bochs BIOS */ /* adapted for the LGPL'd VGABIOS by vruppert */ /* This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see . */ #include #include #include typedef unsigned char byte; void check( int value, char* message ); #define MAX_BIOS_DATA 0x10000 long chksum_bios_get_offset( byte* data, long offset ); byte chksum_bios_calc_value( byte* data, long offset ); byte chksum_bios_get_value( byte* data, long offset ); void chksum_bios_set_value( byte* data, long offset, byte value ); #define PMID_LEN 20 #define PMID_CHKSUM 19 long chksum_pmid_get_offset( byte* data, long offset ); byte chksum_pmid_calc_value( byte* data, long offset ); byte chksum_pmid_get_value( byte* data, long offset ); void chksum_pmid_set_value( byte* data, long offset, byte value ); #define PCIR_LEN 24 long chksum_pcir_get_offset( byte* data, long offset ); byte bios_data[MAX_BIOS_DATA]; long bios_len; int main(int argc, char* argv[]) { FILE* stream; long offset, tmp_offset, pcir_offset; byte bios_len_byte, cur_val = 0, new_val = 0; int hits, modified; if (argc != 2) { printf( "Error. Need a file-name as an argument.\n" ); exit( EXIT_FAILURE ); } if ((stream = fopen(argv[1], "rb")) == NULL) { printf("Error opening %s for reading.\n", argv[1]); exit(EXIT_FAILURE); } memset(bios_data, 0, MAX_BIOS_DATA); bios_len = fread(bios_data, 1, MAX_BIOS_DATA, stream); if (bios_len > MAX_BIOS_DATA) { printf("Error reading max. 65536 Bytes from %s.\n", argv[1]); fclose(stream); exit(EXIT_FAILURE); } fclose(stream); modified = 0; if (bios_len < 0x8000) { bios_len = 0x8000; modified = 1; } else if ((bios_len & 0x1FF) != 0) { bios_len = (bios_len + 0x200) & ~0x1FF; modified = 1; } bios_len_byte = (byte)(bios_len / 512); if (bios_len_byte != bios_data[2]) { if (modified == 0) { bios_len += 0x200; } bios_data[2] = (byte)(bios_len / 512); modified = 1; } hits = 0; offset = 0L; while( (tmp_offset = chksum_pmid_get_offset( bios_data, offset )) != -1L ) { offset = tmp_offset; cur_val = chksum_pmid_get_value( bios_data, offset ); new_val = chksum_pmid_calc_value( bios_data, offset ); printf( "\nPMID entry at: 0x%4lX\n", offset ); printf( "Current checksum: 0x%02X\n", cur_val ); printf( "Calculated checksum: 0x%02X ", new_val ); hits++; } if ((hits == 1) && (cur_val != new_val)) { printf("Setting checksum."); chksum_pmid_set_value( bios_data, offset, new_val ); if (modified == 0) { bios_len += 0x200; bios_data[2]++; } modified = 1; } if (hits >= 2) { printf( "Multiple PMID entries! No checksum set." ); } if (hits) { printf("\n"); } offset = 0L; pcir_offset = chksum_pcir_get_offset( bios_data, offset ); if (pcir_offset != -1L) { if (bios_data[pcir_offset + 16] != bios_data[2]) { bios_data[pcir_offset + 16] = bios_data[2]; if (modified == 0) { bios_len += 0x200; bios_data[2]++; bios_data[pcir_offset + 16]++; } modified = 1; } } offset = 0L; do { offset = chksum_bios_get_offset(bios_data, offset); cur_val = chksum_bios_get_value(bios_data, offset); new_val = chksum_bios_calc_value(bios_data, offset); if ((cur_val != new_val) && (modified == 0)) { bios_len += 0x200; bios_data[2]++; if (pcir_offset != -1L) { bios_data[pcir_offset + 16]++; } modified = 1; } else { printf("\nBios checksum at: 0x%4lX\n", offset); printf("Current checksum: 0x%02X\n", cur_val); printf("Calculated checksum: 0x%02X ", new_val); if (cur_val != new_val) { printf("Setting checksum."); chksum_bios_set_value(bios_data, offset, new_val); cur_val = new_val; modified = 1; } printf( "\n" ); } } while (cur_val != new_val); if (modified == 1) { if ((stream = fopen( argv[1], "wb")) == NULL) { printf("Error opening %s for writing.\n", argv[1]); exit(EXIT_FAILURE); } if (fwrite(bios_data, 1, bios_len, stream) < bios_len) { printf("Error writing %ld KBytes to %s.\n", bios_len / 1024, argv[1]); fclose(stream); exit(EXIT_FAILURE); } fclose(stream); } return (EXIT_SUCCESS); } void check( int okay, char* message ) { if( !okay ) { printf( "\n\nError. %s.\n", message ); exit( EXIT_FAILURE ); } } long chksum_bios_get_offset( byte* data, long offset ) { return (bios_len - 1); } byte chksum_bios_calc_value( byte* data, long offset ) { int i; byte sum; sum = 0; for( i = 0; i < offset; i++ ) { sum = sum + *( data + i ); } sum = -sum; /* iso ensures -s + s == 0 on unsigned types */ return( sum ); } byte chksum_bios_get_value( byte* data, long offset ) { return( *( data + offset ) ); } void chksum_bios_set_value( byte* data, long offset, byte value ) { *( data + offset ) = value; } byte chksum_pmid_calc_value( byte* data, long offset ) { int i; int len; byte sum; len = PMID_LEN; check((offset + len) <= (bios_len - 1), "PMID entry length out of bounds" ); sum = 0; for( i = 0; i < len; i++ ) { if( i != PMID_CHKSUM ) { sum = sum + *( data + offset + i ); } } sum = -sum; return( sum ); } long chksum_pmid_get_offset( byte* data, long offset ) { long result = -1L; while ((offset + PMID_LEN) < (bios_len - 1)) { offset = offset + 1; if( *( data + offset + 0 ) == 'P' && \ *( data + offset + 1 ) == 'M' && \ *( data + offset + 2 ) == 'I' && \ *( data + offset + 3 ) == 'D' ) { result = offset; break; } } return( result ); } byte chksum_pmid_get_value( byte* data, long offset ) { check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" ); return( *( data + offset + PMID_CHKSUM ) ); } void chksum_pmid_set_value( byte* data, long offset, byte value ) { check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" ); *( data + offset + PMID_CHKSUM ) = value; } long chksum_pcir_get_offset( byte* data, long offset ) { long result = -1L; while ((offset + PCIR_LEN) < (bios_len - 1)) { offset = offset + 1; if( *( data + offset + 0 ) == 'P' && \ *( data + offset + 1 ) == 'C' && \ *( data + offset + 2 ) == 'I' && \ *( data + offset + 3 ) == 'R' ) { result = offset; break; } } return( result ); } xen-4.9.2/tools/firmware/vgabios/dataseghack0000775000175000017500000000103313256712137017245 0ustar smbsmb#!/bin/bash awk \ 'BEGIN { }\ /^\.text/,/DATA_SEG_DEFS_HERE/ { print }\ END { }'\ $1 > temp.awk.1 awk \ 'BEGIN { i = 0; last = "hello" }\ /BLOCK_STRINGS_BEGIN/,/^\.bss/ { if ( i > 1 ) { print last } last = $0; i = i + 1 }\ END { }'\ $1 > temp.awk.2 awk \ 'BEGIN { }\ /DATA_SEG_DEFS_HERE/,/BLOCK_STRINGS_BEGIN/ { print }\ END { }'\ $1 > temp.awk.3 cp $1 $1.orig cat temp.awk.1 temp.awk.2 temp.awk.3 | sed -e 's/^\.data//' -e 's/^\.bss//' -e 's/^\.text//' > $1 /bin/rm -f temp.awk.1 temp.awk.2 temp.awk.3 $1.orig xen-4.9.2/tools/firmware/vgabios/clext.c0000664000175000017500000007742013256712137016360 0ustar smbsmb// // QEMU Cirrus CLGD 54xx VGABIOS Extension. // // Copyright (c) 2004 Makoto Suzuki (suzu) // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; If not, see . // //#define CIRRUS_VESA3_PMINFO #ifdef VBE #undef CIRRUS_VESA3_PMINFO #endif #define PM_BIOSMEM_CURRENT_MODE 0x449 #define PM_BIOSMEM_CRTC_ADDRESS 0x463 #define PM_BIOSMEM_VBE_MODE 0x4BA #define PM_BIOSMEM_VBE_POWER 0x4BC typedef struct { /* + 0 */ unsigned short mode; unsigned short width; unsigned short height; unsigned short depth; /* + 8 */ unsigned short hidden_dac; /* 0x3c6 */ unsigned short *seq; /* 0x3c4 */ unsigned short *graph; /* 0x3ce */ unsigned short *crtc; /* 0x3d4 */ /* +16 */ unsigned char bitsperpixel; unsigned char vesacolortype; unsigned char vesaredmask; unsigned char vesaredpos; unsigned char vesagreenmask; unsigned char vesagreenpos; unsigned char vesabluemask; unsigned char vesabluepos; /* +24 */ unsigned char vesareservedmask; unsigned char vesareservedpos; } cirrus_mode_t; #define CIRRUS_MODE_SIZE 26 /* For VESA BIOS 3.0 */ #define CIRRUS_PM16INFO_SIZE 20 /* VGA */ unsigned short cseq_vga[] = {0x0007,0xffff}; unsigned short cgraph_vga[] = {0x0009,0x000a,0x000b,0xffff}; unsigned short ccrtc_vga[] = {0x001a,0x001b,0x001d,0xffff}; /* extensions */ unsigned short cgraph_svgacolor[] = { 0x0000,0x0001,0x0002,0x0003,0x0004,0x4005,0x0506,0x0f07,0xff08, 0x0009,0x000a,0x000b, 0xffff }; /* 640x480x8 */ unsigned short cseq_640x480x8[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, 0x580b,0x580c,0x580d,0x580e, 0x0412,0x0013,0x2017, 0x331b,0x331c,0x331d,0x331e, 0xffff }; unsigned short ccrtc_640x480x8[] = { 0x2c11, 0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07, 0x4009,0x000c,0x000d, 0xea10,0xdf12,0x5013,0x4014,0xdf15,0x0b16,0xc317,0xff18, 0x001a,0x221b,0x001d, 0xffff }; /* 640x480x16 */ unsigned short cseq_640x480x16[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, 0x580b,0x580c,0x580d,0x580e, 0x0412,0x0013,0x2017, 0x331b,0x331c,0x331d,0x331e, 0xffff }; unsigned short ccrtc_640x480x16[] = { 0x2c11, 0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07, 0x4009,0x000c,0x000d, 0xea10,0xdf12,0xa013,0x4014,0xdf15,0x0b16,0xc317,0xff18, 0x001a,0x221b,0x001d, 0xffff }; /* 640x480x24 */ unsigned short cseq_640x480x24[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507, 0x580b,0x580c,0x580d,0x580e, 0x0412,0x0013,0x2017, 0x331b,0x331c,0x331d,0x331e, 0xffff }; unsigned short ccrtc_640x480x24[] = { 0x2c11, 0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07, 0x4009,0x000c,0x000d, 0xea10,0xdf12,0x0013,0x4014,0xdf15,0x0b16,0xc317,0xff18, 0x001a,0x321b,0x001d, 0xffff }; /* 800x600x8 */ unsigned short cseq_800x600x8[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, 0x230b,0x230c,0x230d,0x230e, 0x0412,0x0013,0x2017, 0x141b,0x141c,0x141d,0x141e, 0xffff }; unsigned short ccrtc_800x600x8[] = { 0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007, 0x6009,0x000c,0x000d, 0x7d10,0x5712,0x6413,0x4014,0x5715,0x9816,0xc317,0xff18, 0x001a,0x221b,0x001d, 0xffff }; /* 800x600x16 */ unsigned short cseq_800x600x16[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, 0x230b,0x230c,0x230d,0x230e, 0x0412,0x0013,0x2017, 0x141b,0x141c,0x141d,0x141e, 0xffff }; unsigned short ccrtc_800x600x16[] = { 0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007, 0x6009,0x000c,0x000d, 0x7d10,0x5712,0xc813,0x4014,0x5715,0x9816,0xc317,0xff18, 0x001a,0x221b,0x001d, 0xffff }; /* 800x600x24 */ unsigned short cseq_800x600x24[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507, 0x230b,0x230c,0x230d,0x230e, 0x0412,0x0013,0x2017, 0x141b,0x141c,0x141d,0x141e, 0xffff }; unsigned short ccrtc_800x600x24[] = { 0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007, 0x6009,0x000c,0x000d, 0x7d10,0x5712,0x2c13,0x4014,0x5715,0x9816,0xc317,0xff18, 0x001a,0x321b,0x001d, 0xffff }; /* 1024x768x8 */ unsigned short cseq_1024x768x8[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, 0x760b,0x760c,0x760d,0x760e, 0x0412,0x0013,0x2017, 0x341b,0x341c,0x341d,0x341e, 0xffff }; unsigned short ccrtc_1024x768x8[] = { 0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507, 0x6009,0x000c,0x000d, 0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18, 0x001a,0x221b,0x001d, 0xffff }; /* 1024x768x16 */ unsigned short cseq_1024x768x16[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, 0x760b,0x760c,0x760d,0x760e, 0x0412,0x0013,0x2017, 0x341b,0x341c,0x341d,0x341e, 0xffff }; unsigned short ccrtc_1024x768x16[] = { 0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507, 0x6009,0x000c,0x000d, 0x0310,0xff12,0x0013,0x4014,0xff15,0x2416,0xc317,0xff18, 0x001a,0x321b,0x001d, 0xffff }; /* 1024x768x24 */ unsigned short cseq_1024x768x24[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507, 0x760b,0x760c,0x760d,0x760e, 0x0412,0x0013,0x2017, 0x341b,0x341c,0x341d,0x341e, 0xffff }; unsigned short ccrtc_1024x768x24[] = { 0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507, 0x6009,0x000c,0x000d, 0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18, 0x001a,0x321b,0x001d, 0xffff }; /* 1280x1024x8 */ unsigned short cseq_1280x1024x8[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, 0x760b,0x760c,0x760d,0x760e, 0x0412,0x0013,0x2017, 0x341b,0x341c,0x341d,0x341e, 0xffff }; unsigned short ccrtc_1280x1024x8[] = { 0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, 0x6009,0x000c,0x000d, 0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18, 0x001a,0x221b,0x001d, 0xffff }; /* 1280x1024x16 */ unsigned short cseq_1280x1024x16[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, 0x760b,0x760c,0x760d,0x760e, 0x0412,0x0013,0x2017, 0x341b,0x341c,0x341d,0x341e, 0xffff }; unsigned short ccrtc_1280x1024x16[] = { 0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, 0x6009,0x000c,0x000d, 0x0310,0xff12,0x4013,0x4014,0xff15,0x2416,0xc317,0xff18, 0x001a,0x321b,0x001d, 0xffff }; /* 1600x1200x8 */ unsigned short cseq_1600x1200x8[] = { 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, 0x760b,0x760c,0x760d,0x760e, 0x0412,0x0013,0x2017, 0x341b,0x341c,0x341d,0x341e, 0xffff }; unsigned short ccrtc_1600x1200x8[] = { 0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, 0x6009,0x000c,0x000d, 0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18, 0x001a,0x221b,0x001d, 0xffff }; cirrus_mode_t cirrus_modes[] = { {0x5f,640,480,8,0x00, cseq_640x480x8,cgraph_svgacolor,ccrtc_640x480x8,8, 4,0,0,0,0,0,0,0,0}, {0x64,640,480,16,0xe1, cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16, 6,5,11,6,5,5,0,0,0}, {0x66,640,480,15,0xf0, cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16, 6,5,10,5,5,5,0,1,15}, {0x71,640,480,24,0xe5, cseq_640x480x24,cgraph_svgacolor,ccrtc_640x480x24,24, 6,8,16,8,8,8,0,0,0}, {0x5c,800,600,8,0x00, cseq_800x600x8,cgraph_svgacolor,ccrtc_800x600x8,8, 4,0,0,0,0,0,0,0,0}, {0x65,800,600,16,0xe1, cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16, 6,5,11,6,5,5,0,0,0}, {0x67,800,600,15,0xf0, cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16, 6,5,10,5,5,5,0,1,15}, {0x60,1024,768,8,0x00, cseq_1024x768x8,cgraph_svgacolor,ccrtc_1024x768x8,8, 4,0,0,0,0,0,0,0,0}, {0x74,1024,768,16,0xe1, cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16, 6,5,11,6,5,5,0,0,0}, {0x68,1024,768,15,0xf0, cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16, 6,5,10,5,5,5,0,1,15}, {0x78,800,600,24,0xe5, cseq_800x600x24,cgraph_svgacolor,ccrtc_800x600x24,24, 6,8,16,8,8,8,0,0,0}, {0x79,1024,768,24,0xe5, cseq_1024x768x24,cgraph_svgacolor,ccrtc_1024x768x24,24, 6,8,16,8,8,8,0,0,0}, {0x6d,1280,1024,8,0x00, cseq_1280x1024x8,cgraph_svgacolor,ccrtc_1280x1024x8,8, 4,0,0,0,0,0,0,0,0}, {0x69,1280,1024,15,0xf0, cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16, 6,5,10,5,5,5,0,1,15}, {0x75,1280,1024,16,0xe1, cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16, 6,5,11,6,5,5,0,0,0}, {0x7b,1600,1200,8,0x00, cseq_1600x1200x8,cgraph_svgacolor,ccrtc_1600x1200x8,8, 4,0,0,0,0,0,0,0,0}, {0xfe,0,0,0,0,cseq_vga,cgraph_vga,ccrtc_vga,0, 0xff,0,0,0,0,0,0,0,0}, {0xff,0,0,0,0,0,0,0,0, 0xff,0,0,0,0,0,0,0,0}, }; unsigned char cirrus_id_table[] = { // 5430 0xA0, 0x32, // 5446 0xB8, 0x39, 0xff, 0xff }; unsigned short cirrus_vesa_modelist[] = { // 640x480x8 0x101, 0x5f, // 640x480x15 0x110, 0x66, // 640x480x16 0x111, 0x64, // 640x480x24 0x112, 0x71, // 800x600x8 0x103, 0x5c, // 800x600x15 0x113, 0x67, // 800x600x16 0x114, 0x65, // 800x600x24 0x115, 0x78, // 1024x768x8 0x105, 0x60, // 1024x768x15 0x116, 0x68, // 1024x768x16 0x117, 0x74, // 1024x768x24 0x118, 0x79, // 1280x1024x8 0x107, 0x6d, // 1280x1024x15 0x119, 0x69, // 1280x1024x16 0x11a, 0x75, // invalid 0xffff,0xffff }; ASM_START cirrus_installed: .ascii "cirrus-compatible VGA is detected" .byte 0x0d,0x0a .byte 0x0d,0x0a,0x00 cirrus_not_installed: .ascii "cirrus-compatible VGA is not detected" .byte 0x0d,0x0a .byte 0x0d,0x0a,0x00 cirrus_vesa_vendorname: cirrus_vesa_productname: cirrus_vesa_oemname: .ascii "VGABIOS Cirrus extension" .byte 0 cirrus_vesa_productrevision: .ascii "1.0" .byte 0 cirrus_init: call cirrus_check jnz no_cirrus SET_INT_VECTOR(0x10, #0xC000, #cirrus_int10_handler) mov al, #0x0f ; memory setup mov dx, #0x3C4 out dx, al inc dx in al, dx and al, #0x18 mov ah, al mov al, #0x0a dec dx out dx, ax mov ax, #0x0007 ; set vga mode out dx, ax mov ax, #0x0431 ; reset bitblt mov dx, #0x3CE out dx, ax mov ax, #0x0031 out dx, ax no_cirrus: ret cirrus_display_info: push ds push si push cs pop ds call cirrus_check mov si, #cirrus_not_installed jnz cirrus_msgnotinstalled mov si, #cirrus_installed cirrus_msgnotinstalled: call _display_string pop si pop ds ret cirrus_check: push ax push dx mov ax, #0x9206 mov dx, #0x3C4 out dx, ax inc dx in al, dx cmp al, #0x12 pop dx pop ax ret cirrus_int10_handler: pushf push bp cmp ah, #0x00 ;; set video mode jz cirrus_set_video_mode cmp ah, #0x12 ;; cirrus extension jz cirrus_extbios cmp ah, #0x4F ;; VESA extension jz cirrus_vesa cirrus_unhandled: pop bp popf jmp vgabios_int10_handler cirrus_return: #ifdef CIRRUS_DEBUG call cirrus_debug_dump #endif pop bp popf iret cirrus_set_video_mode: #ifdef CIRRUS_DEBUG call cirrus_debug_dump #endif push si push ax push bx push ds #ifdef CIRRUS_VESA3_PMINFO db 0x2e ;; cs: mov si, [cirrus_vesa_sel0000_data] #else xor si, si #endif mov ds, si xor bx, bx mov [PM_BIOSMEM_VBE_MODE], bx pop ds pop bx call cirrus_get_modeentry jnc cirrus_set_video_mode_extended mov al, #0xfe call cirrus_get_modeentry_nomask call cirrus_switch_mode pop ax pop si jmp cirrus_unhandled cirrus_extbios: #ifdef CIRRUS_DEBUG call cirrus_debug_dump #endif cmp bl, #0x80 jb cirrus_unhandled cmp bl, #0xAF ja cirrus_unhandled push bx and bx, #0x7F shl bx, 1 db 0x2e ;; cs: mov bp, cirrus_extbios_handlers[bx] pop bx push #cirrus_return push bp ret cirrus_vesa: #ifdef CIRRUS_DEBUG call cirrus_debug_dump #endif cmp al, #0x10 ja cirrus_vesa_not_handled push bx xor bx, bx mov bl, al shl bx, 1 db 0x2e ;; cs: mov bp, cirrus_vesa_handlers[bx] pop bx push #cirrus_return push bp ret cirrus_vesa_not_handled: mov ax, #0x014F ;; not implemented jmp cirrus_return #ifdef CIRRUS_DEBUG cirrus_debug_dump: push es push ds pusha push cs pop ds call _cirrus_debugmsg popa pop ds pop es ret #endif cirrus_set_video_mode_extended: call cirrus_switch_mode pop ax ;; mode test al, #0x80 jnz cirrus_set_video_mode_extended_1 push ax mov ax, #0xffff ; set to 0xff to keep win 2K happy call cirrus_clear_vram pop ax cirrus_set_video_mode_extended_1: and al, #0x7f push ds #ifdef CIRRUS_VESA3_PMINFO db 0x2e ;; cs: mov si, [cirrus_vesa_sel0000_data] #else xor si, si #endif mov ds, si mov [PM_BIOSMEM_CURRENT_MODE], al pop ds mov al, #0x20 pop si jmp cirrus_return cirrus_vesa_pmbios_init: retf cirrus_vesa_pmbios_entry: pushf push bp cmp ah, #0x4F jnz cirrus_vesa_pmbios_unimplemented cmp al, #0x0F ja cirrus_vesa_pmbios_unimplemented push bx xor bx, bx mov bl, al shl bx, 1 db 0x2e ;; cs: mov bp, cirrus_vesa_handlers[bx] pop bx push #cirrus_vesa_pmbios_return push bp ret cirrus_vesa_pmbios_unimplemented: mov ax, #0x014F cirrus_vesa_pmbios_return: pop bp popf retf ; in si:mode table cirrus_switch_mode: push ds push bx push dx push cs pop ds mov bx, [si+10] ;; seq mov dx, #0x3c4 mov ax, #0x1206 out dx, ax ;; Unlock cirrus special call cirrus_switch_mode_setregs mov bx, [si+12] ;; graph mov dx, #0x3ce call cirrus_switch_mode_setregs mov bx, [si+14] ;; crtc call cirrus_get_crtc call cirrus_switch_mode_setregs mov dx, #0x3c6 mov al, #0x00 out dx, al in al, dx in al, dx in al, dx in al, dx mov al, [si+8] ;; hidden dac out dx, al mov al, #0xff out dx, al mov al, #0x00 mov bl, [si+17] ;; memory model or bl, bl jz is_text_mode mov al, #0x01 cmp bl, #0x03 jnz is_text_mode or al, #0x40 is_text_mode: mov bl, #0x10 call biosfn_get_single_palette_reg and bh, #0xfe or bh, al call biosfn_set_single_palette_reg pop dx pop bx pop ds ret cirrus_enable_16k_granularity: push ax push dx mov dx, #0x3ce mov al, #0x0b out dx, al inc dx in al, dx or al, #0x20 ;; enable 16k out dx, al pop dx pop ax ret cirrus_switch_mode_setregs: csms_1: mov ax, [bx] cmp ax, #0xffff jz csms_2 out dx, ax add bx, #0x2 jmp csms_1 csms_2: ret cirrus_extbios_80h: push dx call cirrus_get_crtc mov al, #0x27 out dx, al inc dx in al, dx mov bx, #_cirrus_id_table c80h_1: db 0x2e ;; cs: mov ah, [bx] cmp ah, al jz c80h_2 cmp ah, #0xff jz c80h_2 inc bx inc bx jmp c80h_1 c80h_2: db 0x2e ;; cs: mov al, 0x1[bx] pop dx mov ah, #0x00 xor bx, bx ret cirrus_extbios_81h: mov ax, #0x103 ;; XXX ret cirrus_extbios_82h: push dx call cirrus_get_crtc xor ax, ax mov al, #0x27 out dx, al inc dx in al, dx and al, #0x03 mov ah, #0xAF pop dx ret cirrus_extbios_85h: push cx push dx mov dx, #0x3C4 mov al, #0x0f ;; get DRAM band width out dx, al inc dx in al, dx ;; al = 4 << bandwidth mov cl, al shr cl, #0x03 and cl, #0x03 cmp cl, #0x03 je c85h2 mov al, #0x04 shl al, cl jmp c85h3 c85h2: ;; 4MB or 2MB and al, #0x80 mov al, #0x20 ;; 2 MB je c85h3 mov al, #0x40 ;; 4 MB c85h3: pop dx pop cx ret cirrus_extbios_9Ah: mov ax, #0x4060 mov cx, #0x1132 ret cirrus_extbios_A0h: call cirrus_get_modeentry mov ah, #0x01 sbb ah, #0x00 mov bx, cirrus_extbios_A0h_callback mov si, #0xffff mov di, bx mov ds, bx mov es, bx ret cirrus_extbios_A0h_callback: ;; fatal: not implemented yet cli hlt retf cirrus_extbios_A1h: mov bx, #0x0E00 ;; IBM 8512/8513, color ret cirrus_extbios_A2h: mov al, #0x07 ;; HSync 31.5 - 64.0 kHz ret cirrus_extbios_AEh: mov al, #0x01 ;; High Refresh 75Hz ret cirrus_extbios_unimplemented: ret cirrus_vesa_00h: push ds push si mov bp, di push es pop ds cld mov ax, [di] cmp ax, #0x4256 ;; VB jnz cv00_1 mov ax, [di+2] cmp ax, #0x3245 ;; E2 jnz cv00_1 ;; VBE2 lea di, 0x14[bp] mov ax, #0x0100 ;; soft ver. stosw mov ax, # cirrus_vesa_vendorname stosw mov ax, cs stosw mov ax, # cirrus_vesa_productname stosw mov ax, cs stosw mov ax, # cirrus_vesa_productrevision stosw mov ax, cs stosw cv00_1: mov di, bp mov ax, #0x4556 ;; VE stosw mov ax, #0x4153 ;; SA stosw mov ax, #0x0200 ;; v2.00 stosw mov ax, # cirrus_vesa_oemname stosw mov ax, cs stosw xor ax, ax ;; caps stosw stosw lea ax, 0x40[bp] stosw mov ax, es stosw call cirrus_extbios_85h ;; vram in 64k mov ah, #0x00 stosw push cs pop ds lea di, 0x40[bp] mov si, #_cirrus_vesa_modelist cv00_2: lodsw stosw add si, #2 cmp ax, #0xffff jnz cv00_2 mov ax, #0x004F mov di, bp pop si pop ds ret cirrus_vesa_01h: mov ax, cx and ax, #0x3fff call cirrus_vesamode_to_mode cmp ax, #0xffff jnz cirrus_vesa_01h_1 jmp cirrus_vesa_unimplemented cirrus_vesa_01h_1: push ds push si push cx push dx push bx mov bp, di cld push cs pop ds call cirrus_get_modeentry_nomask push di xor ax, ax mov cx, #0x80 rep stosw ;; clear buffer pop di mov ax, #0x003b ;; mode stosw mov ax, #0x0007 ;; attr stosw mov ax, #0x0010 ;; granularity =16K stosw mov ax, #0x0040 ;; size =64K stosw mov ax, #0xA000 ;; segment A stosw xor ax, ax ;; no segment B stosw mov ax, #cirrus_vesa_05h_farentry stosw mov ax, cs stosw call cirrus_get_line_offset_entry stosw ;; bytes per scan line mov ax, [si+2] ;; width stosw mov ax, [si+4] ;; height stosw mov ax, #0x08 stosb mov ax, #0x10 stosb mov al, #1 ;; count of planes stosb mov al, [si+6] ;; bpp stosb mov al, #0x1 ;; XXX number of banks stosb mov al, [si+17] stosb ;; memory model mov al, #0x0 ;; XXX size of bank in K stosb call cirrus_get_line_offset_entry mov bx, [si+4] mul bx ;; dx:ax=vramdisp or ax, ax jz cirrus_vesa_01h_3 inc dx cirrus_vesa_01h_3: call cirrus_extbios_85h ;; al=vram in 64k mov ah, #0x00 mov cx, dx xor dx, dx div cx dec ax stosb ;; number of image pages = vramtotal/vramdisp-1 mov al, #0x00 stosb ;; v1.2+ stuffs push si add si, #18 movsw movsw movsw movsw pop si mov ah, [si+16] mov al, #0x0 sub ah, #9 rcl al, #1 ; bit 0=palette flag stosb ;; direct screen mode info ;; v2.0+ stuffs ;; 32-bit LFB address xor ax, ax stosw call cirrus_get_lfb_addr stosw or ax, ax jz cirrus_vesa_01h_4 push di mov di, bp db 0x26 ;; es: mov ax, [di] or ax, #0x0080 ;; mode bit 7:LFB stosw pop di cirrus_vesa_01h_4: xor ax, ax stosw ; reserved stosw ; reserved stosw ; reserved mov ax, #0x004F mov di, bp pop bx pop dx pop cx pop si pop ds test cx, #0x4000 ;; LFB flag jz cirrus_vesa_01h_5 push cx db 0x26 ;; es: mov cx, [di] cmp cx, #0x0080 ;; is LFB supported? jnz cirrus_vesa_01h_6 mov ax, #0x014F ;; error - no LFB cirrus_vesa_01h_6: pop cx cirrus_vesa_01h_5: ret cirrus_vesa_02h: ;; XXX support CRTC registers test bx, #0x3e00 jnz cirrus_vesa_02h_2 ;; unknown flags mov ax, bx and ax, #0x1ff ;; bit 8-0 mode cmp ax, #0x100 ;; legacy VGA mode jb cirrus_vesa_02h_legacy call cirrus_vesamode_to_mode cmp ax, #0xffff jnz cirrus_vesa_02h_1 cirrus_vesa_02h_2: jmp cirrus_vesa_unimplemented cirrus_vesa_02h_legacy: #ifdef CIRRUS_VESA3_PMINFO db 0x2e ;; cs: cmp byte ptr [cirrus_vesa_is_protected_mode], #0 jnz cirrus_vesa_02h_2 #endif // CIRRUS_VESA3_PMINFO int #0x10 mov ax, #0x004F ret cirrus_vesa_02h_1: push si push ax call cirrus_get_modeentry_nomask call cirrus_switch_mode test bx, #0x4000 ;; LFB jnz cirrus_vesa_02h_3 call cirrus_enable_16k_granularity cirrus_vesa_02h_3: test bx, #0x8000 ;; no clear jnz cirrus_vesa_02h_4 push ax xor ax,ax call cirrus_clear_vram pop ax cirrus_vesa_02h_4: pop ax push ds #ifdef CIRRUS_VESA3_PMINFO db 0x2e ;; cs: mov si, [cirrus_vesa_sel0000_data] #else xor si, si #endif mov ds, si mov [PM_BIOSMEM_CURRENT_MODE], al mov [PM_BIOSMEM_VBE_MODE], bx pop ds pop si mov ax, #0x004F ret cirrus_vesa_03h: push ds #ifdef CIRRUS_VESA3_PMINFO db 0x2e ;; cs: mov ax, [cirrus_vesa_sel0000_data] #else xor ax, ax #endif mov ds, ax mov bx, # PM_BIOSMEM_VBE_MODE mov ax, [bx] mov bx, ax test bx, bx jnz cirrus_vesa_03h_1 mov bx, # PM_BIOSMEM_CURRENT_MODE mov al, [bx] mov bl, al xor bh, bh cirrus_vesa_03h_1: mov ax, #0x004f pop ds ret cirrus_vesa_05h_farentry: call cirrus_vesa_05h retf cirrus_vesa_05h: cmp bl, #0x01 ja cirrus_vesa_05h_1 cmp bh, #0x00 jz cirrus_vesa_05h_setmempage cmp bh, #0x01 jz cirrus_vesa_05h_getmempage cirrus_vesa_05h_1: jmp cirrus_vesa_unimplemented cirrus_vesa_05h_setmempage: or dh, dh ; address must be < 0x100 jnz cirrus_vesa_05h_1 push dx mov al, bl ;; bl=bank number add al, #0x09 mov ah, dl ;; dx=window address in granularity mov dx, #0x3ce out dx, ax pop dx mov ax, #0x004F ret cirrus_vesa_05h_getmempage: mov al, bl ;; bl=bank number add al, #0x09 mov dx, #0x3ce out dx, al inc dx in al, dx xor dx, dx mov dl, al ;; dx=window address in granularity mov ax, #0x004F ret cirrus_vesa_06h: mov ax, cx cmp bl, #0x01 je cirrus_vesa_06h_3 cmp bl, #0x02 je cirrus_vesa_06h_2 jb cirrus_vesa_06h_1 mov ax, #0x0100 ret cirrus_vesa_06h_1: call cirrus_get_bpp_bytes mov bl, al xor bh, bh mov ax, cx mul bx cirrus_vesa_06h_2: call cirrus_set_line_offset cirrus_vesa_06h_3: call cirrus_get_bpp_bytes mov bl, al xor bh, bh xor dx, dx call cirrus_get_line_offset push ax div bx mov cx, ax pop bx call cirrus_extbios_85h ;; al=vram in 64k xor dx, dx mov dl, al xor ax, ax div bx mov dx, ax mov ax, #0x004f ret cirrus_vesa_07h: cmp bl, #0x80 je cirrus_vesa_07h_1 cmp bl, #0x01 je cirrus_vesa_07h_2 jb cirrus_vesa_07h_1 mov ax, #0x0100 ret cirrus_vesa_07h_1: push dx call cirrus_get_bpp_bytes mov bl, al xor bh, bh mov ax, cx mul bx pop bx push ax call cirrus_get_line_offset mul bx pop bx add ax, bx jnc cirrus_vesa_07h_3 inc dx cirrus_vesa_07h_3: push dx and dx, #0x0003 mov bx, #0x04 div bx pop dx shr dx, #2 call cirrus_set_start_addr mov ax, #0x004f ret cirrus_vesa_07h_2: call cirrus_get_start_addr shl dx, #2 push dx mov bx, #0x04 mul bx pop bx or dx, bx push ax call cirrus_get_line_offset mov bx, ax pop ax div bx push ax push dx call cirrus_get_bpp_bytes mov bl, al xor bh, bh pop ax xor dx, dx div bx mov cx, ax pop dx mov ax, #0x004f ret cirrus_vesa_10h: ;; Power management functions ;; Set up DS to read stored power info from RAM push ds #ifdef CIRRUS_VESA3_PMINFO db 0x2e ;; cs: mov ax, [cirrus_vesa_sel0000_data] #else xor ax, ax #endif mov ds, ax ;; Now choose the right function cmp bl, #0x00 ja cirrus_vesa_10h_01 ;; ;; Function 00h: Get capabilities ;; mov bx, #0x0720 ;; 07: standby/suspend/off, 20: VBE/PM 2.0 mov ax, #0x004f jmp cirrus_vesa_10h_done cirrus_vesa_10h_01: cmp bl, #0x01 ja cirrus_vesa_10h_02 ;; ;; Function 01h: Set power state ;; mov ax, bx mov bx, # PM_BIOSMEM_VBE_POWER mov [bx], ah mov ax, #0x004f jmp cirrus_vesa_10h_done cirrus_vesa_10h_02: cmp bl, #0x02 ja cirrus_vesa_10h_unimplemented ;; ;; Function 02h: Get power state ;; mov bx, # PM_BIOSMEM_VBE_POWER mov bh, [bx] mov ax, #0x004f jmp cirrus_vesa_10h_done cirrus_vesa_10h_unimplemented: mov ax, #0x014F ;; not implemented cirrus_vesa_10h_done: pop ds ret cirrus_vesa_unimplemented: mov ax, #0x014F ;; not implemented ret ;; in ax:vesamode, out ax:cirrusmode cirrus_vesamode_to_mode: push ds push cx push si push cs pop ds mov cx, #0xffff mov si, #_cirrus_vesa_modelist cvtm_1: cmp [si],ax jz cvtm_2 cmp [si],cx jz cvtm_2 add si, #4 jmp cvtm_1 cvtm_2: mov ax,[si+2] pop si pop cx pop ds ret ; cirrus_get_crtc ;; NOTE - may be called in protected mode cirrus_get_crtc: push ds push ax mov dx, #0x3cc in al, dx and al, #0x01 shl al, #5 mov dx, #0x3b4 add dl, al pop ax pop ds ret ;; in - al:mode, out - cflag:result, si:table, ax:destroyed cirrus_get_modeentry: and al, #0x7f cirrus_get_modeentry_nomask: mov si, #_cirrus_modes cgm_1: db 0x2e ;; cs: mov ah, [si] cmp al, ah jz cgm_2 cmp ah, #0xff jz cgm_4 add si, # CIRRUS_MODE_SIZE jmp cgm_1 cgm_4: xor si, si stc ;; video mode is not supported jmp cgm_3 cgm_2: clc ;; video mode is supported cgm_3: ret ; get LFB address ; out - ax:LFB address (high 16 bit) ;; NOTE - may be called in protected mode cirrus_get_lfb_addr: push cx push dx push eax xor cx, cx mov dl, #0x00 call cirrus_pci_read cmp ax, #0xffff jz cirrus_get_lfb_addr_5 cirrus_get_lfb_addr_3: mov dl, #0x00 call cirrus_pci_read cmp ax, #0x1013 ;; cirrus jz cirrus_get_lfb_addr_4 add cx, #0x8 cmp cx, #0x200 ;; search bus #0 and #1 jb cirrus_get_lfb_addr_3 cirrus_get_lfb_addr_5: xor dx, dx ;; no LFB jmp cirrus_get_lfb_addr_6 cirrus_get_lfb_addr_4: mov dl, #0x10 ;; I/O space #0 call cirrus_pci_read test ax, #0xfff1 jnz cirrus_get_lfb_addr_5 shr eax, #16 mov dx, ax ;; LFB address cirrus_get_lfb_addr_6: pop eax mov ax, dx pop dx pop cx ret cirrus_pci_read: mov eax, #0x00800000 mov ax, cx shl eax, #8 mov al, dl mov dx, #0xcf8 out dx, eax add dl, #4 in eax, dx ret ;; out - al:bytes per pixel cirrus_get_bpp_bytes: push dx mov dx, #0x03c4 mov al, #0x07 out dx, al inc dx in al, dx and al, #0x0e cmp al, #0x06 jne cirrus_get_bpp_bytes_1 and al, #0x02 cirrus_get_bpp_bytes_1: shr al, #1 cmp al, #0x04 je cirrus_get_bpp_bytes_2 inc al cirrus_get_bpp_bytes_2: pop dx ret ;; in - ax: new line offset cirrus_set_line_offset: shr ax, #3 push ax call cirrus_get_crtc mov al, #0x13 out dx, al inc dx pop ax out dx, al dec dx mov al, #0x1b out dx, al inc dx shl ah, #4 in al, dx and al, #ef or al, ah out dx, al ret ;; out - ax: active line offset cirrus_get_line_offset: push dx push bx call cirrus_get_crtc mov al, #0x13 out dx, al inc dx in al, dx mov bl, al dec dx mov al, #0x1b out dx, al inc dx in al, dx mov ah, al shr ah, #4 and ah, #0x01 mov al, bl shl ax, #3 pop bx pop dx ret ;; in - si: table ;; out - ax: line offset for mode cirrus_get_line_offset_entry: push bx mov bx, [si+14] ;; crtc table push bx offset_loop1: mov ax, [bx] cmp al, #0x13 je offset_found1 inc bx inc bx jnz offset_loop1 offset_found1: xor al, al shr ax, #5 pop bx push ax offset_loop2: mov ax, [bx] cmp al, #0x1b je offset_found2 inc bx inc bx jnz offset_loop2 offset_found2: pop bx and ax, #0x1000 shr ax, #1 or ax, bx pop bx ret ;; in - new address in DX:AX cirrus_set_start_addr: push bx push dx push ax call cirrus_get_crtc mov al, #0x0d out dx, al inc dx pop ax out dx, al dec dx mov al, #0x0c out dx, al inc dx mov al, ah out dx, al dec dx mov al, #0x1d out dx, al inc dx in al, dx and al, #0x7f pop bx mov ah, bl shl bl, #4 and bl, #0x80 or al, bl out dx, al dec dx mov bl, ah and ah, #0x01 shl bl, #1 and bl, #0x0c or ah, bl mov al, #0x1b out dx, al inc dx in al, dx and al, #0xf2 or al, ah out dx, al pop bx ret ;; out - current address in DX:AX cirrus_get_start_addr: push bx call cirrus_get_crtc mov al, #0x0c out dx, al inc dx in al, dx mov ah, al dec dx mov al, #0x0d out dx, al inc dx in al, dx push ax dec dx mov al, #0x1b out dx, al inc dx in al, dx dec dx mov bl, al and al, #0x01 and bl, #0x0c shr bl, #1 or bl, al mov al, #0x1d out dx, al inc dx in al, dx and al, #0x80 shr al, #4 or bl, al mov dl, bl xor dh, dh pop ax pop bx ret cirrus_clear_vram: pusha push es mov si, ax call cirrus_enable_16k_granularity call cirrus_extbios_85h shl al, #2 mov bl, al xor ah,ah cirrus_clear_vram_1: mov al, #0x09 mov dx, #0x3ce out dx, ax push ax ;; Windows Vista appears to be emulating this sequence as part of changing ;; screen resolution, but it generates 4096 writes per iteration. ;; Instead, use a magic register sequence to write the whole bank. ;;mov cx, #0xa000 ;;mov es, cx ;;xor di, di ;;mov ax, si ;;mov cx, #8192 ;;cld ;;rep ;; stosw mov ax, si shl ax, #8 mov al, #0xfe out dx, ax ;; Low byte of value to be written to the bank mov ax, si mov al, #0xff out dx, ax ;; High byte and trigger the write pop ax inc ah cmp ah, bl jne cirrus_clear_vram_1 xor ah,ah mov dx, #0x3ce out dx, ax pop es popa ret cirrus_extbios_handlers: ;; 80h dw cirrus_extbios_80h dw cirrus_extbios_81h dw cirrus_extbios_82h dw cirrus_extbios_unimplemented ;; 84h dw cirrus_extbios_unimplemented dw cirrus_extbios_85h dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented ;; 88h dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented ;; 8Ch dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented ;; 90h dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented ;; 94h dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented ;; 98h dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_9Ah dw cirrus_extbios_unimplemented ;; 9Ch dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented ;; A0h dw cirrus_extbios_A0h dw cirrus_extbios_A1h dw cirrus_extbios_A2h dw cirrus_extbios_unimplemented ;; A4h dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented ;; A8h dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented ;; ACh dw cirrus_extbios_unimplemented dw cirrus_extbios_unimplemented dw cirrus_extbios_AEh dw cirrus_extbios_unimplemented cirrus_vesa_handlers: ;; 00h dw cirrus_vesa_00h dw cirrus_vesa_01h dw cirrus_vesa_02h dw cirrus_vesa_03h ;; 04h dw cirrus_vesa_unimplemented dw cirrus_vesa_05h dw cirrus_vesa_06h dw cirrus_vesa_07h ;; 08h dw cirrus_vesa_unimplemented dw cirrus_vesa_unimplemented dw cirrus_vesa_unimplemented dw cirrus_vesa_unimplemented ;; 0Ch dw cirrus_vesa_unimplemented dw cirrus_vesa_unimplemented dw cirrus_vesa_unimplemented dw cirrus_vesa_unimplemented ;; 10h dw cirrus_vesa_10h ASM_END #ifdef CIRRUS_VESA3_PMINFO ASM_START cirrus_vesa_pminfo: /* + 0 */ .byte 0x50,0x4d,0x49,0x44 ;; signature[4] /* + 4 */ dw cirrus_vesa_pmbios_entry ;; entry_bios dw cirrus_vesa_pmbios_init ;; entry_init /* + 8 */ cirrus_vesa_sel0000_data: dw 0x0000 ;; sel_00000 cirrus_vesa_selA000_data: dw 0xA000 ;; sel_A0000 /* +12 */ cirrus_vesa_selB000_data: dw 0xB000 ;; sel_B0000 cirrus_vesa_selB800_data: dw 0xB800 ;; sel_B8000 /* +16 */ cirrus_vesa_selC000_data: dw 0xC000 ;; sel_C0000 cirrus_vesa_is_protected_mode: ;; protected mode flag and checksum dw (~((0xf2 + (cirrus_vesa_pmbios_entry >> 8) + (cirrus_vesa_pmbios_entry) \ + (cirrus_vesa_pmbios_init >> 8) + (cirrus_vesa_pmbios_init)) & 0xff) << 8) + 0x01 ASM_END #endif // CIRRUS_VESA3_PMINFO #ifdef CIRRUS_DEBUG static void cirrus_debugmsg(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS) Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS; { if((GET_AH()!=0x0E)&&(GET_AH()!=0x02)&&(GET_AH()!=0x09)&&(AX!=0x4F05)) printf("vgabios call ah%02x al%02x bx%04x cx%04x dx%04x\n",GET_AH(),GET_AL(),BX,CX,DX); } #endif xen-4.9.2/tools/firmware/vgabios/TODO0000664000175000017500000000117313256712137015555 0ustar smbsmbShort term : ------------ General - Fix init mode (ah=00). Should use more BIOS variables - Add new functionalities and modify static functionality table - Performance : 16 bits IO v0.7 - Implement the remaining functions (don't know if all are needed): - chargen ax=1120, ax=1121, ax=1122, ax=1123, ax=1124 - display switch interface ah=12 bl=35 - video refresh control ah=12 bl=36 - Graphic modes v1.0 - Bugfixes ================================================================================================= VBE: ---- Long term: - have plex86 host side display interface - have text io functions in vbe mode xen-4.9.2/tools/firmware/vgabios/Makefile0000664000175000017500000000730713256712137016532 0ustar smbsmbCC = gcc GCC = gcc BCC = bcc AS86 = as86 RELEASE = `pwd | sed "s-.*/--"` VGABIOS_REL_DATE ?= `date '+%d %b %Y'` RELVERS = `pwd | sed "s-.*/--" | sed "s/vgabios//" | sed "s/-//"` VGABIOS_DATE = "-DVGABIOS_DATE=\"$(VGABIOS_REL_DATE)\"" .PHONY: all all: bios cirrus-bios .PHONY: bios bios: biossums vgabios.bin vgabios.debug.bin .PHONY: cirrus-bios cirrus-bios: vgabios-cirrus.bin vgabios-cirrus.debug.bin .PHONY: clean clean: rm -f biossums vbetables-gen vbetables.h *.o *.s *.ld86 \ temp.awk.* vgabios*.orig _vgabios_* _vgabios-debug_* core vgabios*.bin vgabios*.txt $(RELEASE).bin *.bak rm -f VGABIOS-lgpl-latest*.bin .PHONY: distclean distclean: clean .PHONY: release release: VGABIOS_VERS=\"-DVGABIOS_VERS=\\\"$(RELVERS)\\\"\" make bios cirrus-bios /bin/rm -f *.o *.s *.ld86 \ temp.awk.* vgabios.*.orig _vgabios_.*.c core *.bak .#* cp VGABIOS-lgpl-latest.bin ../$(RELEASE).bin cp VGABIOS-lgpl-latest.debug.bin ../$(RELEASE).debug.bin cp VGABIOS-lgpl-latest.cirrus.bin ../$(RELEASE).cirrus.bin cp VGABIOS-lgpl-latest.cirrus.debug.bin ../$(RELEASE).cirrus.debug.bin tar czvf ../$(RELEASE).tgz --exclude CVS -C .. $(RELEASE)/ vgabios.bin: biossums vgabios.c vgabios.h vgafonts.h vgatables.h vbe.h vbe.c vbetables.h $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DVBE $(VGABIOS_DATE) > _vgabios_.c $(BCC) -o vgabios.s -C-c -D__i86__ -S -0 _vgabios_.c sed -e 's/^\.text//' -e 's/^\.data//' vgabios.s > _vgabios_.s $(AS86) _vgabios_.s -b vgabios.bin -u -w- -g -0 -j -O -l vgabios.txt rm -f _vgabios_.s _vgabios_.c vgabios.s cp vgabios.bin VGABIOS-lgpl-latest.bin ./biossums VGABIOS-lgpl-latest.bin ls -l VGABIOS-lgpl-latest.bin vgabios.debug.bin: biossums vgabios.c vgabios.h vgafonts.h vgatables.h vbe.h vbe.c vbetables.h $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DVBE -DDEBUG $(VGABIOS_DATE) > _vgabios-debug_.c $(BCC) -o vgabios-debug.s -C-c -D__i86__ -S -0 _vgabios-debug_.c sed -e 's/^\.text//' -e 's/^\.data//' vgabios-debug.s > _vgabios-debug_.s $(AS86) _vgabios-debug_.s -b vgabios.debug.bin -u -w- -g -0 -j -O -l vgabios.debug.txt rm -f _vgabios-debug_.s _vgabios-debug_.c vgabios-debug.s cp vgabios.debug.bin VGABIOS-lgpl-latest.debug.bin ./biossums VGABIOS-lgpl-latest.debug.bin ls -l VGABIOS-lgpl-latest.debug.bin vgabios-cirrus.bin: biossums vgabios.c vgabios.h vgafonts.h vgatables.h clext.c $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DCIRRUS -DPCIBIOS $(VGABIOS_DATE) > _vgabios-cirrus_.c $(BCC) -o vgabios-cirrus.s -C-c -D__i86__ -S -0 _vgabios-cirrus_.c sed -e 's/^\.text//' -e 's/^\.data//' vgabios-cirrus.s > _vgabios-cirrus_.s $(AS86) _vgabios-cirrus_.s -b vgabios-cirrus.bin -u -w- -g -0 -j -O -l vgabios-cirrus.txt rm -f _vgabios-cirrus_.s _vgabios-cirrus_.c vgabios-cirrus.s cp vgabios-cirrus.bin VGABIOS-lgpl-latest.cirrus.bin ./biossums VGABIOS-lgpl-latest.cirrus.bin ls -l VGABIOS-lgpl-latest.cirrus.bin vgabios-cirrus.debug.bin: biossums vgabios.c vgabios.h vgafonts.h vgatables.h clext.c $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DCIRRUS -DCIRRUS_DEBUG -DPCIBIOS $(VGABIOS_DATE) > _vgabios-cirrus-debug_.c $(BCC) -o vgabios-cirrus-debug.s -C-c -D__i86__ -S -0 _vgabios-cirrus-debug_.c sed -e 's/^\.text//' -e 's/^\.data//' vgabios-cirrus-debug.s > _vgabios-cirrus-debug_.s $(AS86) _vgabios-cirrus-debug_.s -b vgabios-cirrus.debug.bin -u -w- -g -0 -j -O -l vgabios-cirrus.debug.txt rm -f _vgabios-cirrus-debug_.s _vgabios-cirrus-debug_.c vgabios-cirrus-debug.s cp vgabios-cirrus.debug.bin VGABIOS-lgpl-latest.cirrus.debug.bin ./biossums VGABIOS-lgpl-latest.cirrus.debug.bin ls -l VGABIOS-lgpl-latest.cirrus.debug.bin biossums: biossums.c $(CC) -o biossums biossums.c vbetables-gen: vbetables-gen.c $(CC) -o vbetables-gen vbetables-gen.c vbetables.h: vbetables-gen ./vbetables-gen > $@ xen-4.9.2/tools/firmware/vgabios/ChangeLog0000664000175000017500000010450213256712137016637 0ustar smbsmb2008-05-11 08:40 vruppert * biossums.c (1.6): - fixed a warning 2008-03-02 08:47 vruppert * vbe.c (1.60): - added debug message for unsupported VBE modes 2008-02-24 09:18 vruppert * vbe.c (1.59): - in LFB modes the number of banks must be set to 1 2008-01-27 10:44 vruppert * Makefile (1.21), biossums.c (1.5), vgabios.c (1.67): - added PCI data structure for the Cirrus VGABIOS images - added support for the PCI data structure in biossums - updated year in copyright 2008-01-26 11:46 vruppert * BUGS (1.4), Makefile (1.20), README (1.14), TODO (1.13), vbe_display_api.txt (1.14): - whitespace cleanup 2006-11-26 10:43 vruppert * Makefile (1.19): - disable the generation of linemarkers by the preprocessor, since the latest versions of bcc don't like them 2006-09-02 13:15 vruppert * biossums.c (1.4): - the biossums utility no longer modifies VGABIOS images with proper checksum and size 2006-08-19 14:28 vruppert * Changelog (1.26), README (1.13), TODO (1.12): - updates for 0.6a release 2006-08-19 09:39 vruppert * vbe.c (1.58): - improved VGA compatible setup for VBE modes (disable CGA and Hercules compatible memory layout) 2006-08-18 20:39 vruppert * vbe.c (1.57): - improved VGA compatible setup for >=8bpp VBE modes (CRTC doubleword mode and GRDC shift register setting added) - now using symbolic name for CRTC address register 2006-08-15 20:42 vruppert * vbe.c (1.56), vbetables-gen.c (1.4): - init 4bpp VBE modes by a temporary switch to VGA mode 0x6A - all 4bpp VBE modes now enabled 2006-08-14 20:24 vruppert * vbe.c (1.55): - VGA compatible setup for VBE modes improved (Bochs hack can be removed now) 2006-08-12 07:51 vruppert * .cvsignore (1.1): - .cvsignore added for auto-generated file 2006-08-12 07:47 vruppert * vbe.c (1.54), vbe.h (1.27), vbe_display_api.txt (1.13), vbetables-gen.c (1.3): - cleaned up VBE memory size definitions (removed duplicate defines, main definition now in vbetables-gen.c) 2006-08-09 21:28 vruppert * vbetables.h (1.30): - removed auto-generated file 2006-08-09 21:26 vruppert * vbe.c (1.53), vbe.h (1.26), vbe_display_api.txt (1.12), vbetables-gen.c (1.2), vbetables.h (1.29): - VBE video memory increased to 8 MB - VBE dispi ID changed to B0C4 - documentation update 2006-07-11 08:03 vruppert * Makefile (1.18), vbetables-gen.c (1.1), vbetables.h (1.28): - generate vbetables.h dynamicly * initial patch from the qemu project by Fabrice Bellard * only add modes that fit in video memory (still 4 MB) * several other fixes (e.g. 4 bpp specific stuff, number of pages) 2006-07-10 07:47 vruppert * vgabios.c (1.66): - biosfn_scroll(): check variable 'i' for underflowing when scrolling downwards to avoid screen corruption 2006-07-10 07:47 vruppert * vbe.c (1.52): - VBE set bank functions failure handling added - VBE get/set logical scan line length fixes for the 4bpp mode 2006-07-08 13:27 vruppert * vbe.c (1.51), vbetables.h (1.27): - added special case for the 4 bpp when setting VBE display start - VBE mode table fixes 2006-07-07 13:30 vruppert * clext.c (1.12): - bank pointer must be set to 0 after a mode set 2006-06-21 16:58 vruppert * vbe.c (1.50), vbetables.h (1.26): - improved VBE display capabilities check (X resulution checked now) - removed obsolete defines (LFB always available, always generate dynamic list) - CR/LF to LF fixes 2006-06-18 15:22 vruppert * clext.c (1.11), vbe.c (1.49), vbe.h (1.25), vbetables.h (1.25), vgabios.c (1.65): - applied patch from the qemu project (Fabrice Bellard) * Cirrus SVGA now supports the "no clear" bit when switching to Cirrus or VESA mode * Bochs VBE protected mode interface improved * save/restore video state support for Bochs VBE and standard VGA added * Bochs VBE prepared for more modi 2006-03-25 10:19 vruppert * clext.c (1.10), vgabios.c (1.64), vgatables.h (1.10): - applied patch from Fabrice Bellard * added minimal support for the video parameter table (VPT) * added Cirrus SVGA mode 0x7b (1600x1200x8) 2005-12-26 19:50 vruppert * vbe.c (1.48), vgabios.c (1.63): - Bochs VBE protected mode interface added (based on a patch by malc@pulsesoft.com) 2005-12-26 19:50 vruppert * biossums.c (1.3): - biossums utility now supports VGABIOS sizes up to 64 kBytes 2005-09-21 18:45 vruppert * vgatables.h (1.9): - mode 0x11: all color planes must be enabled in this 2-color VGA mode 2005-08-30 18:41 vruppert * biossums.c (1.2): - missing license text added in biossums.c 2005-07-02 18:39 vruppert * vgabios.c (1.62): - BIOS configuration word usually reports initial mode 80x25 color text - vgabios function 0x0e (write teletype): linefeed (0x0a) only increments the cursor row value 2005-05-24 16:50 vruppert * vbe.c (1.47), vgabios.c (1.61): - output to the vgabios info port can be disabled now. It is still enabled by default and always possible in debug mode. (based on a patch from Alex Beregszaszi) 2005-05-20 16:06 vruppert * vbe.c (1.46), vgabios.c (1.60): - fixed return value for the default case in the VBE section (non-debug mode) - removed unused macros HALT and PANIC_PORT 2005-03-07 20:39 vruppert * README (1.9): - updates for 0.5a release 2005-03-06 13:06 vruppert * Makefile (1.17): - vgabios files with cirrus support added to release target 2005-03-06 12:24 vruppert * Makefile (1.16): - cross compilation support added (patch from Alex Beregszaszi) 2005-03-05 13:03 vruppert * BUGS (1.3), README (1.8), TODO (1.11): - documentation updates 2004-12-04 15:26 vruppert * VGABIOS-lgpl-latest.bin (1.61), VGABIOS-lgpl-latest.cirrus.bin (1.13), VGABIOS-lgpl-latest.cirrus.debug.bin (1.13), VGABIOS-lgpl-latest.debug.bin (1.61), clext.c (1.9): - Cirrus extension: support for 1280x1024x15 and 1280x1024x16 modes added (patch from Fabrice Bellard) 2004-08-08 16:53 vruppert * VGABIOS-lgpl-latest.bin (1.60), VGABIOS-lgpl-latest.cirrus.bin (1.12), VGABIOS-lgpl-latest.cirrus.debug.bin (1.12), VGABIOS-lgpl-latest.debug.bin (1.60), clext.c (1.8): - use single bank mode for VBE - enable 16k granularity for VBE only 2004-07-30 19:33 vruppert * VGABIOS-lgpl-latest.bin (1.59), VGABIOS-lgpl-latest.cirrus.bin (1.11), VGABIOS-lgpl-latest.cirrus.debug.bin (1.11), VGABIOS-lgpl-latest.debug.bin (1.59), clext.c (1.7): - cirrus init: set standard vga mode and reset bitblt 2004-07-22 18:38 vruppert * VGABIOS-lgpl-latest.bin (1.58), VGABIOS-lgpl-latest.cirrus.bin (1.10), VGABIOS-lgpl-latest.cirrus.debug.bin (1.10), VGABIOS-lgpl-latest.debug.bin (1.58), clext.c (1.6), vbe.c (1.45), vbetables.h (1.24): - cirrus extension: tables for mode 1280x1024x8 added - vbe: dispi_set_xres() and dispi_set_virt_width() now modify vga compatible registers - vbe: mode list entry for mode 800x600x4 fixed 2004-07-18 20:23 vruppert * VGABIOS-lgpl-latest.bin (1.57), VGABIOS-lgpl-latest.cirrus.bin (1.9), VGABIOS-lgpl-latest.cirrus.debug.bin (1.9), VGABIOS-lgpl-latest.debug.bin (1.57), vgabios.c (1.59), vgatables.h (1.8): - disable CRTC write protection before setting new values - CRTC line for mode 0x6a fixed 2004-07-07 16:08 vruppert * Makefile (1.15), VGABIOS-lgpl-latest.bin (1.56), VGABIOS-lgpl-latest.cirrus.bin (1.8), VGABIOS-lgpl-latest.cirrus.debug.bin (1.8), VGABIOS-lgpl-latest.debug.bin (1.56), biossums.c (1.1), clext.c (1.5): - biossums utility for the Bochs BIOS adapted for the LGPL'd VGABIOS - VESA3 PMINFO checksum calculated in the source - 24 bpp mode entries fixed (patch from Fabrice Bellard) 2004-06-25 18:28 vruppert * VGABIOS-lgpl-latest.cirrus.bin (1.7), VGABIOS-lgpl-latest.cirrus.debug.bin (1.7), clext.c (1.4): - 4MB memory probe added (patch from Fabrice Bellard) 2004-06-25 17:31 vruppert * VGABIOS-lgpl-latest.bin (1.55), VGABIOS-lgpl-latest.cirrus.bin (1.6), VGABIOS-lgpl-latest.cirrus.debug.bin (1.6), VGABIOS-lgpl-latest.debug.bin (1.55), clext.c (1.3): - fixed value of sequencer reset register in cirrus mode table - fixed possible overflow error if cirrus start address is >256k 2004-06-23 21:11 vruppert * VGABIOS-lgpl-latest.bin (1.54), VGABIOS-lgpl-latest.cirrus.bin (1.5), VGABIOS-lgpl-latest.cirrus.debug.bin (1.5), VGABIOS-lgpl-latest.debug.bin (1.54), clext.c (1.2): - applied new patch for the cirrus extension from suzu * enable VESA LFB support if a Cirrus PCI adapter is detected * prepared VBE3 protected mode info block (test case required) - added VBE functions 4F06h and 4F07h - some bugfixes 2004-06-17 18:57 vruppert * Makefile (1.14), VGABIOS-lgpl-latest.bin (1.53), VGABIOS-lgpl-latest.cirrus.bin (1.2), VGABIOS-lgpl-latest.cirrus.debug.bin (1.2), VGABIOS-lgpl-latest.debug.bin (1.53): - fixed makefile targets for the binaries with cirrus extension 2004-06-16 21:11 vruppert * Makefile (1.13), VGABIOS-lgpl-latest.bin (1.52), VGABIOS-lgpl-latest.cirrus.bin (1.1), VGABIOS-lgpl-latest.cirrus.debug.bin (1.1), VGABIOS-lgpl-latest.debug.bin (1.52), clext.c (1.1), vgabios.c (1.58): - applied suzu's cirrus extension patch. Cirrus SVGA detection, most of the cirrus-specific modes and some basic VBE features are present now. 2004-05-31 21:15 vruppert * VGABIOS-lgpl-latest.bin (1.51), VGABIOS-lgpl-latest.debug.bin (1.51), vgabios.c (1.57): - write character in planar graphics modes: sequencer map mask must be 0x0f and bit operation must be 'replace' if bit 7 of attribute is clear - read/write pixel in planar graphics modes: bit mask setup simplified 2004-05-11 18:08 vruppert * VGABIOS-lgpl-latest.bin (1.50), VGABIOS-lgpl-latest.debug.bin (1.50), vgabios.c (1.56): - biosfn_select_vert_res rewritten in assembler - scroll text in planar graphics modes: attribute for blank line fixed - write character in planar graphics modes: graphics controller values fixed 2004-05-09 20:32 vruppert * VGABIOS-lgpl-latest.bin (1.49), VGABIOS-lgpl-latest.debug.bin (1.49), vbe.c (1.44), vbe.h (1.24), vgabios.c (1.55): - VBE init code and some dispi ioport functions rewritten in assembler - text scroll functions for CGA graphics modes added - scroll text in graphics modes: attribute for blank line fixed 2004-05-08 16:06 vruppert * BUGS (1.2), README (1.7), TODO (1.10), VGABIOS-lgpl-latest.bin (1.48), VGABIOS-lgpl-latest.debug.bin (1.48), vbe.c (1.43), vbe.h (1.23), vbe_display_api.txt (1.11), vgabios.c (1.54): - VBE internal functions dispi_set_enable and dispi_set_bank now called both from C and asm code - VBE function 0x03 rewritten in assembler - VBE function 0x08 cleaned up - text output and scroll functions for graphics modes rewritten using case structures - documentation and comments updated 2004-05-06 21:18 vruppert * VGABIOS-lgpl-latest.bin (1.47), VGABIOS-lgpl-latest.debug.bin (1.47), vbe.c (1.42), vbe.h (1.22), vgabios.c (1.53): - VBE functions 0x05, 0x06, 0x07 and some dispi ioport functions rewritten in assembler - VBE functions 0x06 and 0x07: get functions now supported, 15 bpp bug fixed 2004-05-05 19:24 vruppert * VGABIOS-lgpl-latest.bin (1.46), VGABIOS-lgpl-latest.debug.bin (1.46), vbe.c (1.41), vbe.h (1.21), vbe_display_api.txt (1.10), vgabios.c (1.52): - 8 bit DAC capability flag set - vbe_biosfn_set_get_dac_palette_format implemented - VBE api description updated - C definitions from header files now used assembler code 2004-05-02 17:27 vruppert * VGABIOS-lgpl-latest.bin (1.45), VGABIOS-lgpl-latest.debug.bin (1.45), vgabios.c (1.51): - text scroll functions for PLANAR1/PLANAR4 graphics modes added - function biosfn_get_ega_info rewritten in assembler - read/write graphics pixel functions rewritten using a case structure 2004-05-01 16:03 vruppert * VGABIOS-lgpl-latest.bin (1.44), VGABIOS-lgpl-latest.debug.bin (1.44), vgabios.c (1.50): - biosfn_enable_cursor_emulation rewritten in assembler - remap of the cursor shape depends on modeset control bit 0 - text output in PLANAR4 modes now supports attribute bit 7 (XOR with background) 2004-04-25 20:13 vruppert * VGABIOS-lgpl-latest.bin (1.43), VGABIOS-lgpl-latest.debug.bin (1.43), vgabios.c (1.49), vgatables.h (1.7): - table entries for vga mode 0x0f fixed (PLANAR2 exists on EGA only) - function release_font_access now supports the monochrome text mode - PLANAR1 modes now supported in text output functions and read/write pixel - function AH=0x12/BL=0x32 rewritten in assembler 2004-04-25 08:45 vruppert * VGABIOS-lgpl-latest.bin (1.42), VGABIOS-lgpl-latest.debug.bin (1.42), vgabios.c (1.48): - block address calculation in font functions fixed - functions AX=0x1103, AH=0x12/BL=0x31 and AH=0x12/BL=0x33 rewritten in assembler 2004-04-24 09:59 vruppert * VGABIOS-lgpl-latest.bin (1.41), VGABIOS-lgpl-latest.debug.bin (1.41), vgabios.c (1.47): - read/write graphics pixel for PLANAR4 modes added - CGA specific functions (group AH = 0x0B) implemented 2004-04-23 14:34 vruppert * VGABIOS-lgpl-latest.bin (1.40), VGABIOS-lgpl-latest.debug.bin (1.40), vgabios.c (1.46): - remaining palette and dac read/write functions (except gray scale summing) rewritten in assembler 2004-04-18 13:43 vruppert * VGABIOS-lgpl-latest.bin (1.39), VGABIOS-lgpl-latest.debug.bin (1.39), vgabios.c (1.45): - some palette and dac read/write functions rewritten in assembler - main int10 debug message now works with assembler functions, too 2004-04-18 09:15 japj * vbe.c (1.40): updated my email address + put vgabios url in the bios copyright string (instead of my old email address) 2004-04-17 07:18 vruppert * VGABIOS-lgpl-latest.bin (1.38), VGABIOS-lgpl-latest.debug.bin (1.38), vgabios.c (1.44): - biosfn_set_video_mode: don't load DAC registers if default palette loading is disabled. Perform gray scale summing if enabled. - biosfn_perform_gray_scale_summing: switch between DAC read and write mode is required to make this function work. Maximum DAC value always set to 0x3f. 2004-04-08 17:50 vruppert * VGABIOS-lgpl-latest.bin (1.37), VGABIOS-lgpl-latest.debug.bin (1.37), vgabios.c (1.43): - write character function for the LINEAR8 mode - get_font_access() and release_font_access() rewritten in assembler - fixed wrong variable name in the init code 2004-04-06 19:31 vruppert * VGABIOS-lgpl-latest.bin (1.36), VGABIOS-lgpl-latest.debug.bin (1.36), vgabios.c (1.42): - init functions rewitten in assembler - function biosfn_set_display_code rewritten in assembler 2004-04-05 19:40 vruppert * VGABIOS-lgpl-latest.bin (1.35), VGABIOS-lgpl-latest.debug.bin (1.35), vgabios.c (1.41): - functions biosfn_get_video_mode() and biosfn_read_display_code() rewritten in assembler 2004-04-04 18:20 vruppert * VGABIOS-lgpl-latest.bin (1.34), VGABIOS-lgpl-latest.debug.bin (1.34), vgabios.c (1.40): - write character function for CGA modes added - read/write graphics pixel for CGA and LINEAR8 modes added 2004-02-23 21:08 vruppert * VGABIOS-lgpl-latest.bin (1.33), VGABIOS-lgpl-latest.debug.bin (1.33), vbe.c (1.39): - dispi_get_max_bpp(): restore the original value of the vbe enable register 2004-02-22 14:17 vruppert * README (1.6), vbe.c (1.38), vbe.h (1.20), vbe_display_api.txt (1.9), VGABIOS-lgpl-latest.bin (1.32), VGABIOS-lgpl-latest.debug.bin (1.32): - new function dispi_get_max_bpp() returns the bpp capabilities of the Bochs gui - create the mode list depending on the supported bpp capability - unused stuff removed - documentation updated 2004-02-21 18:20 vruppert * vbe.c (1.37), vbe.h (1.19), vbetables.h (1.23), VGABIOS-lgpl-latest.bin (1.31), VGABIOS-lgpl-latest.debug.bin (1.31): - dynamicly genarated vbe mode_info list works now 2003-11-17 21:04 vruppert * vbe.c (1.36), vbetables.h (1.22), vgabios.c (1.39), vgatables.h (1.6), VGABIOS-lgpl-latest.bin (1.30), VGABIOS-lgpl-latest.debug.bin (1.30): - new VBE presence flag stored at unused BDA address 0xB9 - VBE init code rewritten - added BIOS TTY flag for VBE mode 0x0102 (TODO: scrolling) - vgabios_init_func: load and activate text font already done by set_video_mode - function biosfn_get_all_palette_reg() fixed 2003-11-06 00:26 cbothamy * README (1.5): - add changes for 0.4c release 2003-11-06 00:22 cbothamy * VGABIOS-lgpl-latest.bin (1.29), VGABIOS-lgpl-latest.debug.bin (1.29): - compile vgabios.c rev1.38 2003-11-06 00:21 cbothamy * vgabios.c (1.38): - activate char table after loading it when setting a text video mode 2003-11-06 00:19 cbothamy * Makefile (1.12): - when making a release, remove unwanted files first, and exclude CVS from the tarball 2003-11-04 22:50 cbothamy * ChangeLog (1.20, v0_4b): - update ChangeLog for 0.4b release 2003-11-04 22:49 cbothamy * README (1.4, v0_4b): - update Changes for 0.4b release 2003-11-04 20:26 vruppert * vgabios.c (1.37), VGABIOS-lgpl-latest.bin (1.28), VGABIOS-lgpl-latest.debug.bin (1.28) (utags: v0_4b): - biosfn_get_font_info(): character height must be returned in CX 2003-11-03 21:57 vruppert * vbe.c (1.35, v0_4b), vgabios.c (1.36), VGABIOS-lgpl-latest.bin (1.27), VGABIOS-lgpl-latest.debug.bin (1.27): - the 'noclearmem' flag is not stored in the 'current video mode' register (0040h:0049h) - VBE also stores the 'noclear' flag in the 'video control' register (0040h:0087h) 2003-10-05 10:06 vruppert * vbe.h (1.18, v0_4b), vbe_display_api.txt (1.8, v0_4b), VGABIOS-lgpl-latest.bin (1.26), VGABIOS-lgpl-latest.debug.bin (1.26): - changed VBE i/o registers to 0x01CE/CF (suggestion from Daniel Gimpelevich) 2003-08-18 18:38 vruppert * VGABIOS-lgpl-latest.bin (1.25), VGABIOS-lgpl-latest.debug.bin (1.25), vgabios.c (1.35): - wrong offsets to the character tables (INT 0x1F/0x43) fixed (underscore added) - functions accessing the CRT controller optimized using a local variable 'crtc_addr' 2003-08-17 15:46 cbothamy * ChangeLog (1.19, v0_4a): - ChangeLog is now automatically generated by running "cvs2cl -r -t -P -S" - update ChangeLog for 0.4a release 2003-08-17 15:44 cbothamy * README (1.3, v0_4a): - added the old ChangeLog in the HOSTORY section of the README file - update History for 0.4a release, with a summary of Changes 2003-08-17 15:24 cbothamy * Makefile (1.11, v0_4b, v0_4a): - fix Makefile for "release" target 2003-08-16 01:49 cbothamy * Makefile (1.10), README (1.2), VGABIOS-lgpl-latest.bin (1.24, v0_4a), VGABIOS-lgpl-latest.debug.bin (1.24, v0_4a), vgabios.c (1.34, v0_4a): - update the Makefile for releases - remove references to old plex86 website - update the Makefile so it build VGABIOS-lgpl-latest.bin and VGABIOS-lgpl-latest.debug.bin 2003-08-07 18:17 vruppert * VGABIOS-lgpl-latest.bin (1.23), VGABIOS-lgpl-latest.debug.bin (1.23): - current VBE mode now stored in BDA (unused address 0xBA) 2003-08-07 17:54 vruppert * vbe.c (1.34), vgatables.h (1.5, v0_4b) (utags: v0_4a): - current VBE mode now stored in BDA (unused address 0xBA) 2003-07-20 18:05 vruppert * vgabios.c (1.33), VGABIOS-lgpl-latest.bin (1.22), VGABIOS-lgpl-latest.debug.bin (1.22): - fixed a few functions accessing the attribute controller 2003-07-19 09:33 vruppert * vgabios.c (1.32), VGABIOS-lgpl-latest.bin (1.21), VGABIOS-lgpl-latest.debug.bin (1.21): - re-enable video after programming the attribute controller - biosfn_set_all_palette_reg(): number of palette registers fixed 2003-07-16 22:32 vruppert * ChangeLog (1.18), vbe.c (1.33), vbe.h (1.17, v0_4a), vbe_display_api.txt (1.7, v0_4a), vgabios.c (1.31), VGABIOS-lgpl-latest.bin (1.20), VGABIOS-lgpl-latest.debug.bin (1.20): - LFB flag now stored in the register VBE_DISPI_INDEX_ENABLE - release date in Changelog fixed - release date of VBE BIOS 0.6 was the same as VGA BIOS 0.3b - year changed in copyright messages 2003-07-15 12:40 vruppert * VGABIOS-lgpl-latest.bin (1.19), VGABIOS-lgpl-latest.debug.bin (1.19): - new function dispi_get_bpp() - function vbe_biosfn_set_get_logical_scan_line_length() fixed for >8bpp - number of image pages of all VBE modes fixed 2003-07-15 12:35 vruppert * vbe.c (1.32), vbetables.h (1.21, v0_4b, v0_4a): - new function dispi_get_bpp() - function vbe_biosfn_set_get_logical_scan_line_length() fixed for >8bpp - number of image pages of all VBE modes fixed 2003-07-14 19:45 vruppert * vbe_display_api.txt (1.6): - description of VBE_DISPI_ interface 0xb0c2 added 2003-07-10 19:07 vruppert * vbe.c (1.31), vbetables.h (1.20), VGABIOS-lgpl-latest.bin (1.18), VGABIOS-lgpl-latest.debug.bin (1.18): - 15 bpp VBE modes added - "Bochs own" mode 0x142 (640x480x32bpp) added 2003-07-01 19:00 vruppert * vbe.c (1.30), vbe.h (1.16), vbetables.h (1.19), VGABIOS-lgpl-latest.bin (1.17), VGABIOS-lgpl-latest.debug.bin (1.17): - VBE preserve display memory feature implemented - VBE mode entries 0x117 and 0x118 added 2003-06-30 21:27 vruppert * vbe.c (1.29), vbe.h (1.15), vbetables.h (1.18), VGABIOS-lgpl-latest.bin (1.16), VGABIOS-lgpl-latest.debug.bin (1.16): - VBE mode info blocks of modes with >8bpp enabled - VBE modes with 24 bpp: bytes per scanline fixed - vbe_biosfn_set_mode() now supports >8bpp - VBE will be enabled with new VBE_DISPI_ID2 (0xB0C2) 2003-06-29 12:53 vruppert * vbetables.h (1.17), VGABIOS-lgpl-latest.bin (1.15), VGABIOS-lgpl-latest.debug.bin (1.15): - duplicate lines with VBE_MODE_ATTRIBUTE_GRAPHICS_MODE removed - VBE mode info items of currently unsupported modes fixed 2003-06-15 21:19 vruppert * vgabios.c (1.30), VGABIOS-lgpl-latest.bin (1.14), VGABIOS-lgpl-latest.debug.bin (1.14): - function write_gfx_char() rewritten 2003-04-26 09:27 vruppert * VGABIOS-lgpl-latest.debug.bin (1.13): - added missing VBE function dispi_get_bank() - added missing return codes for VBE function 4F05h - memory size is always reported in VBE function 4F00h - fixed scan line length for VBE mode 0102h - fixed function set_active_page() for graphics modes - fixed the page sizes of some VGA modes 2003-04-26 09:22 vruppert * vbe.c (1.28), vbetables.h (1.16), vgabios.c (1.29), vgatables.h (1.4), VGABIOS-lgpl-latest.bin (1.13): - added missing VBE function dispi_get_bank() - added missing return codes for VBE function 4F05h - memory size is always reported in VBE function 4F00h - fixed scan line length for VBE mode 0102h - fixed function set_active_page() for graphics modes - fixed the page sizes of some VGA modes 2003-04-20 09:51 vruppert * vgabios.c (1.28), vgatables.h (1.3), VGABIOS-lgpl-latest.bin (1.12), VGABIOS-lgpl-latest.debug.bin (1.12): - function write_gfx_char() now supports different font sizes - some entries of the static functionality table fixed 2003-04-18 09:23 vruppert * vbe.c (1.27), vbe.h (1.14), vbetables.h (1.15): - applied patch #1331 * new function dispi_set_bank_farcall() * VBE mode info item WinFuncPtr points to the new function if the flag VBE_WINDOW_ATTRIBUTE_RELOCATABLE is set * flag VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE added 2003-02-11 20:17 vruppert * VGABIOS-lgpl-latest.bin (1.11), VGABIOS-lgpl-latest.debug.bin (1.11), vbe.c (1.26), vbetables.h (1.14): - VBE mode search rewritten * improved function mode_info_find_mode() is now used by the VBE functions 0x4F01 and 0x4F02 * removed all mode list entries with the LFB bit set. LFB detection is now present in the function mode_info_find_mode() 2003-02-09 20:59 vruppert * VGABIOS-lgpl-latest.bin (1.10), VGABIOS-lgpl-latest.debug.bin (1.10), vgabios.c (1.27): - function write_gfx_char(): memory address now calculated in this function; background color is always black - function biosfn_write_char_attr(): the count parameter is now used in graphics modes too - function biosfn_write_char_only() works the same way as function biosfn_write_char_attr() in graphics mode - copying charmap data optimized using memcpyb() 2003-02-09 11:36 vruppert * VGABIOS-lgpl-latest.bin (1.9), VGABIOS-lgpl-latest.debug.bin (1.9): - VESA mode 0x102 added (uses existing SVGA mode 0x6a) - all VESA modes with the LFB flag set removed from the list (Linux doesn't like mode numbers > 0x07ff) 2003-02-09 11:02 vruppert * vbe.c (1.25), vbe.h (1.13), vbetables.h (1.13): - VESA mode 0x102 added (uses existing SVGA mode 0x6a) - all VESA modes with the LFB flag set removed from the list (Linux doesn't like mode numbers > 0x07ff) 2003-02-08 13:04 vruppert * vbe.c (1.24), vgabios.c (1.26): - vbe_biosfn_return_current_mode() now returns the active standard VGA mode TODO: return VESA mode if enabled - biosfn_set_video_mode() now clears the screen in CGA mode correctly - write character functions are now working in all PLANAR4 graphics modes - added stubs for unimplemented features in graphics modes 2003-02-04 22:19 vruppert * VGABIOS-lgpl-latest.bin (1.8), VGABIOS-lgpl-latest.debug.bin (1.8): - set video mode: clear vga memory in graphics mode - set video mode: load default font in text mode - write character implemented for graphics mode 0x12 2003-02-04 22:06 vruppert * vgabios.c (1.25): - set video mode: clear vga memory in graphics mode - set video mode: load default font in text mode - write character implemented for graphics mode 0x12 2003-01-21 19:30 vruppert * vgabios.c (1.24): - remap the cursor size if the char height is > 8 and the new values are < 8 2003-01-20 18:24 cbothamy * Makefile (1.9): - fix so make -j2 does not overwrite temp files 2003-01-19 12:35 vruppert * vgabios.c (1.23): - function set_scan_lines() recalculates the number of rows and the page size - new values for char height, text rows and page size are stored in the BIOS data segment - asm helper function idiv_u added 2003-01-15 18:49 cbothamy * VGABIOS-lgpl-latest.bin (1.7), VGABIOS-lgpl-latest.debug.bin (1.7): - compile vgabios rev 1.22 2003-01-15 18:49 cbothamy * vgabios.c (1.22): - fix bug found by ams : a 8bits index value was compared to 0x100 in some cases in biosfn_set_all_dac_reg, biosfn_read_all_dac_reg, biosfn_perform_gray_scale_summing 2003-01-15 17:34 cbothamy * Makefile (1.8): - fix symbol table file names, discovered by ams 2003-01-04 21:20 vruppert * VGABIOS-lgpl-latest.bin (1.6), VGABIOS-lgpl-latest.debug.bin (1.6), vgabios.c (1.21): - biosfn_set_video_mode(): reset attribute controller flip-flop before setting up the controller's registers (bug found with amidiag) 2003-01-04 09:50 vruppert * vbe.c (1.23): - VBE function 0x00 returns VBE 1.x compatible information if no VBE signature is present 2003-01-01 12:44 vruppert * VGABIOS-lgpl-latest.bin (1.5), VGABIOS-lgpl-latest.debug.bin (1.5): - SVGA mode 0x6A (800x600x4) added to the list of graphics modes 2002-12-31 18:07 vruppert * vgatables.h (1.2): - SVGA mode 0x6A (800x600x4) added to the list of graphics modes 2002-11-23 10:38 cbothamy * ChangeLog (1.17, v0_3b): - fix changelog for 0.3b release 2002-10-20 17:12 vruppert * VGABIOS-lgpl-latest.bin (1.4), VGABIOS-lgpl-latest.debug.bin (1.4), vgabios.c (1.20) (utags: v0_3b): - new function set_scan_lines() for the font size change (patch from Hartmut Birr) - cursor shape start and end must be updated in set_scan_lines() - set_scan_lines() is called by the functions 0x1110, 0x1111, 0x1112 and 0x1114 after copying the font data 2002-10-04 08:20 vruppert * VGABIOS-lgpl-latest.bin (1.3), VGABIOS-lgpl-latest.debug.bin (1.3), vgabios.c (1.19): - biosfn_set_single_dac_reg(): the red value is stored in DH 2002-09-19 19:05 cbothamy * VGABIOS-lgpl-latest.bin (1.2), VGABIOS-lgpl-latest.debug.bin (1.2): - updated with latest changes 2002-09-19 19:03 cbothamy * ChangeLog (1.16), Makefile (1.7, v0_3b), vbe.c (1.22, v0_3b), vgabios.c (1.18), vgabios.h (1.3, v0_4b, v0_4a, v0_3b): - updated the Makefile - removed display of copyrights. - changed the Copyright string to "LGPL VGABios developers" 2002-09-08 21:14 vruppert * vgabios.c (1.17): - set the cursor shape depending on the current font height - clear BL before calling int 0x10 function 0x1103 in vgabios_init_func 2002-08-23 22:58 cbothamy * vbe.c (1.21), vbetables.h (1.12, v0_3b): - added lfb-mode numbers (patch from mathis) 2002-07-21 21:57 japj * vbe.c (1.20), vgabios.c (1.16): gcc2/3 preprocessing fix 2002-05-18 16:55 cbothamy * vgabios.c (1.15): - include patch from Volker that adds some text font functions 2002-05-01 23:13 japj * VGABIOS-lgpl-latest.bin (1.1), VGABIOS-lgpl-latest.debug.bin (1.1): adding latest bin & debug bin of the vgabios 2002-04-29 14:50 japj * ChangeLog (1.15), vbe.c (1.19), vbe.h (1.12, v0_3b), vbetables.h (1.11), vgabios.c (1.14): - applying hw scrolling/multibuffering patch 2002-04-25 21:59 japj * Makefile (1.6), vbe.c (1.18), vgabios.c (1.13): - reverting #asm/##asm & endasm patch (does not work with with cygwin) 2002-04-19 19:38 japj * Makefile (1.5), vbe.c (1.17), vgabios.c (1.12): - fixing preprocessing of vgabios with latest gcc (from Mandrake 8.2) 2002-04-08 23:44 japj * ChangeLog (1.14), vbe_display_api.txt (1.5, v0_3b): - preparing docs for new DISPI interface (for hardware scrolling) 2002-04-03 19:06 japj * ChangeLog (1.13), TODO (1.9, v0_4b, v0_4a, v0_3b), vbe.c (1.16): - defaulting LFB on + updated changelog & todo 2002-04-03 00:38 cbothamy * vbe.c (1.15), vgabios.c (1.11): - changed the logging ports to 0x500 -> 0x502 2002-03-14 17:54 japj * vbe.c (1.14): - vbetables.h is dependant upon some defines (VBE_HAVE_LFB), so put the include *after* the define 2002-03-13 21:47 japj * ChangeLog (1.12), TODO (1.8), vbe.c (1.13), vbetables.h (1.10), vgabios.c (1.10): - made LFB dependant upon define - not implement vbe functions return failure - updated todo & docs for things after bochs 1.4 2002-03-13 19:46 japj * vbe.h (1.11), vbe_display_api.txt (1.4): - added max video memory + documented what is in the 0xb0c0 interface 2002-03-12 02:33 cbothamy * ChangeLog (1.11), Makefile (1.4): - updated for 0.3a. Merged vgabios.bin and vbebios.bin 2002-03-10 21:36 japj * ChangeLog (1.10), vbetables.h (1.9): - added LFB modes for testing with vbe-lfb patch in Bochs 2002-03-10 17:42 japj * vbe.c (1.12, v0_3a): - show people when they do NOT have VBE support available 2002-03-10 17:36 japj * TODO (1.7, v0_3a), vbe.c (1.11), vbe.h (1.10, v0_3a), vgabios.c (1.9, v0_3a): - cleanup of vbe internal functions (set 8bpp mode is now dependant on ModeInfo content instead of hardcoded functions) 2002-03-10 17:20 cbothamy * ChangeLog (1.9, v0_3a), TODO (1.6): - updated for 0.3a 2002-03-10 17:19 cbothamy * vbe.c (1.10), vbe.h (1.9): - added vbe_has_vbe_display function that detects an attached vbe display 2002-03-10 17:12 cbothamy * vgabios.c (1.8): - vbe calls are done only if a vbe display is detected 2002-03-10 11:25 japj * vbe.h (1.8), vbe_display_api.txt (1.3, v0_3a): - preparing for LFB support 2002-03-09 14:25 japj * vgabios.c (1.7): - fixing initial cursor shape to _ instead of - 2002-03-08 23:08 japj * ChangeLog (1.8), TODO (1.5), vbe.c (1.9), vbe.h (1.7), vgabios.c (1.6): - updating vbe code to new API 2002-03-08 21:48 japj * vbe.c (1.8), vbe.h (1.6), vbetables.h (1.8, v0_3a): - updating vbe code with #defines from API 2002-03-08 21:31 japj * vbe_display_api.txt (1.2): - adding some text about how banks work 2002-03-08 21:09 japj * ChangeLog (1.7), vbe_display_api.txt (1.1): - adding vbe_display_api documentation 2002-03-07 21:36 japj * ChangeLog (1.6), vbe.c (1.7), vbetables.h (1.7): - added 1024x768xbpp support - some more cleanups/comments 2002-03-06 21:55 japj * ChangeLog (1.5), TODO (1.4), vbe.c (1.6), vbetables.h (1.6), vgabios.c (1.5): - updated changelog with new modi - added 640x480x8 (Mandrake Installer can use this!) - added pre VBE2 compatible 'detection' - fixed problem when normal vga set mode wouldn't disable vbe mode 2002-03-06 20:59 japj * TODO (1.3), vbe.c (1.5), vbe.h (1.5), vbetables.h (1.5), vgabios.c (1.4): - adding 640x400x8 and 800x600x8 vbe support (this depends HEAVILY on my bochs vga code patch - japj) 2002-03-06 18:00 japj * vbe.c (1.4), vbe.h (1.4), vbetables.h (1.4): - implemented banked & lfb support for 320x200x8bpp (some fixes for vbetest program not displaying anything) 2002-03-05 20:25 japj * Makefile (1.3, v0_3a): for vbe debug bios: - print debugging information in assembly output - print source code in assembly output 2002-03-01 19:39 japj * ChangeLog (1.4), TODO (1.2), vbe.c (1.3), vbe.h (1.3), vbetables.h (1.3): - added vbe support for 320x200x8 using the standard vgamode (0x13) 2002-02-19 00:29 japj * ChangeLog (1.3): - updating ChangeLog with lfbprof 2002-02-18 23:26 japj * tests/lfbprof/: lfbprof.c (1.2), lfbprof.h (1.2) (utags: v0_3a, v0_3b, v0_4a, v0_4b): - fixed unsigned short for mode list (-1 != 0xffff otherwise) - fixed LfbMapRealPointer macro mask problem (some modes were skipped) - added some extra 'debugging' printf's 2002-02-18 23:07 japj * tests/lfbprof/: Makefile (1.1, v0_4b, v0_4a, v0_3b, v0_3a), lfbprof.c (1.1), lfbprof.h (1.1): - Adding lfbprof testprogram (for vbe testing purposes) It needs to be compiled with the Watcom C Compiler 2002-02-18 18:48 japj * vbe.c (1.2), vbe.h (1.2): - cosmetic updates to vbe.c/h + added bunch of FIXMEs for work that needs to be done 2002-02-18 18:34 japj * vbetables.h (1.2): - cosmetic updates in vbetables.h 2002-02-18 18:32 japj * ChangeLog (1.2): updated changelog with merge of vbebios 0.2 2002-02-18 18:07 japj * vgabios.c (1.3): - small cosmetic cleanup in vgabios vbe code + added FIXMEs 2002-02-18 17:55 japj * Makefile (1.2), dataseghack (1.2, v0_4b, v0_4a, v0_3b, v0_3a), vbe.c (1.1), vbe.h (1.1), vbetables.h (1.1), vgabios.c (1.2), vgabios.h (1.2, v0_3a): - merging with vbebios 0.2 release 2002-02-18 11:31 cbothamy * BUGS (1.1, v0_4b, v0_4a, v0_3b, v0_3a), COPYING (1.1, v0_4b, v0_4a, v0_3b, v0_3a), ChangeLog (1.1), Makefile (1.1), Notes (1.1, v0_4b, v0_4a, v0_3b, v0_3a), README (1.1, v0_3b, v0_3a), TODO (1.1), dataseghack (1.1), vgabios.c (1.1), vgabios.h (1.1), vgafonts.h (1.1, v0_4b, v0_4a, v0_3b, v0_3a), vgatables.h (1.1, v0_3b, v0_3a), tests/testbios.c (1.1, v0_4b, v0_4a, v0_3b, v0_3a): - initial import xen-4.9.2/tools/firmware/vgabios/vgabios.h0000664000175000017500000000253513256712137016673 0ustar smbsmb#ifndef vgabios_h_included #define vgabios_h_included /* Types */ typedef unsigned char Bit8u; typedef unsigned short Bit16u; typedef unsigned long Bit32u; typedef unsigned short Boolean; /* Defines */ #define SET_AL(val8) AX = ((AX & 0xff00) | (val8)) #define SET_BL(val8) BX = ((BX & 0xff00) | (val8)) #define SET_CL(val8) CX = ((CX & 0xff00) | (val8)) #define SET_DL(val8) DX = ((DX & 0xff00) | (val8)) #define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8)) #define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8)) #define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8)) #define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8)) #define GET_AL() ( AX & 0x00ff ) #define GET_BL() ( BX & 0x00ff ) #define GET_CL() ( CX & 0x00ff ) #define GET_DL() ( DX & 0x00ff ) #define GET_AH() ( AX >> 8 ) #define GET_BH() ( BX >> 8 ) #define GET_CH() ( CX >> 8 ) #define GET_DH() ( DX >> 8 ) #define SET_CF() FLAGS |= 0x0001 #define CLEAR_CF() FLAGS &= 0xfffe #define GET_CF() (FLAGS & 0x0001) #define SET_ZF() FLAGS |= 0x0040 #define CLEAR_ZF() FLAGS &= 0xffbf #define GET_ZF() (FLAGS & 0x0040) #define SCROLL_DOWN 0 #define SCROLL_UP 1 #define NO_ATTR 2 #define WITH_ATTR 3 #define SCREEN_SIZE(x,y) (((x*y*2)|0x00ff)+1) #define SCREEN_MEM_START(x,y,p) ((((x*y*2)|0x00ff)+1)*p) #define SCREEN_IO_START(x,y,p) ((((x*y)|0x00ff)+1)*p) #endif xen-4.9.2/tools/firmware/vgabios/README0000664000175000017500000001746013256712137015753 0ustar smbsmbPlex86/Bochs VGABios -------------------- The goal of this project is to have a LGPL'd Video Bios in plex86, Bochs and qemu. This VGA Bios is very specific to the emulated VGA card. It is NOT meant to drive a physical vga card. Cirrus SVGA extension --------------------- The Cirrus SVGA extension is designed for the Cirrus emulation in Bochs and qemu. The initial patch for the Cirrus extension has been written by Makoto Suzuki (suzu). Install ------- To compile the VGA Bios you will need : - gcc - bcc - as86 - ld86 Untar the archive, and type make. You should get a "VGABIOS-lgpl-latest.bin" file. Alternatively, you can use the binary file "VGABIOS-lgpl-latest.bin", i have compiled for you. Edit your plex86/bochs conf file, and modify the load-rom command in the VGA BIOS section, to point to the new vgabios image file. Debugging --------- You can get a very basic debugging system: messages printed by the vgabios. You have to register the "unmapped" device driver in plex86 or bochs, and make sure it grabs port 0xfff0. Comment the #undef DEBUG at the beginning of vgabios.c. You can then use the "printf" function in the bios. Testing ------- Look at the "testvga.c" file in the archive. This is a minimal Turbo C 2.0 source file that calls a few int10 functions. Feel free to modify it to suit your needs. Copyright and License --------------------- This program has been written by Christophe Bothamy It is protected by the GNU Lesser Public License, which you should have received a copy of along with this package. Reverse Engineering ------------------- The VGA Bios has been written without reverse-engineering any existing Bios. Acknowledgment -------------- The source code contains code ripped from rombios.c of plex86, written by Kevin Lawton The source code contains fonts from fntcol16.zip (c) by Joseph Gil avalable at : ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip These fonts are public domain The source code is based on information taken from : - Kevin Lawton's vga card emulation for bochs/plex86 - Ralf Brown's interrupts list avalaible at http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html - Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/ - Michael Abrash's Graphics Programming Black Book - Francois Gervais' book "programmation des cartes graphiques cga-ega-vga" edited by sybex - DOSEMU 1.0.1 source code for several tables values and formulas Feedback -------- Please report any bugs, comments, patches for this VGA Bios to info@vruppert.de You can find the latest release at : http://www.nongnu.org/vgabios/ For any information on bochs, visit the website http://bochs.sourceforge.net/ For any information on qemu, visit the website http://fabrice.bellard.free.fr/qemu/ History ------- vgabios-0.6b : May 30 2008 - Volker . added PCI data structure for the Cirrus VGABIOS images . minor bugfixes in biossums utility, VBE support and makefile vgabios-0.6a : Aug 19 2006 - Volker . added minimal support for the video parameter table (VPT) . Cirrus SVGA now supports the "no clear" bit in Cirrus and VESA mode . Bochs VBE protected mode interface improved . save/restore video state support for Bochs VBE and standard VGA added . generate vbetables.h dynamicly . VBE video memory increased to 8 MB (VBE dispi ID changed to B0C4) . lots of 4bpp VBE fixes (all 4bpp VBE modes now enabled) . VGA compatible setup for VBE modes added vgabios-0.5d : Dec 29 2005 - Volker . Bochs VBE protected mode interface added (based on a patch by malc@pulsesoft.com) . biossums utility now supports VGABIOS sizes up to 64 kBytes . VGA mode 0x11: all color planes must be enabled in this 2-color VGA mode vgabios-0.5c : Jul 07 2005 - Volker . BIOS configuration word usually reports initial mode 80x25 color text . vgabios function 0x0e (write teletype): linefeed (0x0a) only increments the cursor row value vgabios-0.5b : May 24 2005 - Volker . fixed return value for the default case in the VBE section (non-debug mode) . removed unused stuff vgabios-0.5a : Mar 07 2005 - Volker . Cirrus SVGA extension (initial patches from Makoto Suzuki, improvements from Fabrice Bellard) . vgabios image size is now exactly 32k with a checksum . a lot of vgabios and vbe functions rewritten in assembler . dynamicly generated VBE mode info list . write character function for CGA and LINEAR8 modes . read/write graphics pixel for some graphics modes . text scroll feature for some graphics modes . VBE 8-bit DAC support vgabios-0.4c : Nov 06 2003 - Christophe . fix font problem on initial screen of NT4 Loader vgabios-0.4b : Nov 04 2003 - Volker . fix offset of character tables . optimizations of CRT controller accesses . VBE i/o registers changed to 0x01CE/CF (suggestion from Daniel Gimpelevich) . "noclear" flag stored in BIOS area . fix character height returned by get_font_info function vgabios-0.4a : Aug 17 2003 - Volker . VBE mode search rewritten (VBE modes with LFB bit removed) . many bugfixes and optimizations . write character function implemented for graphics modes . support for 15bpp, 16bpp, 24bpp and 32bpp VBE modes added . SVGA mode 0x6A added . VBE modes 0x102, 0x117, 0x118 and 0x142 (Bochs specific) vgabios-0.3b : Nov 23 2002 - Christophe . added lfb-mode numbers (patch from mathis) . updated the Makefile . removed display of copyrights. . changed the Copyright string to "LGPL VGABios developers" - Volker . set the cursor shape depending on the current font height . clear BL before calling int 0x10 function 0x1103 in vgabios_init_func . added some text font functions - Jeroen . Forced to new DISPI (0xb0c1) interface (requires latest bochs vbe code) . Added multibuffering support . Added new DISPI interface for: virt width, height, x offset, y offset . Added LFB modes (to be used with the vbe-lfb patch in bochs) see VBE_HAVE_LFB in vbe.c (currently default enabled) . updated TODO & docs for changes after bochs 1.4 vgabios-0.3a : Mar 10 2002 - Christophe . Fixed bug in function ah=13 - Jeroen . updated vbebios implementation to new api . added vbe_display_api documentation . added 640x400x8, 640x480x8, 800x600x8, 1024x768 (>640x480 needs a special bochs patch atm) . added 320x200x8 vbe support (uses the standard 320x200x8 vga mode to display, this allows for testing & having something on screen as well, at least until bochs host side display is up & running) . adding lfbprof (vbe) testprogram (+some small fixes to it) . merging with vbebios 0.2 vgabios-0.2b : Nov 19 2001 - Christophe . Fixed bug in function ah=13 vgabios-0.2a : Nov 09 2001 - Christophe . Included bugfix from techt@pikeonline.net about grayscale summing . Added the "IBM" string at org 0x1e as Bart Oldeman suggested . Fixed DS and ES that where inverted in the int10 parameters list! . The following have been implemented : - function ax=1a00, ax=1a01, ah=1b - function ax=1130 . Added debug messages for unimplemented/unknown functions Must be compiled with DEBUG defined. The output is trapped by the unknown-ioport driver of plex/bochs (port 0xfff0 is used) vgabios-0.1a : May 8 2001 - Christophe . First release. The work has been focused only on text mode. . The following have been implemented : - inits - int 10 handler - functions ah=00, ah=01, ah=02, ah=03, ah=05, ah=06, ah=07, ah=08 ah=09, ah=0a, ah=0e, ah=0f, ax=1000, ax=1001, ax=1002, ax=1003 ax=1007, ax=1008, ax=1009, ax=1010, ax=1012, ax=1013, ax=1015 ax=1017, ax=1018, ax=1019, ax=101a, ax=101b, ah=12 bl=10, ah=12 bl=30, ah=12 bl=31, ah=12 bl=32, ah=12 bl=33, ah=12 bl=34 ah=13 xen-4.9.2/tools/firmware/vgabios/vgafonts.h0000664000175000017500000016414013256712137017071 0ustar smbsmb/* * These fonts come from ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip * The package is (c) by Joseph Gil * The individual fonts are public domain */ static Bit8u vgafont8[256*8]= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78, 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00, 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38, 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00, 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00, 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00, 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00, 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30, 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7, 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70, 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00, 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f, 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00, 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0, 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00, 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00, 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0, 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00, 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static Bit8u vgafont14[256*14]= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x66, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x66, 0x6c, 0x6c, 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x76, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, 0x00, 0xc6, 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0x40, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static Bit8u vgafont16[256*16]= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static Bit8u vgafont14alt[1]={0x00}; static Bit8u vgafont16alt[1]={0x00}; xen-4.9.2/tools/firmware/vgabios/vbetables-gen.c0000664000175000017500000002260013256712137017745 0ustar smbsmb/* Generate the VGABIOS VBE Tables */ #include #include #define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 16 typedef struct { int width; int height; int depth; int mode; } ModeInfo; ModeInfo modes[] = { /* standard VESA modes */ { 640, 400, 8 , 0x100}, { 640, 480, 8 , 0x101}, { 800, 600, 4 , 0x102}, { 800, 600, 8 , 0x103}, { 1024, 768, 4 , 0x104}, { 1024, 768, 8 , 0x105}, { 1280, 1024, 4 , 0x106}, { 1280, 1024, 8 , 0x107}, { 320, 200, 15 , 0x10D}, { 320, 200, 16 , 0x10E}, { 320, 200, 24 , 0x10F}, { 640, 480, 15 , 0x110}, { 640, 480, 16 , 0x111}, { 640, 480, 24 , 0x112}, { 800, 600, 15 , 0x113}, { 800, 600, 16 , 0x114}, { 800, 600, 24 , 0x115}, { 1024, 768, 15 , 0x116}, { 1024, 768, 16 , 0x117}, { 1024, 768, 24 , 0x118}, { 1280, 1024, 15 , 0x119}, { 1280, 1024, 16 , 0x11A}, { 1280, 1024, 24 , 0x11B}, { 1600, 1200, 8 , 0x11C}, { 1600, 1200, 15 , 0x11D}, { 1600, 1200, 16 , 0x11E}, { 1600, 1200, 24 , 0x11F}, /* BOCHS/PLE, 86 'own' mode numbers */ { 320, 200, 32 , 0x140}, { 640, 400, 32 , 0x141}, { 640, 480, 32 , 0x142}, { 800, 600, 32 , 0x143}, { 1024, 768, 32 , 0x144}, { 1280, 1024, 32 , 0x145}, { 320, 200, 8 , 0x146}, { 1600, 1200, 32 , 0x147}, { 1152, 864, 8 , 0x148}, { 1152, 864, 15 , 0x149}, { 1152, 864, 16 , 0x14a}, { 1152, 864, 24 , 0x14b}, { 1152, 864, 32 , 0x14c}, { 1280, 800, 16 , 0x178}, { 1280, 800, 24 , 0x179}, { 1280, 800, 32 , 0x17a}, { 1280, 960, 16 , 0x17b}, { 1280, 960, 24 , 0x17c}, { 1280, 960, 32 , 0x17d}, { 1440, 900, 16 , 0x17e}, { 1440, 900, 24 , 0x17f}, { 1440, 900, 32 , 0x180}, { 1400, 1050, 16 , 0x181}, { 1400, 1050, 24 , 0x182}, { 1400, 1050, 32 , 0x183}, { 1680, 1050, 16 , 0x184}, { 1680, 1050, 24 , 0x185}, { 1680, 1050, 32 , 0x186}, { 1920, 1200, 16 , 0x187}, { 1920, 1200, 24 , 0x188}, { 1920, 1200, 32 , 0x189}, { 2560, 1600, 16 , 0x18a}, { 2560, 1600, 24 , 0x18b}, { 2560, 1600, 32 , 0x18c}, { 0, }, }; int main(int argc, char **argv) { const ModeInfo *pm; int pages, pitch; int r_size, r_pos, g_size, g_pos, b_size, b_pos, a_size, a_pos; const char *str; long vram_size = VBE_DISPI_TOTAL_VIDEO_MEMORY_MB * 1024 * 1024; printf("/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n"); printf("#define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB %d\n\n", VBE_DISPI_TOTAL_VIDEO_MEMORY_MB); printf("static ModeInfoListItem mode_info_list[]=\n"); printf("{\n"); for (pm = modes; pm->mode != 0; pm++) { if (pm->depth == 4) pitch = (pm->width + 7) / 8; else pitch = pm->width * ((pm->depth + 7) / 8); pages = vram_size / (pm->height * pitch); if (pages > 0) { printf("{ 0x%04x, /* %dx%dx%d */\n", pm->mode, pm->width, pm->height, pm->depth); if (pm->depth == 4) printf("{ /*Bit16u ModeAttributes*/ %s,\n", "VBE_MODE_ATTRIBUTE_SUPPORTED | " "VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | " "VBE_MODE_ATTRIBUTE_COLOR_MODE | " "VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT | " "VBE_MODE_ATTRIBUTE_GRAPHICS_MODE"); else printf("{ /*Bit16u ModeAttributes*/ %s,\n", "VBE_MODE_ATTRIBUTE_SUPPORTED | " "VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | " "VBE_MODE_ATTRIBUTE_COLOR_MODE | " "VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE | " "VBE_MODE_ATTRIBUTE_GRAPHICS_MODE"); printf("/*Bit8u WinAAttributes*/ %s,\n", "VBE_WINDOW_ATTRIBUTE_RELOCATABLE | " "VBE_WINDOW_ATTRIBUTE_READABLE | " "VBE_WINDOW_ATTRIBUTE_WRITEABLE"); printf("/*Bit8u WinBAttributes*/ %d,\n", 0); printf("/*Bit16u WinGranularity*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB"); printf("/*Bit16u WinSize*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB"); printf("/*Bit16u WinASegment*/ %s,\n", "VGAMEM_GRAPH"); printf("/*Bit16u WinBSegment*/ 0x%04x,\n", 0); printf("/*Bit32u WinFuncPtr*/ %d,\n", 0); printf("/*Bit16u BytesPerScanLine*/ %d,\n", pitch); // Mandatory information for VBE 1.2 and above printf("/*Bit16u XResolution*/ %d,\n", pm->width); printf("/*Bit16u YResolution*/ %d,\n", pm->height); printf("/*Bit8u XCharSize*/ %d,\n", 8); printf("/*Bit8u YCharSize*/ %d,\n", 16); if (pm->depth == 4) { printf("/*Bit8u NumberOfPlanes*/ %d,\n", 4); } else { printf("/*Bit8u NumberOfPlanes*/ %d,\n", 1); } printf("/*Bit8u BitsPerPixel*/ %d,\n", pm->depth); printf("/*Bit8u NumberOfBanks*/ %d,\n", (pm->height * pitch + 65535) / 65536); if (pm->depth == 4) str = "VBE_MEMORYMODEL_PLANAR"; else if (pm->depth == 8) str = "VBE_MEMORYMODEL_PACKED_PIXEL"; else str = "VBE_MEMORYMODEL_DIRECT_COLOR"; printf("/*Bit8u MemoryModel*/ %s,\n", str); printf("/*Bit8u BankSize*/ %d,\n", 0); if (pm->depth == 4) printf("/*Bit8u NumberOfImagePages*/ %d,\n", (pages / 4) - 1); else printf("/*Bit8u NumberOfImagePages*/ %d,\n", pages - 1); printf("/*Bit8u Reserved_page*/ %d,\n", 0); // Direct Color fields (required for direct/6 and YUV/7 memory models) switch(pm->depth) { case 15: r_size = 5; r_pos = 10; g_size = 5; g_pos = 5; b_size = 5; b_pos = 0; a_size = 1; a_pos = 15; break; case 16: r_size = 5; r_pos = 11; g_size = 6; g_pos = 5; b_size = 5; b_pos = 0; a_size = 0; a_pos = 0; break; case 24: r_size = 8; r_pos = 16; g_size = 8; g_pos = 8; b_size = 8; b_pos = 0; a_size = 0; a_pos = 0; break; case 32: r_size = 8; r_pos = 16; g_size = 8; g_pos = 8; b_size = 8; b_pos = 0; a_size = 8; a_pos = 24; break; default: r_size = 0; r_pos = 0; g_size = 0; g_pos = 0; b_size = 0; b_pos = 0; a_size = 0; a_pos = 0; break; } printf("/*Bit8u RedMaskSize*/ %d,\n", r_size); printf("/*Bit8u RedFieldPosition*/ %d,\n", r_pos); printf("/*Bit8u GreenMaskSize*/ %d,\n", g_size); printf("/*Bit8u GreenFieldPosition*/ %d,\n", g_pos); printf("/*Bit8u BlueMaskSize*/ %d,\n", b_size); printf("/*Bit8u BlueFieldPosition*/ %d,\n", b_pos); printf("/*Bit8u RsvdMaskSize*/ %d,\n", a_size); printf("/*Bit8u RsvdFieldPosition*/ %d,\n", a_pos); if (pm->depth == 32) printf("/*Bit8u DirectColorModeInfo*/ %s,\n", "VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE"); else printf("/*Bit8u DirectColorModeInfo*/ %s,\n", "0"); // Mandatory information for VBE 2.0 and above if (pm->depth > 4) printf("/*Bit32u PhysBasePtr*/ %s,\n", "VBE_DISPI_LFB_PHYSICAL_ADDRESS"); else printf("/*Bit32u PhysBasePtr*/ %s,\n", "0"); printf("/*Bit32u OffScreenMemOffset*/ %d,\n", 0); printf("/*Bit16u OffScreenMemSize*/ %d,\n", 0); // Mandatory information for VBE 3.0 and above printf("/*Bit16u LinBytesPerScanLine*/ %d,\n", pitch); printf("/*Bit8u BnkNumberOfPages*/ %d,\n", 0); printf("/*Bit8u LinNumberOfPages*/ %d,\n", 0); printf("/*Bit8u LinRedMaskSize*/ %d,\n", r_size); printf("/*Bit8u LinRedFieldPosition*/ %d,\n", r_pos); printf("/*Bit8u LinGreenMaskSize*/ %d,\n", g_size); printf("/*Bit8u LinGreenFieldPosition*/ %d,\n", g_pos); printf("/*Bit8u LinBlueMaskSize*/ %d,\n", b_size); printf("/*Bit8u LinBlueFieldPosition*/ %d,\n", b_pos); printf("/*Bit8u LinRsvdMaskSize*/ %d,\n", a_size); printf("/*Bit8u LinRsvdFieldPosition*/ %d,\n", a_pos); printf("/*Bit32u MaxPixelClock*/ %d,\n", 0); printf("} },\n"); } } printf("{ VBE_VESA_MODE_END_OF_LIST,\n"); printf("{ 0,\n"); printf("} },\n"); printf("};\n"); return 0; } xen-4.9.2/tools/firmware/vgabios/vbe_display_api.txt0000664000175000017500000002264513256712137020767 0ustar smbsmbVBE Display API ------------------------------------------------------------------------------------------------------------- This document is part of the Bochs/VBEBios documentation, it specifies the bochs host <-> vbebios client communication. That means, the display code implementation and the vbebios code depend very heavily on each other. As such, this documents needs be synchronised between bochs CVS and the vgabios CVS. This document does not describe how the VBEBios implements the VBE2/3 spec. This document does not describe how the Bochs display code will display gfx based upon this spec. API History ----------- 0xb0c0 supports the following VBE_DISPI_ interfaces (present in Bochs 1.4): VBE_DISPI_INDEX_ID VBE_DISPI_INDEX_XRES VBE_DISPI_INDEX_YRES VBE_DISPI_INDEX_BPP VBE_DISPI_INDEX_ENABLE VBE_DISPI_INDEX_BANK Bpp format supported is: VBE_DISPI_BPP_8 0xb0c1 supports 0xb0c0 VBE_DISPI_ interfaces, additional interfaces (present in Bochs 2.0): VBE_DISPI_INDEX_VIRT_WIDTH VBE_DISPI_INDEX_VIRT_HEIGHT VBE_DISPI_INDEX_X_OFFSET VBE_DISPI_INDEX_Y_OFFSET 0xb0c2 supports 0xb0c1 VBE_DISPI_ interfaces, interfaces updated for additional features (present in Bochs 2.1): VBE_DISPI_INDEX_BPP supports >8bpp color depth (value = bits) VBE_DISPI_INDEX_ENABLE supports new flags VBE_DISPI_NOCLEARMEM and VBE_DISPI_LFB_ENABLED VBE i/o registers changed from 0xFF80/81 to 0x01CE/CF 0xb0c3 supports 0xb0c2 VBE_DISPI_ interfaces, interfaces updated for additional features: VBE_DISPI_INDEX_ENABLE supports new flags VBE_DISPI_GETCAPS and VBE_DISPI_8BIT_DAC 0xb0c4 VBE video memory increased to 8 MB History ------- Version 0.6 2002 Nov 23 Jeroen Janssen - Added LFB support - Added Virt width, height and x,y offset Version 0.5 2002 March 08 Jeroen Janssen - Added documentation about panic behaviour / current limits of the data values. - Changed BPP API (in order to include future (A)RGB formats) - Initial version (based upon extended display text of the vbe bochs display patch) Todo ---- Version 0.6+ [random order] - Add lots of different (A)RGB formats References ---------- [VBE3] VBE 3 Specification at http://www.vesa.org/vbe3.pdf [BOCHS] Bochs Open Source IA-32 Emulator at http://bochs.sourceforge.net [VBEBIOS] VBE Bios for Bochs at http://savannah.gnu.org/projects/vgabios/ [Screenshots] Screenshots of programs using the VBE Bios at http://japj.org/projects/bochs_plex86/screenshots.html Abbreviations ------------- VBE Vesa Bios Extension DISPI (Bochs) Display Interface BPP Bits Per Pixel LFB Linear Frame Buffer #defines -------- vbetables-gen.c #define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 8 vbe.h #define VBE_DISPI_BANK_ADDRESS 0xA0000 #define VBE_DISPI_BANK_SIZE_KB 64 #define VBE_DISPI_MAX_XRES 1024 #define VBE_DISPI_MAX_YRES 768 #define VBE_DISPI_IOPORT_INDEX 0x01CE #define VBE_DISPI_IOPORT_DATA 0x01CF #define VBE_DISPI_INDEX_ID 0x0 #define VBE_DISPI_INDEX_XRES 0x1 #define VBE_DISPI_INDEX_YRES 0x2 #define VBE_DISPI_INDEX_BPP 0x3 #define VBE_DISPI_INDEX_ENABLE 0x4 #define VBE_DISPI_INDEX_BANK 0x5 #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 #define VBE_DISPI_INDEX_X_OFFSET 0x8 #define VBE_DISPI_INDEX_Y_OFFSET 0x9 #define VBE_DISPI_ID0 0xB0C0 #define VBE_DISPI_ID1 0xB0C1 #define VBE_DISPI_ID2 0xB0C2 #define VBE_DISPI_ID3 0xB0C3 #define VBE_DISPI_ID4 0xB0C4 #define VBE_DISPI_DISABLED 0x00 #define VBE_DISPI_ENABLED 0x01 #define VBE_DISPI_VBE_ENABLED 0x40 #define VBE_DISPI_NOCLEARMEM 0x80 #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 API --- The display api works by using a index (VBE_DISPI_IOPORT_INDEX) and data (VBE_DISPI_IOPORT_DATA) ioport. One writes the index of the parameter to the index port. Next, the parameter value can be read or written. [0xb0c0] * VBE_DISPI_INDEX_ID : WORD {R,W} This parameter can be used to detect the current display API (both bochs & vbebios). The bios writes VBE_DISPI_ID0 to the dataport and reads it back again. This way, the display code knows the vbebios 'ID' and the vbebios can check if the correct display code is present. As a result, a PANIC can be generated if an incompatible vbebios/display code combination is detected. This panic can be generated from the bochs display code (NOT the bios, see Notes). Example values: VBE_DISPI_ID0 * VBE_DISPI_INDEX_XRES : WORD {R,W} This parameter can be used to read/write the vbe display X resolution (in pixels). It's illegal to set the XRES when the VBE is enabled (display code should generate PANIC). If the value written exceeds VBE_DISPI_MAX_XRES, the display code needs to generate a PANIC. Example values: 320,640,800,1024 * VBE_DISPI_INDEX_YRES : WORD {R,W} This parameter can be used to read/write the vbe display Y resolution (in pixels). It's illegal to set the YRES when the VBE is enabled (display code should generate PANIC). If the value written exceeds VBE_DISPI_MAX_YRES, the display code needs to generate a PANIC. Example values: 200,400,480,600,768 * VBE_DISPI_INDEX_BPP : WORD {R,W} This parameter can be used to read/write the vbe display BPP. It's illegal to set the BPP when the VBE is enabled (display code should generate PANIC). If the value written is an incompatible BPP, the display code needs to generate a PANIC. Example values: VBE_DISPI_BPP_8 * VBE_DISPI_INDEX_ENABLE : WORD {R,W} This parameter can be used to read/write the vbe ENABLED state. If the bios writes VBE_DISPI_ENABLED then the display code will setup a hostside display mode with the current XRES, YRES and BPP settings. If the bios write VBE_DISPI_DISABLED then the display code will switch back to normal vga mode behaviour. Example values: VBE_DISPI_ENABLED, VBE_DISPI_DISABLED * VBE_DISPI_INDEX_BANK : WORD {R,W} This parameter can be used to read/write the current selected BANK (at 0xA0000). This can be used for switching banks in banked mode. [0xb0c1] * VBE_DISPI_INDEX_VIRT_WIDTH : WORD {R,W} This parameter can be used to read/write the current virtual width. Upon enabling a mode, this will be set to the current xres Setting this field during enabled mode will result in the virtual width to be changed. Value will be adjusted if current setting is not possible. * VBE_DISPI_INDEX_VIRT_HEIGHT : WORD {R} This parameter can be read in order to obtain the current virtual height. This setting will be adjusted after setting a virtual width in order to stay within limit of video memory. * VBE_DISPI_INDEX_X_OFFSET : WORD {R,W} The current X offset (in pixels!) of the visible screen part. Writing a new offset will also result in a complete screen refresh. * VBE_DISPI_INDEX_Y_OFFSET : WORD {R,W} The current Y offset (in pixels!) of the visible screen part. Writing a new offset will also result in a complete screen refresh. [0xb0c2] * VBE_DISPI_INDEX_BPP : WORD {R,W} The value written is now the number of bits per pixel. A value of 0 is treated the same as 8 for backward compatibilty. These values are supported: 8, 15, 16, 24 and 32. The value of 4 is not yet handled in the VBE code. * VBE_DISPI_INDEX_ENABLE : WORD {R,W} The new flag VBE_DISPI_NOCLEARMEM allows to preserve the VBE video memory. The new flag VBE_DISPI_LFB_ENABLED indicates the usage of the LFB. [0xb0c3] * VBE_DISPI_INDEX_ENABLE : WORD {R,W} If the new flag VBE_DISPI_GETCAPS is enabled, the xres, yres and bpp registers return the gui capabilities. The new flag VBE_DISPI_8BIT_DAC switches the DAC to 8 bit mode. [0xb0c4] * VBE_DISPI_TOTAL_VIDEO_MEMORY_MB set to 8 (moved to auto-generated vbetables.h) Displaying GFX (banked mode) -------------- What happens is that the total screen is devided in banks of 'VBE_DISPI_BANK_SIZE_KB' KiloByte in size. If you want to set a pixel you can calculate its bank by doing: offset = pixel_x + pixel_y * resolution_x; bank = offset / 64 Kb (rounded 1.9999 -> 1) bank_pixel_pos = offset - bank * 64Kb Now you can set the current bank and put the pixel at VBE_DISPI_BANK_ADDRESS + bank_pixel_pos Displaying GFX (linear frame buffer mode) -------------- NOT WRITTEN YET Notes ----- * Since the XRES/YRES/BPP may not be written when VBE is enabled, if you want to switch from one VBE mode to another, you will need to disable VBE first. * Note when the bios doesn't find a valid DISPI_ID, it can disable the VBE functions. This allows people to use the same bios for both vbe enabled and disabled bochs executables. xen-4.9.2/tools/firmware/vgabios/BUGS0000664000175000017500000000014013256712137015541 0ustar smbsmbNot all the functions have been implemented yet. Please report any bugs to xen-4.9.2/tools/firmware/vgabios/vbe.c0000664000175000017500000010644413256712137016014 0ustar smbsmb// ============================================================================================ // // Copyright (C) 2002 Jeroen Janssen // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; If not, see . // // ============================================================================================ // // This VBE is part of the VGA Bios specific to the plex86/bochs Emulated VGA card. // You can NOT drive any physical vga card with it. // // ============================================================================================ // // This VBE Bios is based on information taken from : // - VESA BIOS EXTENSION (VBE) Core Functions Standard Version 3.0 located at www.vesa.org // // ============================================================================================ // defines available // disable VESA/VBE2 check in vbe info //#define VBE2_NO_VESA_CHECK #include "vbe.h" #include "vbetables.h" // The current OEM Software Revision of this VBE Bios #define VBE_OEM_SOFTWARE_REV 0x0002; extern char vbebios_copyright; extern char vbebios_vendor_name; extern char vbebios_product_name; extern char vbebios_product_revision; ASM_START // FIXME: 'merge' these (c) etc strings with the vgabios.c strings? _vbebios_copyright: .ascii "Bochs/Plex86 VBE(C) 2003 http://savannah.nongnu.org/projects/vgabios/" .byte 0x00 _vbebios_vendor_name: .ascii "Bochs/Plex86 Developers" .byte 0x00 _vbebios_product_name: .ascii "Bochs/Plex86 VBE Adapter" .byte 0x00 _vbebios_product_revision: .ascii "$Id: vbe.c,v 1.60 2008/03/02 07:47:21 vruppert Exp $" .byte 0x00 _vbebios_info_string: .ascii "Bochs VBE Display Adapter enabled" .byte 0x0a,0x0d .byte 0x0a,0x0d .byte 0x00 _no_vbebios_info_string: .ascii "NO Bochs VBE Support available!" .byte 0x0a,0x0d .byte 0x0a,0x0d .byte 0x00 #if defined(USE_BX_INFO) || defined(DEBUG) msg_vbe_init: .ascii "VBE Bios $Id: vbe.c,v 1.60 2008/03/02 07:47:21 vruppert Exp $" .byte 0x0a,0x0d, 0x00 #endif .align 2 vesa_pm_start: dw vesa_pm_set_window - vesa_pm_start dw vesa_pm_set_display_start - vesa_pm_start dw vesa_pm_unimplemented - vesa_pm_start dw vesa_pm_io_ports_table - vesa_pm_start vesa_pm_io_ports_table: dw VBE_DISPI_IOPORT_INDEX dw VBE_DISPI_IOPORT_INDEX + 1 dw VBE_DISPI_IOPORT_DATA dw VBE_DISPI_IOPORT_DATA + 1 dw 0xffff dw 0xffff USE32 vesa_pm_set_window: cmp bx, #0x00 je vesa_pm_set_display_window1 mov ax, #0x0100 ret vesa_pm_set_display_window1: mov ax, dx push dx push ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_BANK out dx, ax pop ax mov dx, # VBE_DISPI_IOPORT_DATA out dx, ax in ax, dx pop dx cmp dx, ax jne illegal_window mov ax, #0x004f ret illegal_window: mov ax, #0x014f ret vesa_pm_set_display_start: cmp bl, #0x80 je vesa_pm_set_display_start1 cmp bl, #0x00 je vesa_pm_set_display_start1 mov ax, #0x0100 ret vesa_pm_set_display_start1: ; convert offset to (X, Y) coordinate ; (would be simpler to change Bochs VBE API...) push eax push ecx push edx push esi push edi shl edx, #16 and ecx, #0xffff or ecx, edx shl ecx, #2 mov eax, ecx push eax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx movzx ecx, ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_BPP out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx movzx esi, ax pop eax cmp esi, #4 jz bpp4_mode add esi, #7 shr esi, #3 imul ecx, esi xor edx, edx div ecx mov edi, eax mov eax, edx xor edx, edx div esi jmp set_xy_regs bpp4_mode: shr ecx, #1 xor edx, edx div ecx mov edi, eax mov eax, edx shl eax, #1 set_xy_regs: push dx push ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_X_OFFSET out dx, ax pop ax mov dx, # VBE_DISPI_IOPORT_DATA out dx, ax pop dx mov ax, di push dx push ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_Y_OFFSET out dx, ax pop ax mov dx, # VBE_DISPI_IOPORT_DATA out dx, ax pop dx pop edi pop esi pop edx pop ecx pop eax mov ax, #0x004f ret vesa_pm_unimplemented: mov ax, #0x014f ret USE16 vesa_pm_end: ; DISPI ioport functions dispi_get_id: push dx mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_ID out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx pop dx ret dispi_set_id: push dx push ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_ID out dx, ax pop ax mov dx, # VBE_DISPI_IOPORT_DATA out dx, ax pop dx ret ASM_END static void dispi_set_xres(xres) Bit16u xres; { ASM_START push bp mov bp, sp push ax push dx mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_XRES out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA mov ax, 4[bp] ; xres out dx, ax pop dx pop ax pop bp ASM_END } static void dispi_set_yres(yres) Bit16u yres; { outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_YRES); outw(VBE_DISPI_IOPORT_DATA,yres); } static void dispi_set_bpp(bpp) Bit16u bpp; { outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_BPP); outw(VBE_DISPI_IOPORT_DATA,bpp); } ASM_START ; AL = bits per pixel / AH = bytes per pixel dispi_get_bpp: push dx mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_BPP out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx mov ah, al shr ah, 3 test al, #0x07 jz get_bpp_noinc inc ah get_bpp_noinc: pop dx ret ; get display capabilities _dispi_get_max_xres: push dx push bx call dispi_get_enable mov bx, ax or ax, # VBE_DISPI_GETCAPS call _dispi_set_enable mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_XRES out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx push ax mov ax, bx call _dispi_set_enable pop ax pop bx pop dx ret _dispi_get_max_bpp: push dx push bx call dispi_get_enable mov bx, ax or ax, # VBE_DISPI_GETCAPS call _dispi_set_enable mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_BPP out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx push ax mov ax, bx call _dispi_set_enable pop ax pop bx pop dx ret _dispi_set_enable: push dx push ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_ENABLE out dx, ax pop ax mov dx, # VBE_DISPI_IOPORT_DATA out dx, ax pop dx ret dispi_get_enable: push dx mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_ENABLE out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx pop dx ret _dispi_set_bank: push dx push ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_BANK out dx, ax pop ax mov dx, # VBE_DISPI_IOPORT_DATA out dx, ax pop dx ret dispi_get_bank: push dx mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_BANK out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx pop dx ret ASM_END static void dispi_set_bank_farcall() { ASM_START cmp bx,#0x0100 je dispi_set_bank_farcall_get or bx,bx jnz dispi_set_bank_farcall_error mov ax,dx push dx push ax mov ax,# VBE_DISPI_INDEX_BANK mov dx,# VBE_DISPI_IOPORT_INDEX out dx,ax pop ax mov dx,# VBE_DISPI_IOPORT_DATA out dx,ax in ax,dx pop dx cmp dx,ax jne dispi_set_bank_farcall_error mov ax, #0x004f retf dispi_set_bank_farcall_get: mov ax,# VBE_DISPI_INDEX_BANK mov dx,# VBE_DISPI_IOPORT_INDEX out dx,ax mov dx,# VBE_DISPI_IOPORT_DATA in ax,dx mov dx,ax retf dispi_set_bank_farcall_error: mov ax,#0x014F retf ASM_END } ASM_START dispi_set_x_offset: push dx push ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_X_OFFSET out dx, ax pop ax mov dx, # VBE_DISPI_IOPORT_DATA out dx, ax pop dx ret dispi_get_x_offset: push dx mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_X_OFFSET out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx pop dx ret dispi_set_y_offset: push dx push ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_Y_OFFSET out dx, ax pop ax mov dx, # VBE_DISPI_IOPORT_DATA out dx, ax pop dx ret dispi_get_y_offset: push dx mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_Y_OFFSET out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx pop dx ret vga_set_virt_width: push ax push bx push dx mov bx, ax call dispi_get_bpp cmp al, #0x04 ja set_width_svga shr bx, #1 set_width_svga: shr bx, #3 mov dx, # VGAREG_VGA_CRTC_ADDRESS mov ah, bl mov al, #0x13 out dx, ax pop dx pop bx pop ax ret dispi_set_virt_width: call vga_set_virt_width push dx push ax mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH out dx, ax pop ax mov dx, # VBE_DISPI_IOPORT_DATA out dx, ax pop dx ret dispi_get_virt_width: push dx mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx pop dx ret dispi_get_virt_height: push dx mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_VIRT_HEIGHT out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx pop dx ret _vga_compat_setup: push ax push dx ; set CRT X resolution mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_XRES out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx push ax mov dx, # VGAREG_VGA_CRTC_ADDRESS mov ax, #0x0011 out dx, ax pop ax push ax shr ax, #3 dec ax mov ah, al mov al, #0x01 out dx, ax pop ax call vga_set_virt_width ; set CRT Y resolution mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_YRES out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx dec ax push ax mov dx, # VGAREG_VGA_CRTC_ADDRESS mov ah, al mov al, #0x12 out dx, ax pop ax mov al, #0x07 out dx, al inc dx in al, dx and al, #0xbd test ah, #0x01 jz bit8_clear or al, #0x02 bit8_clear: test ah, #0x02 jz bit9_clear or al, #0x40 bit9_clear: out dx, al ; other settings mov dx, # VGAREG_VGA_CRTC_ADDRESS mov ax, #0x0009 out dx, ax mov al, #0x17 out dx, al mov dx, # VGAREG_VGA_CRTC_DATA in al, dx or al, #0x03 out dx, al mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x10 out dx, al mov dx, # VGAREG_ACTL_READ_DATA in al, dx or al, #0x01 mov dx, # VGAREG_ACTL_ADDRESS out dx, al mov al, #0x20 out dx, al mov dx, # VGAREG_GRDC_ADDRESS mov ax, #0x0506 out dx, ax mov dx, # VGAREG_SEQU_ADDRESS mov ax, #0x0f02 out dx, ax ; settings for >= 8bpp mov dx, # VBE_DISPI_IOPORT_INDEX mov ax, # VBE_DISPI_INDEX_BPP out dx, ax mov dx, # VBE_DISPI_IOPORT_DATA in ax, dx cmp al, #0x08 jb vga_compat_end mov dx, # VGAREG_VGA_CRTC_ADDRESS mov al, #0x14 out dx, al mov dx, # VGAREG_VGA_CRTC_DATA in al, dx or al, #0x40 out dx, al mov dx, # VGAREG_ACTL_RESET in al, dx mov dx, # VGAREG_ACTL_ADDRESS mov al, #0x10 out dx, al mov dx, # VGAREG_ACTL_READ_DATA in al, dx or al, #0x40 mov dx, # VGAREG_ACTL_ADDRESS out dx, al mov al, #0x20 out dx, al mov dx, # VGAREG_SEQU_ADDRESS mov al, #0x04 out dx, al mov dx, # VGAREG_SEQU_DATA in al, dx or al, #0x08 out dx, al mov dx, # VGAREG_GRDC_ADDRESS mov al, #0x05 out dx, al mov dx, # VGAREG_GRDC_DATA in al, dx and al, #0x9f or al, #0x40 out dx, al vga_compat_end: pop dx pop ax ASM_END // ModeInfo helper function static ModeInfoListItem* mode_info_find_mode(mode, using_lfb) Bit16u mode; Boolean using_lfb; { ModeInfoListItem *cur_info=&mode_info_list; while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST) { if (cur_info->mode == mode) { if (!using_lfb) { return cur_info; } else if (cur_info->info.ModeAttributes & VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE) { return cur_info; } else { cur_info++; } } else { cur_info++; } } return 0; } ASM_START ; Has VBE display - Returns true if VBE display detected _vbe_has_vbe_display: push ds push bx mov ax, # BIOSMEM_SEG mov ds, ax mov bx, # BIOSMEM_VBE_FLAG mov al, [bx] and al, #0x01 xor ah, ah pop bx pop ds ret ; VBE Init - Initialise the Vesa Bios Extension Code ; This function does a sanity check on the host side display code interface. vbe_init: mov ax, # VBE_DISPI_ID0 call dispi_set_id call dispi_get_id cmp ax, # VBE_DISPI_ID0 jne no_vbe_interface push ds push bx mov ax, # BIOSMEM_SEG mov ds, ax mov bx, # BIOSMEM_VBE_FLAG mov al, #0x01 mov [bx], al pop bx pop ds mov ax, # VBE_DISPI_ID4 call dispi_set_id no_vbe_interface: #if defined(USE_BX_INFO) || defined(DEBUG) mov bx, #msg_vbe_init push bx call _printf inc sp inc sp #endif ret ; VBE Display Info - Display information on screen about the VBE vbe_display_info: call _vbe_has_vbe_display test ax, ax jz no_vbe_flag mov ax, #0xc000 mov ds, ax mov si, #_vbebios_info_string jmp _display_string no_vbe_flag: mov ax, #0xc000 mov ds, ax mov si, #_no_vbebios_info_string jmp _display_string ASM_END ASM_START _size64: push bp mov bp, sp push dx ; multiply bbp by yres first as results fit in 16bits ; then multiply by xres mov ax, 8[bp] mul word 6[bp] mul word 4[bp] ; divide by 2^19 ceiling result add ax, #0xffff adc dx, #7 mov ax, dx shr ax, #3 pop dx pop bp ret ASM_END /** Function 00h - Return VBE Controller Information * * Input: * AX = 4F00h * ES:DI = Pointer to buffer in which to place VbeInfoBlock structure * (VbeSignature should be VBE2 when VBE 2.0 information is desired and * the info block is 512 bytes in size) * Output: * AX = VBE Return Status * */ void vbe_biosfn_return_controller_information(AX, ES, DI) Bit16u *AX;Bit16u ES;Bit16u DI; { Bit16u ss=get_SS(); VbeInfoBlock vbe_info_block; Bit16u status; Bit16u result; Bit16u vbe2_info; Bit16u cur_mode=0; Bit16u cur_ptr=34; ModeInfoListItem *cur_info=&mode_info_list; status = read_word(ss, AX); #ifdef DEBUG printf("VBE vbe_biosfn_return_vbe_info ES%x DI%x AX%x\n",ES,DI,status); #endif vbe2_info = 0; #ifdef VBE2_NO_VESA_CHECK #else // get vbe_info_block into local variable memcpyb(ss, &vbe_info_block, ES, DI, sizeof(vbe_info_block)); // check for VBE2 signature if (((vbe_info_block.VbeSignature[0] == 'V') && (vbe_info_block.VbeSignature[1] == 'B') && (vbe_info_block.VbeSignature[2] == 'E') && (vbe_info_block.VbeSignature[3] == '2')) || ((vbe_info_block.VbeSignature[0] == 'V') && (vbe_info_block.VbeSignature[1] == 'E') && (vbe_info_block.VbeSignature[2] == 'S') && (vbe_info_block.VbeSignature[3] == 'A')) ) { vbe2_info = 1; #ifdef DEBUG printf("VBE correct VESA/VBE2 signature found\n"); #endif } #endif // VBE Signature vbe_info_block.VbeSignature[0] = 'V'; vbe_info_block.VbeSignature[1] = 'E'; vbe_info_block.VbeSignature[2] = 'S'; vbe_info_block.VbeSignature[3] = 'A'; // VBE Version supported vbe_info_block.VbeVersion = 0x0200; // OEM String vbe_info_block.OemStringPtr_Seg = 0xc000; vbe_info_block.OemStringPtr_Off = &vbebios_copyright; // Capabilities vbe_info_block.Capabilities[0] = VBE_CAPABILITY_8BIT_DAC; vbe_info_block.Capabilities[1] = 0; vbe_info_block.Capabilities[2] = 0; vbe_info_block.Capabilities[3] = 0; // VBE Video Mode Pointer (dynamicly generated from the mode_info_list) vbe_info_block.VideoModePtr_Seg= ES ; vbe_info_block.VideoModePtr_Off= DI + 34; // VBE Total Memory (in 64b blocks) outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIDEO_MEMORY_64K); vbe_info_block.TotalMemory = inw(VBE_DISPI_IOPORT_DATA); if (vbe2_info) { // OEM Stuff vbe_info_block.OemSoftwareRev = VBE_OEM_SOFTWARE_REV; vbe_info_block.OemVendorNamePtr_Seg = 0xc000; vbe_info_block.OemVendorNamePtr_Off = &vbebios_vendor_name; vbe_info_block.OemProductNamePtr_Seg = 0xc000; vbe_info_block.OemProductNamePtr_Off = &vbebios_product_name; vbe_info_block.OemProductRevPtr_Seg = 0xc000; vbe_info_block.OemProductRevPtr_Off = &vbebios_product_revision; // copy updates in vbe_info_block back memcpyb(ES, DI, ss, &vbe_info_block, sizeof(vbe_info_block)); } else { // copy updates in vbe_info_block back (VBE 1.x compatibility) memcpyb(ES, DI, ss, &vbe_info_block, 256); } do { Bit16u size_64k = size64(cur_info->info.XResolution, cur_info->info.YResolution, cur_info->info.BitsPerPixel); Bit16u max_bpp = dispi_get_max_bpp(); if ((cur_info->info.XResolution <= dispi_get_max_xres()) && (cur_info->info.BitsPerPixel <= max_bpp) && (size_64k <= vbe_info_block.TotalMemory)) { #ifdef DEBUG printf("VBE found mode %x => %x\n", cur_info->mode,cur_mode); #endif write_word(ES, DI + cur_ptr, cur_info->mode); cur_mode++; cur_ptr+=2; } else { #ifdef DEBUG printf("VBE mode %x (xres=%x / bpp=%02x) not supported \n", cur_info->mode,cur_info->info.XResolution,cur_info->info.BitsPerPixel); #endif } cur_info++; } while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST); // Add vesa mode list terminator write_word(ES, DI + cur_ptr, cur_info->mode); result = 0x4f; write_word(ss, AX, result); } /** Function 01h - Return VBE Mode Information * * Input: * AX = 4F01h * CX = Mode Number * ES:DI = Pointer to buffer in which to place ModeInfoBlock structure * Output: * AX = VBE Return Status * */ void vbe_biosfn_return_mode_information(AX, CX, ES, DI) Bit16u *AX;Bit16u CX; Bit16u ES;Bit16u DI; { // error by default is 0x014f which means supported but error Bit16u result=0x014f; Bit16u ss=get_SS(); ModeInfoListItem *cur_info; Boolean using_lfb; ModeInfoBlockCompact info; #ifdef DEBUG printf("VBE vbe_biosfn_return_mode_information ES%x DI%x CX%x\n",ES,DI,CX); #endif using_lfb=((CX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER); CX = (CX & 0x1ff); cur_info = mode_info_find_mode(CX, using_lfb, &cur_info); if (cur_info != 0) { Bit16u max_bpp = dispi_get_max_bpp(); Bit16u size_64k; Bit16u totalMemory; outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIDEO_MEMORY_64K); totalMemory = inw(VBE_DISPI_IOPORT_DATA); #ifdef DEBUG printf("VBE found mode %x\n",CX); #endif memcpyb(ss, &info, 0xc000, &(cur_info->info), sizeof(ModeInfoBlockCompact)); size_64k = size64(info.XResolution, info.YResolution, info.BitsPerPixel); if ((info.XResolution > dispi_get_max_xres()) || (info.BitsPerPixel > max_bpp) || (size_64k > totalMemory)) info.ModeAttributes &= ~VBE_MODE_ATTRIBUTE_SUPPORTED; /* Windows 8 require this to be 1! */ info.NumberOfBanks = 1; if (info.WinAAttributes & VBE_WINDOW_ATTRIBUTE_RELOCATABLE) { info.WinFuncPtr = 0xC0000000UL; *(Bit16u *)&(info.WinFuncPtr) = (Bit16u)(dispi_set_bank_farcall); } outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_LFB_ADDRESS_H); info.PhysBasePtr = inw(VBE_DISPI_IOPORT_DATA); info.PhysBasePtr = info.PhysBasePtr << 16; #if 0 outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_LFB_ADDRESS_L); info.PhysBasePtr |= inw(VBE_DISPI_IOPORT_DATA); #endif result = 0x4f; // copy updates in mode_info_block back memsetb(ES, DI, 0, sizeof(ModeInfoBlock)); memcpyb(ES, DI, ss, &info, sizeof(info)); } else { #ifdef DEBUG printf("VBE *NOT* found mode %x\n",CX); #endif } write_word(ss, AX, result); } /** Function 02h - Set VBE Mode * * Input: * AX = 4F02h * BX = Desired Mode to set * ES:DI = Pointer to CRTCInfoBlock structure * Output: * AX = VBE Return Status * */ void vbe_biosfn_set_mode(AX, BX, ES, DI) Bit16u *AX;Bit16u BX; Bit16u ES;Bit16u DI; { Bit16u ss = get_SS(); Bit16u result; ModeInfoListItem *cur_info; Boolean using_lfb; Bit8u no_clear; Bit8u lfb_flag; using_lfb=((BX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER); lfb_flag=using_lfb?VBE_DISPI_LFB_ENABLED:0; no_clear=((BX & VBE_MODE_PRESERVE_DISPLAY_MEMORY) == VBE_MODE_PRESERVE_DISPLAY_MEMORY)?VBE_DISPI_NOCLEARMEM:0; BX = (BX & 0x1ff); //result=read_word(ss,AX); // check for non vesa mode if (BXinfo.XResolution, cur_info->info.YResolution, cur_info->info.BitsPerPixel); #endif // first disable current mode (when switching between vesa modi) dispi_set_enable(VBE_DISPI_DISABLED); if (cur_info->info.BitsPerPixel == 4) { biosfn_set_video_mode(0x6a); } dispi_set_bpp(cur_info->info.BitsPerPixel); dispi_set_xres(cur_info->info.XResolution); dispi_set_yres(cur_info->info.YResolution); dispi_set_bank(0); dispi_set_enable(VBE_DISPI_ENABLED | no_clear | lfb_flag); vga_compat_setup(); write_word(BIOSMEM_SEG,BIOSMEM_VBE_MODE,BX); write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60 | no_clear)); result = 0x4f; } else { #ifdef DEBUG printf("VBE *NOT* found mode %x\n" , BX); #endif result = 0x100; // FIXME: redirect non VBE modi to normal VGA bios operation // (switch back to VGA mode if (BX == 3) result = 0x4f; } write_word(ss, AX, result); } /** Function 03h - Return Current VBE Mode * * Input: * AX = 4F03h * Output: * AX = VBE Return Status * BX = Current VBE Mode * */ ASM_START vbe_biosfn_return_current_mode: push ds mov ax, # BIOSMEM_SEG mov ds, ax call dispi_get_enable and ax, # VBE_DISPI_ENABLED jz no_vbe_mode mov bx, # BIOSMEM_VBE_MODE mov ax, [bx] mov bx, ax jnz vbe_03_ok no_vbe_mode: mov bx, # BIOSMEM_CURRENT_MODE mov al, [bx] mov bl, al xor bh, bh vbe_03_ok: mov ax, #0x004f pop ds ret ASM_END Bit16u vbe_biosfn_read_video_state_size() { return 9 * 2; } void vbe_biosfn_save_video_state(ES, BX) Bit16u ES; Bit16u BX; { Bit16u enable, i; outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE); enable = inw(VBE_DISPI_IOPORT_DATA); write_word(ES, BX, enable); BX += 2; if (!(enable & VBE_DISPI_ENABLED)) return; for(i = VBE_DISPI_INDEX_XRES; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) { if (i != VBE_DISPI_INDEX_ENABLE) { outw(VBE_DISPI_IOPORT_INDEX, i); write_word(ES, BX, inw(VBE_DISPI_IOPORT_DATA)); BX += 2; } } } void vbe_biosfn_restore_video_state(ES, BX) Bit16u ES; Bit16u BX; { Bit16u enable, i; enable = read_word(ES, BX); BX += 2; if (!(enable & VBE_DISPI_ENABLED)) { outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE); outw(VBE_DISPI_IOPORT_DATA, enable); } else { outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES); outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX)); BX += 2; outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES); outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX)); BX += 2; outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP); outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX)); BX += 2; outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE); outw(VBE_DISPI_IOPORT_DATA, enable); for(i = VBE_DISPI_INDEX_BANK; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) { outw(VBE_DISPI_IOPORT_INDEX, i); outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX)); BX += 2; } } } /** Function 04h - Save/Restore State * * Input: * AX = 4F04h * DL = 00h Return Save/Restore State buffer size * 01h Save State * 02h Restore State * CX = Requested states * ES:BX = Pointer to buffer (if DL <> 00h) * Output: * AX = VBE Return Status * BX = Number of 64-byte blocks to hold the state buffer (if DL=00h) * */ void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX) Bit16u *AX; Bit16u CX; Bit16u DX; Bit16u ES; Bit16u *BX; { Bit16u ss=get_SS(); Bit16u result, val; result = 0x4f; switch(GET_DL()) { case 0x00: val = biosfn_read_video_state_size2(CX); #ifdef DEBUG printf("VGA state size=%x\n", val); #endif if (CX & 8) val += vbe_biosfn_read_video_state_size(); write_word(ss, BX, val); break; case 0x01: val = read_word(ss, BX); val = biosfn_save_video_state(CX, ES, val); #ifdef DEBUG printf("VGA save_state offset=%x\n", val); #endif if (CX & 8) vbe_biosfn_save_video_state(ES, val); break; case 0x02: val = read_word(ss, BX); val = biosfn_restore_video_state(CX, ES, val); #ifdef DEBUG printf("VGA restore_state offset=%x\n", val); #endif if (CX & 8) vbe_biosfn_restore_video_state(ES, val); break; default: // function failed result = 0x100; break; } write_word(ss, AX, result); } /** Function 05h - Display Window Control * * Input: * AX = 4F05h * (16-bit) BH = 00h Set memory window * = 01h Get memory window * BL = Window number * = 00h Window A * = 01h Window B * DX = Window number in video memory in window * granularity units (Set Memory Window only) * Note: * If this function is called while in a linear frame buffer mode, * this function must fail with completion code AH=03h * * Output: * AX = VBE Return Status * DX = Window number in window granularity units * (Get Memory Window only) */ ASM_START vbe_biosfn_display_window_control: cmp bl, #0x00 jne vbe_05_failed cmp bh, #0x01 je get_display_window jb set_display_window mov ax, #0x0100 ret set_display_window: mov ax, dx call _dispi_set_bank call dispi_get_bank cmp ax, dx jne vbe_05_failed mov ax, #0x004f ret get_display_window: call dispi_get_bank mov dx, ax mov ax, #0x004f ret vbe_05_failed: mov ax, #0x014f ret ASM_END /** Function 06h - Set/Get Logical Scan Line Length * * Input: * AX = 4F06h * BL = 00h Set Scan Line Length in Pixels * = 01h Get Scan Line Length * = 02h Set Scan Line Length in Bytes * = 03h Get Maximum Scan Line Length * CX = If BL=00h Desired Width in Pixels * If BL=02h Desired Width in Bytes * (Ignored for Get Functions) * * Output: * AX = VBE Return Status * BX = Bytes Per Scan Line * CX = Actual Pixels Per Scan Line * (truncated to nearest complete pixel) * DX = Maximum Number of Scan Lines */ ASM_START vbe_biosfn_set_get_logical_scan_line_length: mov ax, cx cmp bl, #0x01 je get_logical_scan_line_length cmp bl, #0x02 je set_logical_scan_line_bytes jb set_logical_scan_line_pixels mov ax, #0x0100 ret set_logical_scan_line_bytes: push ax call dispi_get_bpp xor bh, bh mov bl, ah or bl, bl jnz no_4bpp_1 shl ax, #3 mov bl, #1 no_4bpp_1: xor dx, dx pop ax div bx set_logical_scan_line_pixels: call dispi_set_virt_width get_logical_scan_line_length: call dispi_get_bpp xor bh, bh mov bl, ah call dispi_get_virt_width mov cx, ax or bl, bl jnz no_4bpp_2 shr ax, #3 mov bl, #1 no_4bpp_2: mul bx mov bx, ax call dispi_get_virt_height mov dx, ax mov ax, #0x004f ret ASM_END /** Function 07h - Set/Get Display Start * * Input(16-bit): * AX = 4F07h * BH = 00h Reserved and must be 00h * BL = 00h Set Display Start * = 01h Get Display Start * = 02h Schedule Display Start (Alternate) * = 03h Schedule Stereoscopic Display Start * = 04h Get Scheduled Display Start Status * = 05h Enable Stereoscopic Mode * = 06h Disable Stereoscopic Mode * = 80h Set Display Start during Vertical Retrace * = 82h Set Display Start during Vertical Retrace (Alternate) * = 83h Set Stereoscopic Display Start during Vertical Retrace * ECX = If BL=02h/82h Display Start Address in bytes * If BL=03h/83h Left Image Start Address in bytes * EDX = If BL=03h/83h Right Image Start Address in bytes * CX = If BL=00h/80h First Displayed Pixel In Scan Line * DX = If BL=00h/80h First Displayed Scan Line * * Output: * AX = VBE Return Status * BH = If BL=01h Reserved and will be 0 * CX = If BL=01h First Displayed Pixel In Scan Line * If BL=04h 0 if flip has not occurred, not 0 if it has * DX = If BL=01h First Displayed Scan Line * * Input(32-bit): * BH = 00h Reserved and must be 00h * BL = 00h Set Display Start * = 80h Set Display Start during Vertical Retrace * CX = Bits 0-15 of display start address * DX = Bits 16-31 of display start address * ES = Selector for memory mapped registers */ ASM_START vbe_biosfn_set_get_display_start: cmp bl, #0x80 je set_display_start cmp bl, #0x01 je get_display_start jb set_display_start mov ax, #0x0100 ret set_display_start: mov ax, cx call dispi_set_x_offset mov ax, dx call dispi_set_y_offset mov ax, #0x004f ret get_display_start: call dispi_get_x_offset mov cx, ax call dispi_get_y_offset mov dx, ax xor bh, bh mov ax, #0x004f ret ASM_END /** Function 08h - Set/Get Dac Palette Format * * Input: * AX = 4F08h * BL = 00h set DAC palette width * = 01h get DAC palette width * BH = If BL=00h: desired number of bits per primary color * Output: * AX = VBE Return Status * BH = current number of bits per primary color (06h = standard VGA) */ ASM_START vbe_biosfn_set_get_dac_palette_format: cmp bl, #0x01 je get_dac_palette_format jb set_dac_palette_format mov ax, #0x0100 ret set_dac_palette_format: call dispi_get_enable cmp bh, #0x06 je set_normal_dac cmp bh, #0x08 jne vbe_08_unsupported or ax, # VBE_DISPI_8BIT_DAC jnz set_dac_mode set_normal_dac: and ax, #~ VBE_DISPI_8BIT_DAC set_dac_mode: call _dispi_set_enable get_dac_palette_format: mov bh, #0x06 call dispi_get_enable and ax, # VBE_DISPI_8BIT_DAC jz vbe_08_ok mov bh, #0x08 vbe_08_ok: mov ax, #0x004f ret vbe_08_unsupported: mov ax, #0x014f ret ASM_END /** Function 09h - Set/Get Palette Data * * Input: * AX = 4F09h * Output: * AX = VBE Return Status * * FIXME: incomplete API description, Input & Output */ void vbe_biosfn_set_get_palette_data(AX) { } /** Function 0Ah - Return VBE Protected Mode Interface * Input: AX = 4F0Ah VBE 2.0 Protected Mode Interface * BL = 00h Return protected mode table * * * Output: AX = Status * ES = Real Mode Segment of Table * DI = Offset of Table * CX = Length of Table including protected mode code * (for copying purposes) */ ASM_START vbe_biosfn_return_protected_mode_interface: test bl, bl jnz _fail mov di, #0xc000 mov es, di mov di, # vesa_pm_start mov cx, # vesa_pm_end sub cx, di mov ax, #0x004f ret _fail: mov ax, #0x014f ret ASM_END xen-4.9.2/tools/firmware/vgabios/vgatables.h0000664000175000017500000006430013256712137017207 0ustar smbsmb/* * * BIOS Memory * */ #define BIOSMEM_SEG 0x40 #define BIOSMEM_INITIAL_MODE 0x10 #define BIOSMEM_CURRENT_MODE 0x49 #define BIOSMEM_NB_COLS 0x4A #define BIOSMEM_PAGE_SIZE 0x4C #define BIOSMEM_CURRENT_START 0x4E #define BIOSMEM_CURSOR_POS 0x50 #define BIOSMEM_CURSOR_TYPE 0x60 #define BIOSMEM_CURRENT_PAGE 0x62 #define BIOSMEM_CRTC_ADDRESS 0x63 #define BIOSMEM_CURRENT_MSR 0x65 #define BIOSMEM_CURRENT_PAL 0x66 #define BIOSMEM_NB_ROWS 0x84 #define BIOSMEM_CHAR_HEIGHT 0x85 #define BIOSMEM_VIDEO_CTL 0x87 #define BIOSMEM_SWITCHES 0x88 #define BIOSMEM_MODESET_CTL 0x89 #define BIOSMEM_DCC_INDEX 0x8A #define BIOSMEM_VS_POINTER 0xA8 #define BIOSMEM_VBE_FLAG 0xB9 #define BIOSMEM_VBE_MODE 0xBA #define BIOSMEM_VBE_POWER 0xBC /* * * VGA registers * */ #define VGAREG_ACTL_ADDRESS 0x3c0 #define VGAREG_ACTL_WRITE_DATA 0x3c0 #define VGAREG_ACTL_READ_DATA 0x3c1 #define VGAREG_INPUT_STATUS 0x3c2 #define VGAREG_WRITE_MISC_OUTPUT 0x3c2 #define VGAREG_VIDEO_ENABLE 0x3c3 #define VGAREG_SEQU_ADDRESS 0x3c4 #define VGAREG_SEQU_DATA 0x3c5 #define VGAREG_PEL_MASK 0x3c6 #define VGAREG_DAC_STATE 0x3c7 #define VGAREG_DAC_READ_ADDRESS 0x3c7 #define VGAREG_DAC_WRITE_ADDRESS 0x3c8 #define VGAREG_DAC_DATA 0x3c9 #define VGAREG_READ_FEATURE_CTL 0x3ca #define VGAREG_READ_MISC_OUTPUT 0x3cc #define VGAREG_GRDC_ADDRESS 0x3ce #define VGAREG_GRDC_DATA 0x3cf #define VGAREG_MDA_CRTC_ADDRESS 0x3b4 #define VGAREG_MDA_CRTC_DATA 0x3b5 #define VGAREG_VGA_CRTC_ADDRESS 0x3d4 #define VGAREG_VGA_CRTC_DATA 0x3d5 #define VGAREG_MDA_WRITE_FEATURE_CTL 0x3ba #define VGAREG_VGA_WRITE_FEATURE_CTL 0x3da #define VGAREG_ACTL_RESET 0x3da #define VGAREG_MDA_MODECTL 0x3b8 #define VGAREG_CGA_MODECTL 0x3d8 #define VGAREG_CGA_PALETTE 0x3d9 /* Video memory */ #define VGAMEM_GRAPH 0xA000 #define VGAMEM_CTEXT 0xB800 #define VGAMEM_MTEXT 0xB000 /* * * Tables of default values for each mode * */ #define MODE_MAX 15 #define TEXT 0x00 #define GRAPH 0x01 #define CTEXT 0x00 #define MTEXT 0x01 #define CGA 0x02 #define PLANAR1 0x03 #define PLANAR4 0x04 #define LINEAR8 0x05 // for SVGA #define LINEAR15 0x10 #define LINEAR16 0x11 #define LINEAR24 0x12 #define LINEAR32 0x13 typedef struct {Bit8u svgamode; Bit8u class; /* TEXT, GRAPH */ Bit8u memmodel; /* CTEXT,MTEXT,CGA,PL1,PL2,PL4,P8,P15,P16,P24,P32 */ Bit8u pixbits; Bit16u sstart; Bit8u pelmask; Bit8u dacmodel; /* 0 1 2 3 */ } VGAMODES; static VGAMODES vga_modes[MODE_MAX+1]= {//mode class model bits sstart pelm dac {0x00, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, {0x01, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, {0x02, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, {0x03, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, {0x04, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01}, {0x05, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01}, {0x06, GRAPH, CGA, 1, 0xB800, 0xFF, 0x01}, {0x07, TEXT, MTEXT, 4, 0xB000, 0xFF, 0x00}, {0x0D, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01}, {0x0E, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01}, {0x0F, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x00}, {0x10, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02}, {0x11, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x02}, {0x12, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02}, {0x13, GRAPH, LINEAR8, 8, 0xA000, 0xFF, 0x03}, {0x6A, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02} }; /* convert index in vga_modes[] to index in video_param_table[] */ static Bit8u line_to_vpti[MODE_MAX+1]={ 0x17, 0x17, 0x18, 0x18, 0x04, 0x05, 0x06, 0x07, 0x0d, 0x0e, 0x11, 0x12, 0x1a, 0x1b, 0x1c, 0x1d, }; /* Default Palette */ #define DAC_MAX_MODEL 3 static Bit8u dac_regs[DAC_MAX_MODEL+1]= {0x3f,0x3f,0x3f,0xff}; /* standard BIOS Video Parameter Table */ typedef struct { Bit8u twidth; Bit8u theightm1; Bit8u cheight; Bit8u slength_l; Bit8u slength_h; Bit8u sequ_regs[4]; Bit8u miscreg; Bit8u crtc_regs[25]; Bit8u actl_regs[20]; Bit8u grdc_regs[9]; } VideoParamTableEntry; static VideoParamTableEntry video_param_table[30] = { { /* index=0x00 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x01 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x02 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x03 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x04 vga mode 0x04 */ 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */ 0x09, 0x03, 0x00, 0x02, /* sequ_regs */ 0x63, /* miscreg */ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, 0xff, /* crtc_regs */ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x03, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x05 vga mode 0x05 */ 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */ 0x09, 0x03, 0x00, 0x02, /* sequ_regs */ 0x63, /* miscreg */ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, 0xff, /* crtc_regs */ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x03, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x06 vga mode 0x06 */ 80, 24, 8, 0x00, 0x10, /* tw, th-1, ch, slength */ 0x01, 0x01, 0x00, 0x06, /* sequ_regs */ 0x63, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xc2, 0xff, /* crtc_regs */ 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x01, 0x00, 0x01, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x07 vga mode 0x07 */ 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */ 0x00, 0x03, 0x00, 0x02, /* sequ_regs */ 0x66, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, 0xff, /* crtc_regs */ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x08 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x09 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x0a no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x0b no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x0c no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x0d vga mode 0x0d */ 40, 24, 8, 0x00, 0x20, /* tw, th-1, ch, slength */ 0x09, 0x0f, 0x00, 0x06, /* sequ_regs */ 0x63, /* miscreg */ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xe3, 0xff, /* crtc_regs */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x0e vga mode 0x0e */ 80, 24, 8, 0x00, 0x40, /* tw, th-1, ch, slength */ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ 0x63, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xe3, 0xff, /* crtc_regs */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x0f no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x10 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x11 vga mode 0x0f */ 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ 0xa3, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, 0xff, /* crtc_regs */ 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x12 vga mode 0x10 */ 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ 0xa3, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, 0xff, /* crtc_regs */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x13 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x14 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x15 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x16 no mode defined */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, { /* index=0x17 vga mode 0x01 */ 40, 24, 16, 0x00, 0x08, /* tw, th-1, ch, slength */ 0x08, 0x03, 0x00, 0x02, /* sequ_regs */ 0x67, /* miscreg */ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x1f, 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x14, 0x1f, 0x96, 0xb9, 0xa3, 0xff, /* crtc_regs */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x18 vga mode 0x03 */ 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */ 0x00, 0x03, 0x00, 0x02, /* sequ_regs */ 0x67, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3, 0xff, /* crtc_regs */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x19 vga mode 0x07 */ 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */ 0x00, 0x03, 0x00, 0x02, /* sequ_regs */ 0x66, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, 0xff, /* crtc_regs */ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x1a vga mode 0x11 */ 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ 0xe3, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, 0xff, /* crtc_regs */ 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x1b vga mode 0x12 */ 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ 0xe3, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, 0xff, /* crtc_regs */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x1c vga mode 0x13 */ 40, 24, 8, 0x00, 0x00, /* tw, th-1, ch, slength */ 0x01, 0x0f, 0x00, 0x0e, /* sequ_regs */ 0x63, /* miscreg */ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x40, 0x96, 0xb9, 0xa3, 0xff, /* crtc_regs */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x41, 0x00, 0x0f, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff, /* grdc_regs */ }, { /* index=0x1d vga mode 0x6a */ 100, 36, 16, 0x00, 0x00, /* tw, th-1, ch, slength */ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ 0xe3, /* miscreg */ 0x7f, 0x63, 0x63, 0x83, 0x6b, 0x1b, 0x72, 0xf0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x8d, 0x57, 0x32, 0x00, 0x57, 0x73, 0xe3, 0xff, /* crtc_regs */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ }, }; /* Mono */ static Bit8u palette0[63+1][3]= { 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f }; static Bit8u palette1[63+1][3]= { 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f }; static Bit8u palette2[63+1][3]= { 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x2a,0x00, 0x2a,0x2a,0x2a, 0x00,0x00,0x15, 0x00,0x00,0x3f, 0x00,0x2a,0x15, 0x00,0x2a,0x3f, 0x2a,0x00,0x15, 0x2a,0x00,0x3f, 0x2a,0x2a,0x15, 0x2a,0x2a,0x3f, 0x00,0x15,0x00, 0x00,0x15,0x2a, 0x00,0x3f,0x00, 0x00,0x3f,0x2a, 0x2a,0x15,0x00, 0x2a,0x15,0x2a, 0x2a,0x3f,0x00, 0x2a,0x3f,0x2a, 0x00,0x15,0x15, 0x00,0x15,0x3f, 0x00,0x3f,0x15, 0x00,0x3f,0x3f, 0x2a,0x15,0x15, 0x2a,0x15,0x3f, 0x2a,0x3f,0x15, 0x2a,0x3f,0x3f, 0x15,0x00,0x00, 0x15,0x00,0x2a, 0x15,0x2a,0x00, 0x15,0x2a,0x2a, 0x3f,0x00,0x00, 0x3f,0x00,0x2a, 0x3f,0x2a,0x00, 0x3f,0x2a,0x2a, 0x15,0x00,0x15, 0x15,0x00,0x3f, 0x15,0x2a,0x15, 0x15,0x2a,0x3f, 0x3f,0x00,0x15, 0x3f,0x00,0x3f, 0x3f,0x2a,0x15, 0x3f,0x2a,0x3f, 0x15,0x15,0x00, 0x15,0x15,0x2a, 0x15,0x3f,0x00, 0x15,0x3f,0x2a, 0x3f,0x15,0x00, 0x3f,0x15,0x2a, 0x3f,0x3f,0x00, 0x3f,0x3f,0x2a, 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f }; static Bit8u palette3[256][3]= { 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, 0x00,0x00,0x00, 0x05,0x05,0x05, 0x08,0x08,0x08, 0x0b,0x0b,0x0b, 0x0e,0x0e,0x0e, 0x11,0x11,0x11, 0x14,0x14,0x14, 0x18,0x18,0x18, 0x1c,0x1c,0x1c, 0x20,0x20,0x20, 0x24,0x24,0x24, 0x28,0x28,0x28, 0x2d,0x2d,0x2d, 0x32,0x32,0x32, 0x38,0x38,0x38, 0x3f,0x3f,0x3f, 0x00,0x00,0x3f, 0x10,0x00,0x3f, 0x1f,0x00,0x3f, 0x2f,0x00,0x3f, 0x3f,0x00,0x3f, 0x3f,0x00,0x2f, 0x3f,0x00,0x1f, 0x3f,0x00,0x10, 0x3f,0x00,0x00, 0x3f,0x10,0x00, 0x3f,0x1f,0x00, 0x3f,0x2f,0x00, 0x3f,0x3f,0x00, 0x2f,0x3f,0x00, 0x1f,0x3f,0x00, 0x10,0x3f,0x00, 0x00,0x3f,0x00, 0x00,0x3f,0x10, 0x00,0x3f,0x1f, 0x00,0x3f,0x2f, 0x00,0x3f,0x3f, 0x00,0x2f,0x3f, 0x00,0x1f,0x3f, 0x00,0x10,0x3f, 0x1f,0x1f,0x3f, 0x27,0x1f,0x3f, 0x2f,0x1f,0x3f, 0x37,0x1f,0x3f, 0x3f,0x1f,0x3f, 0x3f,0x1f,0x37, 0x3f,0x1f,0x2f, 0x3f,0x1f,0x27, 0x3f,0x1f,0x1f, 0x3f,0x27,0x1f, 0x3f,0x2f,0x1f, 0x3f,0x37,0x1f, 0x3f,0x3f,0x1f, 0x37,0x3f,0x1f, 0x2f,0x3f,0x1f, 0x27,0x3f,0x1f, 0x1f,0x3f,0x1f, 0x1f,0x3f,0x27, 0x1f,0x3f,0x2f, 0x1f,0x3f,0x37, 0x1f,0x3f,0x3f, 0x1f,0x37,0x3f, 0x1f,0x2f,0x3f, 0x1f,0x27,0x3f, 0x2d,0x2d,0x3f, 0x31,0x2d,0x3f, 0x36,0x2d,0x3f, 0x3a,0x2d,0x3f, 0x3f,0x2d,0x3f, 0x3f,0x2d,0x3a, 0x3f,0x2d,0x36, 0x3f,0x2d,0x31, 0x3f,0x2d,0x2d, 0x3f,0x31,0x2d, 0x3f,0x36,0x2d, 0x3f,0x3a,0x2d, 0x3f,0x3f,0x2d, 0x3a,0x3f,0x2d, 0x36,0x3f,0x2d, 0x31,0x3f,0x2d, 0x2d,0x3f,0x2d, 0x2d,0x3f,0x31, 0x2d,0x3f,0x36, 0x2d,0x3f,0x3a, 0x2d,0x3f,0x3f, 0x2d,0x3a,0x3f, 0x2d,0x36,0x3f, 0x2d,0x31,0x3f, 0x00,0x00,0x1c, 0x07,0x00,0x1c, 0x0e,0x00,0x1c, 0x15,0x00,0x1c, 0x1c,0x00,0x1c, 0x1c,0x00,0x15, 0x1c,0x00,0x0e, 0x1c,0x00,0x07, 0x1c,0x00,0x00, 0x1c,0x07,0x00, 0x1c,0x0e,0x00, 0x1c,0x15,0x00, 0x1c,0x1c,0x00, 0x15,0x1c,0x00, 0x0e,0x1c,0x00, 0x07,0x1c,0x00, 0x00,0x1c,0x00, 0x00,0x1c,0x07, 0x00,0x1c,0x0e, 0x00,0x1c,0x15, 0x00,0x1c,0x1c, 0x00,0x15,0x1c, 0x00,0x0e,0x1c, 0x00,0x07,0x1c, 0x0e,0x0e,0x1c, 0x11,0x0e,0x1c, 0x15,0x0e,0x1c, 0x18,0x0e,0x1c, 0x1c,0x0e,0x1c, 0x1c,0x0e,0x18, 0x1c,0x0e,0x15, 0x1c,0x0e,0x11, 0x1c,0x0e,0x0e, 0x1c,0x11,0x0e, 0x1c,0x15,0x0e, 0x1c,0x18,0x0e, 0x1c,0x1c,0x0e, 0x18,0x1c,0x0e, 0x15,0x1c,0x0e, 0x11,0x1c,0x0e, 0x0e,0x1c,0x0e, 0x0e,0x1c,0x11, 0x0e,0x1c,0x15, 0x0e,0x1c,0x18, 0x0e,0x1c,0x1c, 0x0e,0x18,0x1c, 0x0e,0x15,0x1c, 0x0e,0x11,0x1c, 0x14,0x14,0x1c, 0x16,0x14,0x1c, 0x18,0x14,0x1c, 0x1a,0x14,0x1c, 0x1c,0x14,0x1c, 0x1c,0x14,0x1a, 0x1c,0x14,0x18, 0x1c,0x14,0x16, 0x1c,0x14,0x14, 0x1c,0x16,0x14, 0x1c,0x18,0x14, 0x1c,0x1a,0x14, 0x1c,0x1c,0x14, 0x1a,0x1c,0x14, 0x18,0x1c,0x14, 0x16,0x1c,0x14, 0x14,0x1c,0x14, 0x14,0x1c,0x16, 0x14,0x1c,0x18, 0x14,0x1c,0x1a, 0x14,0x1c,0x1c, 0x14,0x1a,0x1c, 0x14,0x18,0x1c, 0x14,0x16,0x1c, 0x00,0x00,0x10, 0x04,0x00,0x10, 0x08,0x00,0x10, 0x0c,0x00,0x10, 0x10,0x00,0x10, 0x10,0x00,0x0c, 0x10,0x00,0x08, 0x10,0x00,0x04, 0x10,0x00,0x00, 0x10,0x04,0x00, 0x10,0x08,0x00, 0x10,0x0c,0x00, 0x10,0x10,0x00, 0x0c,0x10,0x00, 0x08,0x10,0x00, 0x04,0x10,0x00, 0x00,0x10,0x00, 0x00,0x10,0x04, 0x00,0x10,0x08, 0x00,0x10,0x0c, 0x00,0x10,0x10, 0x00,0x0c,0x10, 0x00,0x08,0x10, 0x00,0x04,0x10, 0x08,0x08,0x10, 0x0a,0x08,0x10, 0x0c,0x08,0x10, 0x0e,0x08,0x10, 0x10,0x08,0x10, 0x10,0x08,0x0e, 0x10,0x08,0x0c, 0x10,0x08,0x0a, 0x10,0x08,0x08, 0x10,0x0a,0x08, 0x10,0x0c,0x08, 0x10,0x0e,0x08, 0x10,0x10,0x08, 0x0e,0x10,0x08, 0x0c,0x10,0x08, 0x0a,0x10,0x08, 0x08,0x10,0x08, 0x08,0x10,0x0a, 0x08,0x10,0x0c, 0x08,0x10,0x0e, 0x08,0x10,0x10, 0x08,0x0e,0x10, 0x08,0x0c,0x10, 0x08,0x0a,0x10, 0x0b,0x0b,0x10, 0x0c,0x0b,0x10, 0x0d,0x0b,0x10, 0x0f,0x0b,0x10, 0x10,0x0b,0x10, 0x10,0x0b,0x0f, 0x10,0x0b,0x0d, 0x10,0x0b,0x0c, 0x10,0x0b,0x0b, 0x10,0x0c,0x0b, 0x10,0x0d,0x0b, 0x10,0x0f,0x0b, 0x10,0x10,0x0b, 0x0f,0x10,0x0b, 0x0d,0x10,0x0b, 0x0c,0x10,0x0b, 0x0b,0x10,0x0b, 0x0b,0x10,0x0c, 0x0b,0x10,0x0d, 0x0b,0x10,0x0f, 0x0b,0x10,0x10, 0x0b,0x0f,0x10, 0x0b,0x0d,0x10, 0x0b,0x0c,0x10, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00 }; static Bit8u static_functionality[0x10]= { /* 0 */ 0xff, // All modes supported #1 /* 1 */ 0xe0, // All modes supported #2 /* 2 */ 0x0f, // All modes supported #3 /* 3 */ 0x00, 0x00, 0x00, 0x00, // reserved /* 7 */ 0x07, // 200, 350, 400 scan lines /* 8 */ 0x02, // mamimum number of visible charsets in text mode /* 9 */ 0x08, // total number of charset blocks in text mode /* a */ 0xe7, // Change to add new functions /* b */ 0x0c, // Change to add new functions /* c */ 0x00, // reserved /* d */ 0x00, // reserved /* e */ 0x00, // Change to add new functions /* f */ 0x00 // reserved }; xen-4.9.2/tools/firmware/hvmloader/0000775000175000017500000000000013256712137015412 5ustar smbsmbxen-4.9.2/tools/firmware/hvmloader/util.h0000664000175000017500000002230213256712137016537 0ustar smbsmb#ifndef __HVMLOADER_UTIL_H__ #define __HVMLOADER_UTIL_H__ #include #include #include #include #include #include #include "e820.h" /* Request un-prefixed values from errno.h. */ #define XEN_ERRNO(name, value) name = value, enum { #include }; /* Cause xs_wire.h to give us xsd_errors[]. */ #define EINVAL EINVAL #define __STR(...) #__VA_ARGS__ #define STR(...) __STR(__VA_ARGS__) /* GDT selector values. */ #define SEL_CODE16 0x0008 #define SEL_DATA16 0x0010 #define SEL_CODE32 0x0018 #define SEL_DATA32 0x0020 #define SEL_CODE64 0x0028 #undef offsetof #define offsetof(t, m) ((unsigned long)&((t *)0)->m) #undef NULL #define NULL ((void*)0) void __assert_failed(char *assertion, char *file, int line) __attribute__((noreturn)); #define ASSERT(p) \ do { if (!(p)) __assert_failed(#p, __FILE__, __LINE__); } while (0) void __bug(char *file, int line) __attribute__((noreturn)); #define BUG() __bug(__FILE__, __LINE__) #define BUG_ON(p) do { if (p) BUG(); } while (0) #define BUILD_BUG_ON(p) ((void)sizeof(char[1 - 2 * !!(p)])) #define min_t(type,x,y) \ ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) #define max_t(type,x,y) \ ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) static inline int test_bit(unsigned int b, const void *p) { return !!(((const uint8_t *)p)[b>>3] & (1u<<(b&7))); } static inline int test_and_clear_bit(int nr, volatile void *addr) { int oldbit; asm volatile ( "lock ; btrl %2,%1 ; sbbl %0,%0" : "=r" (oldbit), "=m" (*(volatile long *)addr) : "Ir" (nr), "m" (*(volatile long *)addr) : "memory"); return oldbit; } /* MSR access */ void wrmsr(uint32_t idx, uint64_t v); uint64_t rdmsr(uint32_t idx); /* I/O output */ void outb(uint16_t addr, uint8_t val); void outw(uint16_t addr, uint16_t val); void outl(uint16_t addr, uint32_t val); /* I/O input */ uint8_t inb(uint16_t addr); uint16_t inw(uint16_t addr); uint32_t inl(uint16_t addr); /* CMOS access */ uint8_t cmos_inb(uint8_t idx); void cmos_outb(uint8_t idx, uint8_t val); /* APIC access */ uint32_t ioapic_read(uint32_t reg); void ioapic_write(uint32_t reg, uint32_t val); uint32_t lapic_read(uint32_t reg); void lapic_write(uint32_t reg, uint32_t val); /* PCI access */ uint32_t pci_read(uint32_t devfn, uint32_t reg, uint32_t len); #define pci_readb(devfn, reg) ((uint8_t) pci_read(devfn, reg, 1)) #define pci_readw(devfn, reg) ((uint16_t)pci_read(devfn, reg, 2)) #define pci_readl(devfn, reg) ((uint32_t)pci_read(devfn, reg, 4)) void pci_write(uint32_t devfn, uint32_t reg, uint32_t len, uint32_t val); #define pci_writeb(devfn, reg, val) pci_write(devfn, reg, 1, (uint8_t) (val)) #define pci_writew(devfn, reg, val) pci_write(devfn, reg, 2, (uint16_t)(val)) #define pci_writel(devfn, reg, val) pci_write(devfn, reg, 4, (uint32_t)(val)) /* Get a pointer to the shared-info page */ struct shared_info *get_shared_info(void) __attribute__ ((const)); /* Get CPU speed in MHz. */ uint16_t get_cpu_mhz(void); /* Hardware detection. */ int uart_exists(uint16_t uart_base); int lpt_exists(uint16_t lpt_base); int hpet_exists(unsigned long hpet_base); /* Do cpuid instruction, with operation 'idx' */ void cpuid(uint32_t idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); /* Read the TSC register. */ static inline uint64_t rdtsc(void) { uint64_t tsc; asm volatile ( "rdtsc" : "=A" (tsc) ); return tsc; } /* Relax the CPU and let the compiler know that time passes. */ static inline void cpu_relax(void) { asm volatile ( "rep ; nop" : : : "memory" ); } /* Memory barriers. */ #define barrier() asm volatile ( "" : : : "memory" ) #define rmb() barrier() #define wmb() barrier() #define mb() asm volatile ( "lock; addl $0,0(%%esp)" : : : "memory" ) /* * Divide a 64-bit dividend by a 32-bit divisor. * (1) Overwrites the 64-bit dividend _in_place_ with the quotient * (2) Returns the 32-bit remainder */ #define do_div(n, base) ({ \ unsigned long __upper, __low, __high, __mod, __base; \ __base = (base); \ asm ( "" : "=a" (__low), "=d" (__high) : "A" (n) ); \ __upper = __high; \ if ( __high ) \ { \ __upper = __high % (__base); \ __high = __high / (__base); \ } \ asm ( "divl %2" \ : "=a" (__low), "=d" (__mod) \ : "rm" (__base), "0" (__low), "1" (__upper) ); \ asm ( "" : "=A" (n) : "a" (__low), "d" (__high) ); \ __mod; \ }) /* HVM-builder info. */ struct hvm_info_table *get_hvm_info_table(void) __attribute__ ((const)); #define hvm_info (get_hvm_info_table()) /* HVM start info */ extern const struct hvm_start_info *hvm_start_info; /* String and memory functions */ int strcmp(const char *cs, const char *ct); int strncmp(const char *s1, const char *s2, uint32_t n); char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, unsigned n); unsigned strlen(const char *s); long long strtoll(const char *s, char **end, int base); int memcmp(const void *s1, const void *s2, unsigned n); void *memcpy(void *dest, const void *src, unsigned n); void *memmove(void *dest, const void *src, unsigned n); void *memset(void *s, int c, unsigned n); char *itoa(char *a, unsigned int i); /* convert a byte to two lowercase hex digits, with no terminating NUL character. digits[] must have at least two elements. */ void byte_to_hex(char *digits, uint8_t byte); /* Convert an array of 16 unsigned bytes to a DCE/OSF formatted UUID string. Pre-condition: sizeof(dest) >= 37 */ void uuid_to_string(char *dest, uint8_t *uuid); /* Debug output */ #define PRIllx "%x%08x" #define PRIllx_arg(ll) (uint32_t)((ll)>>32), (uint32_t)(ll) int printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); int vprintf(const char *fmt, va_list ap); /* Buffer output */ int snprintf(char *buf, size_t size, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); /* Populate specified memory hole with RAM. */ void mem_hole_populate_ram(xen_pfn_t mfn, uint32_t nr_mfns); /* Allocate a memory hole below 4GB. */ xen_pfn_t mem_hole_alloc(uint32_t nr_mfns); /* Allocate memory in a reserved region below 4GB. */ void *mem_alloc(uint32_t size, uint32_t align); #define virt_to_phys(v) ((unsigned long)(v)) /* Allocate memory in a scratch region */ void *scratch_alloc(uint32_t size, uint32_t align); /* Connect our xenbus client to the backend. * Call once, before any other xenbus actions. */ void xenbus_setup(void); /* Reset the xenbus connection so the next kernel can start again. */ void xenbus_shutdown(void); /* Read a xenstore key. Returns a nul-terminated string (even if the XS * data wasn't nul-terminated) or NULL. The returned string is in a * static buffer, so only valid until the next xenstore/xenbus operation. * If @default_resp is specified, it is returned in preference to a NULL or * empty string received from xenstore. */ const char *xenstore_read(const char *path, const char *default_resp); /* Write a xenstore key. @value must be a nul-terminated string. Returns * zero on success or a xenstore error code on failure. */ int xenstore_write(const char *path, const char *value); /* Get a HVM param. */ int hvm_param_get(uint32_t index, uint64_t *value); /* Set a HVM param. */ int hvm_param_set(uint32_t index, uint64_t value); /* Setup PCI bus */ void pci_setup(void); /* Setup memory map */ void memory_map_setup(void); /* Sync memory map */ void adjust_memory_map(void); /* Prepare the 32bit BIOS */ uint32_t rombios_highbios_setup(void); /* Miscellaneous. */ unsigned int cpu_phys_addr(void); void cacheattr_init(void); unsigned long create_mp_tables(void *table); void hvm_write_smbios_tables( unsigned long ep, unsigned long smbios_start, unsigned long smbios_end); unsigned long create_pir_tables(void); void smp_initialise(void); #include "e820.h" int build_e820_table(struct e820entry *e820, unsigned int lowmem_reserved_base, unsigned int bios_image_base); void dump_e820_table(struct e820entry *e820, unsigned int nr); #ifndef NDEBUG void perform_tests(void); #else #define perform_tests() ((void)0) #endif extern char _start[], _end[]; int get_mem_mapping_layout(struct e820entry entries[], unsigned int *max_entries); extern struct e820map memory_map; bool check_overlap(uint64_t start, uint64_t size, uint64_t reserved_start, uint64_t reserved_size); struct acpi_config; void hvmloader_acpi_build_tables(struct acpi_config *config, unsigned int physical); #endif /* __HVMLOADER_UTIL_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/config-seabios.h0000664000175000017500000000045713256712137020461 0ustar smbsmb#ifndef __HVMLOADER_CONFIG_SEABIOS_H__ #define __HVMLOADER_CONFIG_SEABIOS_H__ #define BIOS_INFO_PHYSICAL_ADDRESS 0x00001000 #endif /* __HVMLOADER_CONFIG_SEABIOS_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/e820.c0000664000175000017500000002045113256712137016236 0ustar smbsmb/* * HVM e820 support. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005, International Business Machines Corporation. * Copyright (c) 2006, Keir Fraser, XenSource Inc. * Copyright (c) 2011, Citrix Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "config.h" #include "util.h" struct e820map memory_map; void memory_map_setup(void) { unsigned int nr_entries = E820MAX, i; int rc; uint64_t alloc_addr = RESERVED_MEMORY_DYNAMIC_START; uint64_t alloc_size = RESERVED_MEMORY_DYNAMIC_END - alloc_addr; rc = get_mem_mapping_layout(memory_map.map, &nr_entries); if ( rc || !nr_entries ) { printf("Get guest memory maps[%d] failed. (%d)\n", nr_entries, rc); BUG(); } memory_map.nr_map = nr_entries; for ( i = 0; i < nr_entries; i++ ) { if ( memory_map.map[i].type == E820_RESERVED && check_overlap(alloc_addr, alloc_size, memory_map.map[i].addr, memory_map.map[i].size) ) { printf("Fail to setup memory map due to conflict"); printf(" on dynamic reserved memory range.\n"); BUG(); } } } /* * Sometimes hvmloader may have relocated RAM so low_mem_pgend/high_mem_end * would be changed over there. But memory_map[] just records the * original low/high memory, so we need to sync these entries once * hvmloader modifies low/high memory. */ void adjust_memory_map(void) { uint32_t low_mem_end = hvm_info->low_mem_pgend << PAGE_SHIFT; uint64_t high_mem_end = (uint64_t)hvm_info->high_mem_pgend << PAGE_SHIFT; unsigned int i; for ( i = 0; i < memory_map.nr_map; i++ ) { uint64_t map_start = memory_map.map[i].addr; uint64_t map_size = memory_map.map[i].size; uint64_t map_end = map_start + map_size; /* If we need to adjust lowmem. */ if ( memory_map.map[i].type == E820_RAM && low_mem_end > map_start && low_mem_end < map_end ) { memory_map.map[i].size = low_mem_end - map_start; continue; } /* Modify the existing highmem region if it exists. */ if ( memory_map.map[i].type == E820_RAM && high_mem_end && map_start == ((uint64_t)1 << 32) ) { if ( high_mem_end != map_end ) memory_map.map[i].size = high_mem_end - map_start; high_mem_end = 0; continue; } } /* If there was no highmem region, just create one. */ if ( high_mem_end ) { memory_map.map[i].addr = ((uint64_t)1 << 32); memory_map.map[i].size = ((uint64_t)hvm_info->high_mem_pgend << PAGE_SHIFT) - memory_map.map[i].addr; memory_map.map[i].type = E820_RAM; memory_map.nr_map++; } } void dump_e820_table(struct e820entry *e820, unsigned int nr) { uint64_t last_end = 0, start, end; int i; printf("E820 table:\n"); for ( i = 0; i < nr; i++ ) { start = e820[i].addr; end = e820[i].addr + e820[i].size; if ( start < last_end ) printf(" OVERLAP!!\n"); else if ( start > last_end ) printf(" HOLE: %08x:%08x - %08x:%08x\n", (uint32_t)(last_end >> 32), (uint32_t)last_end, (uint32_t)(start >> 32), (uint32_t)start); printf(" [%02d]: %08x:%08x - %08x:%08x: ", i, (uint32_t)(start >> 32), (uint32_t)start, (uint32_t)(end >> 32), (uint32_t)end); switch ( e820[i].type ) { case E820_RAM: printf("RAM\n"); break; case E820_RESERVED: printf("RESERVED\n"); break; case E820_ACPI: printf("ACPI\n"); break; case E820_NVS: printf("NVS\n"); break; default: printf("UNKNOWN (%08x)\n", e820[i].type); break; } last_end = end; } } /* Create an E820 table based on memory parameters provided in hvm_info. */ int build_e820_table(struct e820entry *e820, unsigned int lowmem_reserved_base, unsigned int bios_image_base) { unsigned int nr = 0, i, j; uint32_t low_mem_end = hvm_info->low_mem_pgend << PAGE_SHIFT; if ( !lowmem_reserved_base ) lowmem_reserved_base = 0xA0000; /* Lowmem must be at least 512K to keep Windows happy) */ ASSERT ( lowmem_reserved_base > 512<<10 ); ASSERT ( bios_image_base < 0x100000 ); /* * 0x0-lowmem_reserved_base: Ordinary RAM. */ e820[nr].addr = 0x00000; e820[nr].size = lowmem_reserved_base; e820[nr].type = E820_RAM; nr++; /* lowmem_reserved_base-0xA0000: reserved by BIOS implementation. */ if ( lowmem_reserved_base < 0xA0000 ) { /* Reserved for internal use. */ e820[nr].addr = lowmem_reserved_base; e820[nr].size = 0xA0000-lowmem_reserved_base; e820[nr].type = E820_RESERVED; nr++; } /* * Following regions are standard regions of the PC memory map. * They are not covered by e820 regions. OSes will not use as RAM. * 0xA0000-0xC0000: VGA memory-mapped I/O. Not covered by E820. * 0xC0000-0xE0000: 16-bit devices, expansion ROMs (inc. vgabios). * TODO: free pages which turn out to be unused. */ /* * BIOS region. */ e820[nr].addr = bios_image_base; e820[nr].size = 0x100000-bios_image_base; e820[nr].type = E820_RESERVED; nr++; /* * Explicitly reserve space for special pages. * This space starts at RESERVED_MEMBASE an extends to cover various * fixed hardware mappings (e.g., LAPIC, IOAPIC, default SVGA framebuffer). * * If igd_opregion_pgbase we need to split the RESERVED region in two. */ if ( igd_opregion_pgbase ) { uint32_t igd_opregion_base = igd_opregion_pgbase << PAGE_SHIFT; e820[nr].addr = RESERVED_MEMBASE; e820[nr].size = (uint32_t) igd_opregion_base - RESERVED_MEMBASE; e820[nr].type = E820_RESERVED; nr++; e820[nr].addr = igd_opregion_base; e820[nr].size = IGD_OPREGION_PAGES * PAGE_SIZE; e820[nr].type = E820_NVS; nr++; e820[nr].addr = igd_opregion_base + IGD_OPREGION_PAGES * PAGE_SIZE; e820[nr].size = (uint32_t)-e820[nr].addr; e820[nr].type = E820_RESERVED; nr++; } else { e820[nr].addr = RESERVED_MEMBASE; e820[nr].size = (uint32_t)-e820[nr].addr; e820[nr].type = E820_RESERVED; nr++; } /* Low RAM goes here. Reserve space for special pages. */ BUG_ON(low_mem_end < (2u << 20)); /* * Construct E820 table according to recorded memory map. * * The memory map created by toolstack may include, * * #1. Low memory region * * Low RAM starts at least from 1M to make sure all standard regions * of the PC memory map, like BIOS, VGA memory-mapped I/O and vgabios, * have enough space. * * #2. Reserved regions if they exist * * #3. High memory region if it exists * * Note we just have one low memory entry and one high mmeory entry if * exists. */ for ( i = 0; i < memory_map.nr_map; i++ ) { e820[nr] = memory_map.map[i]; nr++; } /* Finally we need to sort all e820 entries. */ for ( j = 0; j < nr - 1; j++ ) { for ( i = j + 1; i < nr; i++ ) { if ( e820[j].addr > e820[i].addr ) { struct e820entry tmp = e820[j]; e820[j] = e820[i]; e820[i] = tmp; } } } return nr; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/mkhex0000775000175000017500000000161413256712137016456 0ustar smbsmb#!/bin/sh # # mkhex: Generate C embeddable hexdumps # # Leendert van Doorn, leendert@watson.ibm.com # Copyright (c) 2005, International Business Machines Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # this program; If not, see . # echo "unsigned $1[] = {" shift od -v -t x $@ | sed 's/^[0-9]* */0x/' | sed 's/ */, 0x/g' | sed 's/$/,/' | sed 's/0x,//' | sed 's/^[0-9]*,//' echo "};" xen-4.9.2/tools/firmware/hvmloader/hvm_param.c0000664000175000017500000000120113256712137017522 0ustar smbsmb/* * hvm_param.c: get/set HVM params. * * Copyright (C) 2014 Citrix Systems R&D Ltd. */ #include "util.h" #include "config.h" #include "hypercall.h" #include int hvm_param_get(uint32_t index, uint64_t *value) { struct xen_hvm_param p; int ret; p.domid = DOMID_SELF; p.index = index; ret = hypercall_hvm_op(HVMOP_get_param, &p); if (ret == 0) *value = p.value; return ret; } int hvm_param_set(uint32_t index, uint64_t value) { struct xen_hvm_param p; p.domid = DOMID_SELF; p.index = index; p.value = value; return hypercall_hvm_op(HVMOP_set_param, &p); } xen-4.9.2/tools/firmware/hvmloader/xenbus.c0000664000175000017500000002374313256712137017073 0ustar smbsmb/* * xenbus.c: static, synchronous, read-only xenbus client for hvmloader. * * Copyright (c) 2009 Tim Deegan, Citrix Systems (R&D) Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "util.h" #include "hypercall.h" #include #include #include #include static struct xenstore_domain_interface *rings; /* Shared ring with dom0 */ static evtchn_port_t event; /* Event-channel to dom0 */ static char payload[XENSTORE_PAYLOAD_MAX + 1]; /* Unmarshalling area */ static void ring_wait(void) { struct shared_info *shinfo = get_shared_info(); struct sched_poll poll; memset(&poll, 0, sizeof(poll)); set_xen_guest_handle(poll.ports, &event); poll.nr_ports = 1; while ( !test_and_clear_bit(event, shinfo->evtchn_pending) ) hypercall_sched_op(SCHEDOP_poll, &poll); } /* Connect our xenbus client to the backend. * Call once, before any other xenbus actions. */ void xenbus_setup(void) { uint64_t val; /* Ask Xen where the xenbus shared page is. */ if ( hvm_param_get(HVM_PARAM_STORE_PFN, &val) ) BUG(); rings = (void *) (unsigned long) (val << PAGE_SHIFT); /* Ask Xen where the xenbus event channel is. */ if ( hvm_param_get(HVM_PARAM_STORE_EVTCHN, &val) ) BUG(); event = val; printf("Xenbus rings @0x%lx, event channel %lu\n", (unsigned long) rings, (unsigned long) event); } /* Reset the xenbus connection so the next kernel can start again. */ void xenbus_shutdown(void) { struct shared_info *shinfo = get_shared_info(); evtchn_send_t send; ASSERT(rings != NULL); if (rings->server_features & XENSTORE_SERVER_FEATURE_RECONNECTION) { rings->connection = XENSTORE_RECONNECT; send.port = event; hypercall_event_channel_op(EVTCHNOP_send, &send); while (*(volatile uint32_t*)&rings->connection == XENSTORE_RECONNECT) ring_wait (); } else { /* If the backend reads the state while we're erasing it then the * ring state will become corrupted, preventing guest frontends from * connecting. This is rare. To help diagnose the failure, we fill * the ring with XS_INVALID packets. */ memset(rings->req, 0xff, XENSTORE_RING_SIZE); memset(rings->rsp, 0xff, XENSTORE_RING_SIZE); rings->req_cons = rings->req_prod = 0; rings->rsp_cons = rings->rsp_prod = 0; } /* Clear the event-channel state too. */ memset(shinfo->vcpu_info, 0, sizeof(shinfo->vcpu_info)); memset(shinfo->evtchn_pending, 0, sizeof(shinfo->evtchn_pending)); memset(shinfo->evtchn_mask, 0, sizeof(shinfo->evtchn_mask)); rings = NULL; } /* Helper functions: copy data in and out of the ring */ static void ring_write(const char *data, uint32_t len) { uint32_t part, done = 0; ASSERT(len <= XENSTORE_PAYLOAD_MAX); while ( len ) { /* Don't overrun the consumer pointer */ while ( (part = (XENSTORE_RING_SIZE - 1) - MASK_XENSTORE_IDX(rings->req_prod - rings->req_cons)) == 0 ) ring_wait(); /* Don't overrun the end of the ring */ if ( part > (XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(rings->req_prod)) ) part = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(rings->req_prod); /* Don't write more than we were asked for */ if ( part > len ) part = len; memcpy(rings->req + MASK_XENSTORE_IDX(rings->req_prod), data + done, part); barrier(); /* = wmb before prod write, rmb before next cons read */ rings->req_prod += part; len -= part; done += part; } } static void ring_read(char *data, uint32_t len) { uint32_t part, done = 0; ASSERT(len <= XENSTORE_PAYLOAD_MAX); while ( len ) { /* Don't overrun the producer pointer */ while ( (part = MASK_XENSTORE_IDX(rings->rsp_prod - rings->rsp_cons)) == 0 ) { /* * Don't wait for producer to fill the ring if it is already full. * Condition happens when you write string > 1K into the ring. * eg case prod=1272 cons=248. */ if ( rings->rsp_prod - rings->rsp_cons == XENSTORE_RING_SIZE ) { part = XENSTORE_RING_SIZE; break; } ring_wait(); } /* Don't overrun the end of the ring */ if ( part > (XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(rings->rsp_cons)) ) part = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(rings->rsp_cons); /* Don't read more than we were asked for */ if ( part > len ) part = len; memcpy(data + done, rings->rsp + MASK_XENSTORE_IDX(rings->rsp_cons), part); barrier(); /* = wmb before cons write, rmb before next prod read */ rings->rsp_cons += part; len -= part; done += part; } } #define MAX_SEGMENTS 4 /* Send a request. */ static void xenbus_send(uint32_t type, ...) { struct xsd_sockmsg hdr; va_list ap; struct { const char *data; uint32_t len; } seg[MAX_SEGMENTS]; evtchn_send_t send; int i, n; /* Not acceptable to use xenbus before setting it up */ ASSERT(rings != NULL); /* Put the request on the ring */ hdr.type = type; hdr.req_id = 0; /* We only ever issue one request at a time */ hdr.tx_id = 0; /* We never use transactions */ hdr.len = 0; va_start(ap, type); for ( i = 0; ; i++ ) { seg[i].data = va_arg(ap, const char *); seg[i].len = va_arg(ap, uint32_t); if ( seg[i].data == NULL ) break; hdr.len += seg[i].len; } n = i; va_end(ap); ring_write((char *) &hdr, sizeof hdr); for ( i = 0; i < n; i++ ) ring_write(seg[i].data, seg[i].len); /* Tell the other end about the request */ send.port = event; hypercall_event_channel_op(EVTCHNOP_send, &send); } /* Wait for the answer to a previous request. * Returns 0 for success, or an errno for error. * The answer is returned in a static buffer which is only * valid until the next call of xenbus_send(). */ static int xenbus_recv(uint32_t *reply_len, const char **reply_data, uint32_t *reply_type) { struct xsd_sockmsg hdr; do { /* Pull the reply off the ring */ ring_read((char *) &hdr, sizeof(hdr)); ring_read(payload, hdr.len); /* For sanity's sake, nul-terminate the answer */ payload[hdr.len] = '\0'; } while ( hdr.type == XS_DEBUG ); if ( reply_type ) *reply_type = hdr.type; /* Handle errors */ if ( hdr.type == XS_ERROR ) { int i; *reply_len = 0; for ( i = 0; i < ((sizeof xsd_errors) / (sizeof xsd_errors[0])); i++ ) if ( !strcmp(xsd_errors[i].errstring, payload) ) return xsd_errors[i].errnum; /* Default error value if we couldn't decode the ASCII error */ return EIO; } if ( reply_data ) *reply_data = payload; if ( reply_len ) *reply_len = hdr.len; return 0; } /* Read a xenstore key. Returns a nul-terminated string (even if the XS * data wasn't nul-terminated) or NULL. The returned string is in a * static buffer, so only valid until the next xenstore/xenbus operation. * If @default_resp is specified, it is returned in preference to a NULL or * empty string received from xenstore. */ const char *xenstore_read(const char *path, const char *default_resp) { uint32_t len = 0, type = 0; const char *answer = NULL; xenbus_send(XS_READ, path, strlen(path), "", 1, /* nul separator */ NULL, 0); if ( xenbus_recv(&len, &answer, &type) || (type != XS_READ) ) answer = NULL; if ( (default_resp != NULL) && ((answer == NULL) || (*answer == '\0')) ) answer = default_resp; /* We know xenbus_recv() nul-terminates its answer, so just pass it on. */ return answer; } /* Write a xenstore key. @value must be a nul-terminated string. Returns * zero on success or a xenstore error code on failure. */ int xenstore_write(const char *path, const char *value) { uint32_t len = 0, type = 0; const char *answer = NULL; int ret; xenbus_send(XS_WRITE, path, strlen(path), "", 1, /* nul separator */ value, strlen(value), NULL, 0); ret = xenbus_recv(&len, &answer, &type); if ( ret == 0 && ((type != XS_WRITE) || (len != 3) || !answer || strcmp(answer, "OK")) ) ret = EIO; return ret; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/32bitbios_support.c0000664000175000017500000001112113256712137021146 0ustar smbsmb/* * 32bitbios_support.c - relocation of 32bit BIOS implementation * * Stefan Berger, stefanb@us.ibm.com * Copyright (c) 2006, International Business Machines Corporation. * * Keir Fraser, keir@xensource.com * Copyright (c) 2007, XenSource Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include #include #ifdef __sun__ #include #endif #include "util.h" #include "config.h" #include "../rombios/32bit/32bitbios_flat.h" static uint32_t relocate_32bitbios(char *elfarray, uint32_t elfarraysize) { Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfarray; Elf32_Shdr *shdr = (Elf32_Shdr *)&elfarray[ehdr->e_shoff]; uint32_t reloc_off, reloc_size; char *highbiosarea; int i; /* * Step 1. General elf cleanup, and compute total relocation size. */ reloc_off = 0; for ( i = 0; i < ehdr->e_shnum; i++ ) { /* By default all section data points into elf image data array. */ shdr[i].sh_addr = (Elf32_Addr)&elfarray[shdr[i].sh_offset]; /* Fix up a corner case of address alignment. */ if ( shdr[i].sh_addralign == 0 ) shdr[i].sh_addralign = 1; /* Any section which contains run-time data must be relocated. */ if ( shdr[i].sh_flags & SHF_ALLOC ) { uint32_t mask = shdr[i].sh_addralign - 1; reloc_off = (reloc_off + mask) & ~mask; reloc_off += shdr[i].sh_size; } } /* * Step 2. Now we know the relocation size, allocate a chunk of high mem. */ reloc_size = reloc_off; printf("%d bytes of ROMBIOS high-memory extensions:\n", reloc_size); highbiosarea = mem_alloc(reloc_size, 1024); BUG_ON(highbiosarea == NULL); printf(" Relocating to 0x%x-0x%x ... ", (uint32_t)&highbiosarea[0], (uint32_t)&highbiosarea[reloc_size]); /* * Step 3. Copy run-time data into the newly-allocated high-memory chunk. */ reloc_off = 0; for ( i = 0; i < ehdr->e_shnum; i++ ) { uint32_t mask = shdr[i].sh_addralign - 1; /* Nothing to do for non-run-time sections. */ if ( !(shdr[i].sh_flags & SHF_ALLOC) ) continue; /* Copy from old location. */ reloc_off = (reloc_off + mask) & ~mask; if ( shdr[i].sh_type == SHT_NOBITS ) memset(&highbiosarea[reloc_off], 0, shdr[i].sh_size); else memcpy(&highbiosarea[reloc_off], (void *)shdr[i].sh_addr, shdr[i].sh_size); /* Update address to new location. */ shdr[i].sh_addr = (Elf32_Addr)&highbiosarea[reloc_off]; reloc_off += shdr[i].sh_size; } BUG_ON(reloc_off != reloc_size); /* * Step 4. Perform relocations in high memory. */ for ( i = 0; i < ehdr->e_shnum; i++ ) { Elf32_Sym *syms, *sym; Elf32_Rel *rels; char *code; uint32_t *loc, fix; int j; if ( shdr[i].sh_type == SHT_RELA ) printf("Unsupported section type SHT_RELA\n"); if ( shdr[i].sh_type != SHT_REL ) continue; syms = (Elf32_Sym *)shdr[shdr[i].sh_link].sh_addr; rels = (Elf32_Rel *)shdr[i].sh_addr; code = (char *)shdr[shdr[i].sh_info].sh_addr; for ( j = 0; j < shdr[i].sh_size / sizeof(Elf32_Rel); j++ ) { sym = &syms[ELF32_R_SYM(rels[j].r_info)]; loc = (uint32_t *)&code[rels[j].r_offset]; fix = shdr[sym->st_shndx].sh_addr + sym->st_value; switch ( ELF32_R_TYPE(rels[j].r_info) ) { case R_386_PC32: *loc += fix - (uint32_t)loc; break; case R_386_32: *loc += fix; break; } } } printf("done\n"); return (uint32_t)highbiosarea; } uint32_t rombios_highbios_setup(void) { return relocate_32bitbios((char *)highbios_array, sizeof(highbios_array)); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/config.h0000664000175000017500000000537213256712137017037 0ustar smbsmb#ifndef __HVMLOADER_CONFIG_H__ #define __HVMLOADER_CONFIG_H__ #include enum virtual_vga { VGA_none, VGA_std, VGA_cirrus, VGA_pt }; extern enum virtual_vga virtual_vga; extern unsigned long igd_opregion_pgbase; #define IGD_OPREGION_PAGES 3 struct bios_config { const char *name; /* BIOS ROM image bits */ void *image; unsigned int image_size; /* Physical address to load at */ unsigned int bios_address; /* ROMS */ void (*load_roms)(void); void (*bios_load)(const struct bios_config *config, void *addr, uint32_t size); void (*bios_info_setup)(void); void (*bios_info_finish)(void); void (*e820_setup)(void); void (*acpi_build_tables)(void); void (*create_mp_tables)(void); void (*create_smbios_tables)(void); void (*create_pir_tables)(void); }; extern struct bios_config rombios_config; extern struct bios_config seabios_config; extern struct bios_config ovmf_config; #define PAGE_SHIFT 12 #define PAGE_SIZE (1ul << PAGE_SHIFT) extern uint32_t ioapic_base_address; extern uint8_t ioapic_version; #define IOAPIC_ID 0x01 #define LAPIC_BASE_ADDRESS 0xfee00000 #define LAPIC_ID(vcpu_id) ((vcpu_id) * 2) #define PCI_ISA_DEVFN 0x08 /* dev 1, fn 0 */ #define PCI_ISA_IRQ_MASK 0x0c20U /* ISA IRQs 5,10,11 are PCI connected */ /* MMIO hole: Hardcoded defaults, which can be dynamically expanded. */ #define PCI_MEM_END 0xfc000000 #define ACPI_TIS_HDR_ADDRESS 0xFED40F00UL extern unsigned long pci_mem_start, pci_mem_end; extern uint64_t pci_hi_mem_start, pci_hi_mem_end; /* Memory map. */ #define SCRATCH_PHYSICAL_ADDRESS 0x00010000 #define HYPERCALL_PHYSICAL_ADDRESS 0x00080000 #define VGABIOS_PHYSICAL_ADDRESS 0x000C0000 #define HVMLOADER_PHYSICAL_ADDRESS 0x00100000 /* Special BIOS mappings, etc. are allocated from here upwards... */ #define RESERVED_MEMBASE 0xFC000000 /* NB. ACPI_INFO_PHYSICAL_ADDRESS *MUST* match definition in acpi/dsdt.asl! */ #define ACPI_INFO_PHYSICAL_ADDRESS 0xFC000000 #define RESERVED_MEMORY_DYNAMIC_START 0xFC001000 #define RESERVED_MEMORY_DYNAMIC_END 0xFE000000 /* * GUEST_RESERVED: Physical address space reserved for guest use. * This is not dynamically advertised to guests, so this range must *never* * be used for any purpose by us, in future. It must always be marked as * reserved in the memory map (e.g., E820_RESERVED) so that mechanisms such * as PCI BAR remapping do not allocate from this region. */ #define GUEST_RESERVED_START 0xFE700000 #define GUEST_RESERVED_END 0xFE800000 extern unsigned long scratch_start; #endif /* __HVMLOADER_CONFIG_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/optionroms.c0000664000175000017500000001445213256712137017775 0ustar smbsmb/* * optionroms.c: Option ROM loading support. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005, International Business Machines Corporation. * * Copyright (c) 2006, Keir Fraser, XenSource Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "config.h" #include "option_rom.h" #include "util.h" #include "pci_regs.h" /* * Scan the list of Option ROMs at @roms for one which supports * PCI (@vendor_id, @device_id) found at slot @devfn. If one is found, * copy it to @dest and return its size rounded up to a multiple 2kB. This * function will not copy ROMs beyond address option_rom_end. */ static int scan_option_rom( unsigned int option_rom_end, uint8_t devfn, uint16_t vendor_id, uint16_t device_id, void *roms, uint32_t dest) { struct option_rom_header *rom; struct option_rom_pnp_header *pnph; struct option_rom_pci_header *pcih; uint8_t csum; int i; static uint32_t orom_ids[64]; static int nr_roms; /* Avoid duplicate ROMs. */ for ( i = 0; i < nr_roms; i++ ) if ( orom_ids[i] == (vendor_id | ((uint32_t)device_id << 16)) ) return 0; rom = roms; for ( ; ; ) { /* Invalid signature means we're out of option ROMs. */ if ( strncmp((char *)rom->signature, "\x55\xaa", 2) || (rom->rom_size == 0) ) break; /* Invalid checksum means we're out of option ROMs. */ csum = 0; for ( i = 0; i < (rom->rom_size * 512); i++ ) csum += ((uint8_t *)rom)[i]; if ( csum != 0 ) break; /* Check the PCI PnP header (if any) for a match. */ pcih = (struct option_rom_pci_header *) ((char *)rom + rom->pci_header_offset); if ( (rom->pci_header_offset != 0) && !strncmp((char *)pcih->signature, "PCIR", 4) && (pcih->vendor_id == vendor_id) && (pcih->device_id == device_id) ) goto found; rom = (struct option_rom_header *) ((char *)rom + rom->rom_size * 512); } return 0; found: /* Find the PnP expansion header (if any). */ pnph = ((rom->expansion_header_offset != 0) ? ((struct option_rom_pnp_header *) ((char *)rom + rom->expansion_header_offset)) : ((struct option_rom_pnp_header *)NULL)); while ( (pnph != NULL) && strncmp((char *)pnph->signature, "$PnP", 4) ) pnph = ((pnph->next_header_offset != 0) ? ((struct option_rom_pnp_header *) ((char *)rom + pnph->next_header_offset)) : ((struct option_rom_pnp_header *)NULL)); printf("Loading PCI Option ROM ...\n"); if ( (pnph != NULL) && (pnph->manufacturer_name_offset != 0) ) printf(" - Manufacturer: %s\n", (char *)rom + pnph->manufacturer_name_offset); if ( (pnph != NULL) && (pnph->product_name_offset != 0) ) printf(" - Product name: %s\n", (char *)rom + pnph->product_name_offset); if ( (dest + rom->rom_size * 512 + 1) > option_rom_end ) { printf("Option ROM size %x exceeds available space\n", rom->rom_size * 512); return 0; } orom_ids[nr_roms++] = vendor_id | ((uint32_t)device_id << 16); memcpy((void *)dest, rom, rom->rom_size * 512); *(uint8_t *)(dest + rom->rom_size * 512) = devfn; return round_option_rom(rom->rom_size * 512 + 1); } /* * Scan the PCI bus for the first NIC supported by etherboot, and copy * the corresponding rom data to *copy_rom_dest. Returns the length of the * selected rom, or 0 if no NIC found. */ int scan_etherboot_nic(unsigned int option_rom_end, uint32_t copy_rom_dest, void *etherboot_rom) { uint16_t class, vendor_id, device_id, devfn; int rom_size = 0; for ( devfn = 0; (devfn < 256) && !rom_size; devfn++ ) { class = pci_readw(devfn, PCI_CLASS_DEVICE); vendor_id = pci_readw(devfn, PCI_VENDOR_ID); device_id = pci_readw(devfn, PCI_DEVICE_ID); /* We're only interested in NICs. */ if ( (vendor_id != 0xffff) && (device_id != 0xffff) && (class == 0x0200) ) rom_size = scan_option_rom( option_rom_end, devfn, vendor_id, device_id, etherboot_rom, copy_rom_dest); } return rom_size; } /* * Scan the PCI bus for the devices that have an option ROM, and copy * the corresponding rom data to rom_phys_addr. */ int pci_load_option_roms(unsigned int option_rom_end, uint32_t rom_base_addr) { uint32_t option_rom_addr, rom_phys_addr = rom_base_addr; uint16_t vendor_id, device_id, devfn, class; for ( devfn = 0; devfn < 256; devfn++ ) { class = pci_readb(devfn, PCI_CLASS_DEVICE + 1); vendor_id = pci_readw(devfn, PCI_VENDOR_ID); device_id = pci_readw(devfn, PCI_DEVICE_ID); if ( (vendor_id == 0xffff) && (device_id == 0xffff) ) continue; /* * Currently only scan options from mass storage devices and serial * bus controller (Fibre Channel included). */ if ( (class != 0x1) && (class != 0xc) ) continue; option_rom_addr = pci_readl(devfn, PCI_ROM_ADDRESS); if ( !option_rom_addr ) continue; /* Ensure Expansion Bar is enabled before copying */ pci_writel(devfn, PCI_ROM_ADDRESS, option_rom_addr | 0x1); rom_phys_addr += scan_option_rom( option_rom_end, devfn, vendor_id, device_id, (void *)(option_rom_addr & ~2047), rom_phys_addr); /* Restore the default original value of Expansion Bar */ pci_writel(devfn, PCI_ROM_ADDRESS, option_rom_addr); } return rom_phys_addr - rom_base_addr; } xen-4.9.2/tools/firmware/hvmloader/rombios.c0000664000175000017500000001544113256712137017235 0ustar smbsmb/* * HVM ROMBIOS support. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005, International Business Machines Corporation. * Copyright (c) 2006, Keir Fraser, XenSource Inc. * Copyright (c) 2011, Citrix Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "config.h" #include "../rombios/config.h" #include "smbios_types.h" #include "pci_regs.h" #include "util.h" #include "hypercall.h" #include "option_rom.h" #include #include #define ROM_INCLUDE_ROMBIOS #define ROM_INCLUDE_VGABIOS #define ROM_INCLUDE_ETHERBOOT #include "roms.inc" #define ROMBIOS_BEGIN 0x000F0000 #define ROMBIOS_SIZE 0x00010000 #define ROMBIOS_MAXOFFSET 0x0000FFFF #define ROMBIOS_END (ROMBIOS_BEGIN + ROMBIOS_SIZE) extern unsigned char dsdt_anycpu[], dsdt_15cpu[]; extern int dsdt_anycpu_len, dsdt_15cpu_len; static void rombios_setup_e820(void) { /* * 0x9E000-0x09F000: Stack. * 0x9FC00-0x0A0000: Extended BIOS Data Area (EBDA). * ... * 0xE0000-0x0F0000: PC-specific area. We place various tables here. * 0xF0000-0x100000: System BIOS. */ *E820_NR = build_e820_table(E820, 0x9E000, 0xE0000); dump_e820_table(E820, *E820_NR); } static void rombios_setup_bios_info(void) { struct rombios_info *info; info = (struct rombios_info *)BIOS_INFO_PHYSICAL_ADDRESS; memset(info, 0, sizeof(*info)); } static void rombios_load_roms(void) { int option_rom_sz = 0, vgabios_sz = 0, etherboot_sz = 0; uint32_t etherboot_phys_addr = 0, option_rom_phys_addr = 0; switch ( virtual_vga ) { case VGA_cirrus: printf("Loading Cirrus VGABIOS ...\n"); memcpy((void *)VGABIOS_PHYSICAL_ADDRESS, vgabios_cirrusvga, sizeof(vgabios_cirrusvga)); vgabios_sz = round_option_rom(sizeof(vgabios_cirrusvga)); break; case VGA_std: printf("Loading Standard VGABIOS ...\n"); memcpy((void *)VGABIOS_PHYSICAL_ADDRESS, vgabios_stdvga, sizeof(vgabios_stdvga)); vgabios_sz = round_option_rom(sizeof(vgabios_stdvga)); break; case VGA_pt: printf("Loading VGABIOS of passthroughed gfx ...\n"); vgabios_sz = round_option_rom( (*(uint8_t *)(VGABIOS_PHYSICAL_ADDRESS+2)) * 512); break; default: printf("No emulated VGA adaptor ...\n"); break; } etherboot_phys_addr = VGABIOS_PHYSICAL_ADDRESS + vgabios_sz; if ( etherboot_phys_addr < OPTIONROM_PHYSICAL_ADDRESS ) etherboot_phys_addr = OPTIONROM_PHYSICAL_ADDRESS; etherboot_sz = scan_etherboot_nic(OPTIONROM_PHYSICAL_END, etherboot_phys_addr, etherboot); option_rom_phys_addr = etherboot_phys_addr + etherboot_sz; option_rom_sz = pci_load_option_roms(OPTIONROM_PHYSICAL_END, option_rom_phys_addr); printf("Option ROMs:\n"); if ( vgabios_sz ) printf(" %05x-%05x: VGA BIOS\n", VGABIOS_PHYSICAL_ADDRESS, VGABIOS_PHYSICAL_ADDRESS + vgabios_sz - 1); if ( etherboot_sz ) printf(" %05x-%05x: Etherboot ROM\n", etherboot_phys_addr, etherboot_phys_addr + etherboot_sz - 1); if ( option_rom_sz ) printf(" %05x-%05x: PCI Option ROMs\n", option_rom_phys_addr, option_rom_phys_addr + option_rom_sz - 1); } static void rombios_load(const struct bios_config *config, void *unused_addr, uint32_t unused_size) { uint32_t bioshigh; struct rombios_info *info; BUILD_BUG_ON(sizeof(rombios) > 0x100000 - ROMBIOS_PHYSICAL_ADDRESS); memcpy((void *)config->bios_address, config->image, config->image_size); bioshigh = rombios_highbios_setup(); info = (struct rombios_info *)BIOS_INFO_PHYSICAL_ADDRESS; info->bios32_entry = bioshigh; } /* * find_mp_table_start - searchs through BIOS memory for '___HVMMP' signature * * The '___HVMMP' signature is created by the ROMBIOS and designates a chunk * of space inside the ROMBIOS that is safe for us to write our MP table info */ static void *get_mp_table_start(void) { char *bios_mem; for ( bios_mem = (char *)ROMBIOS_BEGIN; bios_mem != (char *)ROMBIOS_END; bios_mem++ ) { if ( strncmp(bios_mem, "___HVMMP", 8) == 0 ) return bios_mem; } return NULL; } /* recalculate the new ROMBIOS checksum after adding MP tables */ static void reset_bios_checksum(void) { uint32_t i; uint8_t checksum; checksum = 0; for ( i = 0; i < ROMBIOS_MAXOFFSET; i++ ) checksum += ((uint8_t *)(ROMBIOS_BEGIN))[i]; *((uint8_t *)(ROMBIOS_BEGIN + ROMBIOS_MAXOFFSET)) = -checksum; } static void rombios_acpi_build_tables(void) { struct acpi_config config = { .dsdt_anycpu = dsdt_anycpu, .dsdt_anycpu_len = dsdt_anycpu_len, .dsdt_15cpu = dsdt_15cpu, .dsdt_15cpu_len = dsdt_15cpu_len, }; hvmloader_acpi_build_tables(&config, ACPI_PHYSICAL_ADDRESS); } static void rombios_create_mp_tables(void) { /* Find the 'safe' place in ROMBIOS for the MP tables. */ void *table = get_mp_table_start(); if ( table == NULL ) { printf("Couldn't find start point for MP tables\n"); return; } create_mp_tables(table); reset_bios_checksum(); } static void rombios_create_smbios_tables(void) { hvm_write_smbios_tables( SMBIOS_PHYSICAL_ADDRESS, SMBIOS_PHYSICAL_ADDRESS + sizeof(struct smbios_entry_point), SMBIOS_PHYSICAL_END); } struct bios_config rombios_config = { .name = "ROMBIOS", .image = rombios, .image_size = sizeof(rombios), .bios_address = ROMBIOS_PHYSICAL_ADDRESS, .load_roms = rombios_load_roms, .bios_load = rombios_load, .bios_info_setup = rombios_setup_bios_info, .bios_info_finish = NULL, .e820_setup = rombios_setup_e820, .acpi_build_tables = rombios_acpi_build_tables, .create_mp_tables = rombios_create_mp_tables, .create_smbios_tables = rombios_create_smbios_tables, .create_pir_tables = NULL, /* embedded in ROMBIOS */ }; /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/vnuma.h0000664000175000017500000000357313256712137016721 0ustar smbsmb/****************************************************************************** * vnuma.h * * Copyright (c) 2014, Wei Liu * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation; or, when distributed * separately from the Linux kernel or incorporated into other * software packages, subject to the following license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this source file (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. */ #ifndef __HVMLOADER_VNUMA_H__ #define __HVMLOADER_VNUMA_H__ #include extern unsigned int nr_vnodes, nr_vmemranges; extern unsigned int *vcpu_to_vnode, *vdistance; extern xen_vmemrange_t *vmemrange; void init_vnuma_info(void); #endif /* __HVMLOADER_VNUMA_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/hypercall.h0000664000175000017500000001527313256712137017556 0ustar smbsmb/****************************************************************************** * hypercall.h * * Copyright (c) 2002-2006, K A Fraser * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation; or, when distributed * separately from the Linux kernel or incorporated into other * software packages, subject to the following license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this source file (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. */ #ifndef __HVMLOADER_HYPERCALL_H__ #define __HVMLOADER_HYPERCALL_H__ #include #include #include "config.h" #define hcall_addr(name) \ ((unsigned long)HYPERCALL_PHYSICAL_ADDRESS + __HYPERVISOR_##name * 32) #define _hypercall0(type, name) \ ({ \ long __res; \ asm volatile ( \ "call *%%eax" \ : "=a" (__res) \ : "0" (hcall_addr(name)) \ : "memory" ); \ (type)__res; \ }) #define _hypercall1(type, name, a1) \ ({ \ long __res, __ign1; \ asm volatile ( \ "call *%%eax" \ : "=a" (__res), "=b" (__ign1) \ : "0" (hcall_addr(name)), \ "1" ((long)(a1)) \ : "memory" ); \ (type)__res; \ }) #define _hypercall2(type, name, a1, a2) \ ({ \ long __res, __ign1, __ign2; \ asm volatile ( \ "call *%%eax" \ : "=a" (__res), "=b" (__ign1), "=c" (__ign2) \ : "0" (hcall_addr(name)), \ "1" ((long)(a1)), "2" ((long)(a2)) \ : "memory" ); \ (type)__res; \ }) #define _hypercall3(type, name, a1, a2, a3) \ ({ \ long __res, __ign1, __ign2, __ign3; \ asm volatile ( \ "call *%%eax" \ : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ "=d" (__ign3) \ : "0" (hcall_addr(name)), \ "1" ((long)(a1)), "2" ((long)(a2)), \ "3" ((long)(a3)) \ : "memory" ); \ (type)__res; \ }) #define _hypercall4(type, name, a1, a2, a3, a4) \ ({ \ long __res, __ign1, __ign2, __ign3, __ign4; \ asm volatile ( \ "call *%%eax" \ : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ "=d" (__ign3), "=S" (__ign4) \ : "0" (hcall_addr(name)), \ "1" ((long)(a1)), "2" ((long)(a2)), \ "3" ((long)(a3)), "4" ((long)(a4)) \ : "memory" ); \ (type)__res; \ }) #define _hypercall5(type, name, a1, a2, a3, a4, a5) \ ({ \ long __res, __ign1, __ign2, __ign3, __ign4, __ign5; \ asm volatile ( \ "call *%%eax" \ : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ "=d" (__ign3), "=S" (__ign4), "=D" (__ign5) \ : "0" (hcall_addr(name)), \ "1" ((long)(a1)), "2" ((long)(a2)), \ "3" ((long)(a3)), "4" ((long)(a4)), \ "5" ((long)(a5)) \ : "memory" ); \ (type)__res; \ }) static inline int hypercall_sched_op( int cmd, void *arg) { return _hypercall2(int, sched_op, cmd, arg); } static inline int hypercall_memory_op( unsigned int cmd, void *arg) { return _hypercall2(int, memory_op, cmd, arg); } static inline int hypercall_multicall( void *call_list, int nr_calls) { return _hypercall2(int, multicall, call_list, nr_calls); } static inline int hypercall_event_channel_op( int cmd, void *arg) { return _hypercall2(int, event_channel_op, cmd, arg); } static inline int hypercall_xen_version( int cmd, void *arg) { return _hypercall2(int, xen_version, cmd, arg); } static inline int hypercall_console_io( int cmd, int count, char *str) { return _hypercall3(int, console_io, cmd, count, str); } static inline int hypercall_vm_assist( unsigned int cmd, unsigned int type) { return _hypercall2(int, vm_assist, cmd, type); } static inline int hypercall_vcpu_op( int cmd, int vcpuid, void *extra_args) { return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args); } static inline int hypercall_hvm_op( int cmd, void *arg) { return _hypercall2(int, hvm_op, cmd, arg); } #endif /* __HVMLOADER_HYPERCALL_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/apic_regs.h0000664000175000017500000001051513256712137017521 0ustar smbsmb#ifndef __ASM_APICDEF_H #define __ASM_APICDEF_H #define APIC_DEFAULT_PHYS_BASE 0xfee00000 #define APIC_ID 0x20 #define APIC_ID_MASK (0xFFu<<24) #define GET_APIC_ID(x) (((x)>>24)&0xFFu) #define SET_APIC_ID(x) (((x)<<24)) #define APIC_LVR 0x30 #define APIC_LVR_MASK 0xFF00FF #define GET_APIC_VERSION(x) ((x)&0xFF) #define GET_APIC_MAXLVT(x) (((x)>>16)&0xFF) #define APIC_INTEGRATED(x) ((x)&0xF0) #define APIC_XAPIC(x) ((x) >= 0x14) #define APIC_TASKPRI 0x80 #define APIC_TPRI_MASK 0xFF #define APIC_ARBPRI 0x90 #define APIC_ARBPRI_MASK 0xFF #define APIC_PROCPRI 0xA0 #define APIC_EOI 0xB0 #define APIC_EIO_ACK 0x0 #define APIC_RRR 0xC0 #define APIC_LDR 0xD0 #define APIC_LDR_MASK (0xFF<<24) #define GET_APIC_LOGICAL_ID(x) (((x)>>24)&0xFF) #define SET_APIC_LOGICAL_ID(x) (((x)<<24)) #define APIC_ALL_CPUS 0xFF #define APIC_DFR 0xE0 #define APIC_DFR_CLUSTER 0x0FFFFFFFul #define APIC_DFR_FLAT 0xFFFFFFFFul #define APIC_SPIV 0xF0 #define APIC_SPIV_FOCUS_DISABLED (1<<9) #define APIC_SPIV_APIC_ENABLED (1<<8) #define APIC_ISR 0x100 #define APIC_TMR 0x180 #define APIC_IRR 0x200 #define APIC_ESR 0x280 #define APIC_ESR_SEND_CS 0x00001 #define APIC_ESR_RECV_CS 0x00002 #define APIC_ESR_SEND_ACC 0x00004 #define APIC_ESR_RECV_ACC 0x00008 #define APIC_ESR_SENDILL 0x00020 #define APIC_ESR_RECVILL 0x00040 #define APIC_ESR_ILLREGA 0x00080 #define APIC_ICR 0x300 #define APIC_DEST_SELF 0x40000 #define APIC_DEST_ALLINC 0x80000 #define APIC_DEST_ALLBUT 0xC0000 #define APIC_ICR_RR_MASK 0x30000 #define APIC_ICR_RR_INVALID 0x00000 #define APIC_ICR_RR_INPROG 0x10000 #define APIC_ICR_RR_VALID 0x20000 #define APIC_INT_LEVELTRIG 0x08000 #define APIC_INT_ASSERT 0x04000 #define APIC_ICR_BUSY 0x01000 #define APIC_DEST_LOGICAL 0x00800 #define APIC_DEST_PHYSICAL 0x00000 #define APIC_DM_FIXED 0x00000 #define APIC_DM_LOWEST 0x00100 #define APIC_DM_SMI 0x00200 #define APIC_DM_REMRD 0x00300 #define APIC_DM_NMI 0x00400 #define APIC_DM_INIT 0x00500 #define APIC_DM_STARTUP 0x00600 #define APIC_DM_EXTINT 0x00700 #define APIC_VECTOR_MASK 0x000FF #define APIC_ICR2 0x310 #define GET_APIC_DEST_FIELD(x) (((x)>>24)&0xFF) #define SET_APIC_DEST_FIELD(x) ((x)<<24) #define APIC_LVTT 0x320 #define APIC_LVTTHMR 0x330 #define APIC_LVTPC 0x340 #define APIC_LVT0 0x350 #define APIC_LVT_TIMER_BASE_MASK (0x3<<18) #define GET_APIC_TIMER_BASE(x) (((x)>>18)&0x3) #define SET_APIC_TIMER_BASE(x) (((x)<<18)) #define APIC_TIMER_BASE_CLKIN 0x0 #define APIC_TIMER_BASE_TMBASE 0x1 #define APIC_TIMER_BASE_DIV 0x2 #define APIC_LVT_TIMER_PERIODIC (1<<17) #define APIC_LVT_MASKED (1<<16) #define APIC_LVT_LEVEL_TRIGGER (1<<15) #define APIC_LVT_REMOTE_IRR (1<<14) #define APIC_INPUT_POLARITY (1<<13) #define APIC_SEND_PENDING (1<<12) #define APIC_MODE_MASK 0x700 #define GET_APIC_DELIVERY_MODE(x) (((x)>>8)&0x7) #define SET_APIC_DELIVERY_MODE(x,y) (((x)&~0x700)|((y)<<8)) #define APIC_MODE_FIXED 0x0 #define APIC_MODE_NMI 0x4 #define APIC_MODE_EXTINT 0x7 #define APIC_LVT1 0x360 #define APIC_LVTERR 0x370 #define APIC_TMICT 0x380 #define APIC_TMCCT 0x390 #define APIC_TDCR 0x3E0 #define APIC_TDR_DIV_TMBASE (1<<2) #define APIC_TDR_DIV_1 0xB #define APIC_TDR_DIV_2 0x0 #define APIC_TDR_DIV_4 0x1 #define APIC_TDR_DIV_8 0x2 #define APIC_TDR_DIV_16 0x3 #define APIC_TDR_DIV_32 0x8 #define APIC_TDR_DIV_64 0x9 #define APIC_TDR_DIV_128 0xA #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/Makefile0000664000175000017500000000672113256712137017060 0ustar smbsmb# # Makefile # # Leendert van Doorn, leendert@watson.ibm.com # Copyright (c) 2005, International Business Machines Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # this program; If not, see . # XEN_ROOT = $(CURDIR)/../../.. include $(XEN_ROOT)/tools/firmware/Rules.mk LOADADDR = 0x100000 # SMBIOS spec requires format mm/dd/yyyy SMBIOS_REL_DATE ?= $(shell date +%m/%d/%Y) CFLAGS += $(CFLAGS_xeninclude) # We mustn't use tools-only public interfaces. CFLAGS += -D__XEN_INTERFACE_VERSION__=__XEN_LATEST_INTERFACE_VERSION__ OBJS = hvmloader.o mp_tables.o util.o smbios.o OBJS += smp.o cacheattr.o xenbus.o vnuma.o OBJS += e820.o pci.o pir.o ctype.o OBJS += hvm_param.o OBJS += ovmf.o seabios.o ifeq ($(debug),y) OBJS += tests.o endif CIRRUSVGA_DEBUG ?= n ROMBIOS_DIR := ../rombios ifeq ($(CONFIG_ROMBIOS),y) STDVGA_ROM := ../vgabios/VGABIOS-lgpl-latest.bin ifeq ($(CIRRUSVGA_DEBUG),y) CIRRUSVGA_ROM := ../vgabios/VGABIOS-lgpl-latest.cirrus.debug.bin else CIRRUSVGA_ROM := ../vgabios/VGABIOS-lgpl-latest.cirrus.bin endif ETHERBOOT_ROMS := $(addprefix ../etherboot/ipxe/src/bin/, $(addsuffix .rom, $(ETHERBOOT_NICS))) endif ROMS := ifeq ($(CONFIG_ROMBIOS),y) OBJS += optionroms.o 32bitbios_support.o rombios.o CFLAGS += -DENABLE_ROMBIOS ROMBIOS_ROM := $(ROMBIOS_DIR)/BIOS-bochs-latest ROMS += $(ROMBIOS_ROM) $(STDVGA_ROM) $(CIRRUSVGA_ROM) $(ETHERBOOT_ROMS) endif .PHONY: all all: acpi subdirs-all $(MAKE) hvmloader .PHONY: acpi acpi: $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES)" rombios.o: roms.inc smbios.o: CFLAGS += -D__SMBIOS_DATE__="\"$(SMBIOS_REL_DATE)\"" ACPI_PATH = ../../libacpi DSDT_FILES = dsdt_anycpu.c dsdt_15cpu.c dsdt_anycpu_qemu_xen.c ACPI_OBJS = $(patsubst %.c,%.o,$(DSDT_FILES)) build.o static_tables.o $(ACPI_OBJS): CFLAGS += -I. -DLIBACPI_STDUTILS=\"$(CURDIR)/util.h\" CFLAGS += -I$(ACPI_PATH) vpath build.c $(ACPI_PATH) vpath static_tables.c $(ACPI_PATH) OBJS += $(ACPI_OBJS) hvmloader: $(OBJS) $(LD) $(LDFLAGS_DIRECT) -N -Ttext $(LOADADDR) -o hvmloader.tmp $^ $(OBJCOPY) hvmloader.tmp hvmloader rm -f hvmloader.tmp roms.inc: $(ROMS) echo "/* Autogenerated file. DO NOT EDIT */" > $@.new ifneq ($(ROMBIOS_ROM),) echo "#ifdef ROM_INCLUDE_ROMBIOS" >> $@.new sh ./mkhex rombios $(ROMBIOS_ROM) >> $@.new echo "#endif" >> $@.new endif ifneq ($(STDVGA_ROM),) echo "#ifdef ROM_INCLUDE_VGABIOS" >> $@.new sh ./mkhex vgabios_stdvga $(STDVGA_ROM) >> $@.new echo "#endif" >> $@.new endif ifneq ($(CIRRUSVGA_ROM),) echo "#ifdef ROM_INCLUDE_VGABIOS" >> $@.new sh ./mkhex vgabios_cirrusvga $(CIRRUSVGA_ROM) >> $@.new echo "#endif" >> $@.new endif ifneq ($(ETHERBOOT_ROMS),) echo "#ifdef ROM_INCLUDE_ETHERBOOT" >> $@.new sh ./mkhex etherboot $(ETHERBOOT_ROMS) >> $@.new echo "#endif" >> $@.new endif mv $@.new $@ .PHONY: clean clean: subdirs-clean rm -f roms.inc roms.inc.new acpi.h rm -f hvmloader hvmloader.tmp *.o $(DEPS) $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) clean .PHONY: distclean distclean: clean -include $(DEPS) xen-4.9.2/tools/firmware/hvmloader/option_rom.h0000664000175000017500000000346613256712137017761 0ustar smbsmb#ifndef __HVMLOADER_OPTION_ROM_H__ #define __HVMLOADER_OPTION_ROM_H__ #include struct option_rom_header { uint8_t signature[2]; /* "\x55\xaa" */ uint8_t rom_size; /* 512-byte increments */ uint32_t entry_point; uint8_t reserved[17]; uint16_t pci_header_offset; uint16_t expansion_header_offset; } __attribute__ ((packed)); struct option_rom_pnp_header { uint8_t signature[4]; /* "$PnP" */ uint8_t structure_revision; uint8_t structure_length; /* 16-byte increments */ uint16_t next_header_offset; uint8_t reserved; uint8_t checksum; uint32_t device_id; uint16_t manufacturer_name_offset; uint16_t product_name_offset; uint8_t device_type_code[3]; uint8_t device_indicators; uint16_t boot_connection_vector; uint16_t disconnect_vector; uint16_t bootstap_entry_vector; uint16_t reserved2; uint16_t static_resource_information_vector; } __attribute__ ((packed)); struct option_rom_pci_header { uint8_t signature[4]; /* "PCIR" */ uint16_t vendor_id; uint16_t device_id; uint16_t vital_product_data_offset; uint16_t structure_length; uint8_t structure_revision; uint8_t class_code[3]; uint16_t image_length; uint16_t image_revision; uint8_t code_type; uint8_t indicator; uint16_t reserved; } __attribute__ ((packed)); #define round_option_rom(x) (((x) + 2047) & ~2047) int scan_etherboot_nic(unsigned int option_rom_end, uint32_t copy_rom_dest, void *etherboot_rom); int pci_load_option_roms(unsigned int option_rom_end, uint32_t rom_base_addr); #endif /* __HVMLOADER_OPTION_ROM_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/smbios.c0000664000175000017500000006403213256712137017057 0ustar smbsmb/* * smbios.c - Generate SMBIOS tables for Xen HVM domU's. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . * * Copyright (C) IBM Corporation, 2006 * * Authors: Andrew D. Ball */ #include #include #include #include "smbios_types.h" #include "util.h" #include "hypercall.h" #include /* SBMIOS handle base values */ #define SMBIOS_HANDLE_TYPE0 0x0000 #define SMBIOS_HANDLE_TYPE1 0x0100 #define SMBIOS_HANDLE_TYPE2 0x0200 #define SMBIOS_HANDLE_TYPE3 0x0300 #define SMBIOS_HANDLE_TYPE4 0x0400 #define SMBIOS_HANDLE_TYPE11 0x0B00 #define SMBIOS_HANDLE_TYPE16 0x1000 #define SMBIOS_HANDLE_TYPE17 0x1100 #define SMBIOS_HANDLE_TYPE19 0x1300 #define SMBIOS_HANDLE_TYPE20 0x1400 #define SMBIOS_HANDLE_TYPE22 0x1600 #define SMBIOS_HANDLE_TYPE32 0x2000 #define SMBIOS_HANDLE_TYPE39 0x2700 #define SMBIOS_HANDLE_TYPE127 0x7f00 static void smbios_pt_init(void); static void* get_smbios_pt_struct(uint8_t type, uint32_t *length_out); static void get_cpu_manufacturer(char *buf, int len); static int write_smbios_tables(void *ep, void *start, uint32_t vcpus, uint64_t memsize, uint8_t uuid[16], char *xen_version, uint32_t xen_major_version, uint32_t xen_minor_version, unsigned *nr_structs, unsigned *max_struct_size); static uint64_t get_memsize(void); static void smbios_entry_point_init(void *start, uint16_t max_structure_size, uint16_t structure_table_length, uint32_t structure_table_address, uint16_t number_of_structures); static void * smbios_type_0_init(void *start, const char *xen_version, uint32_t xen_major_version, uint32_t xen_minor_version); static void * smbios_type_1_init(void *start, const char *xen_version, uint8_t uuid[16]); static void * smbios_type_2_init(void *start); static void * smbios_type_3_init(void *start); static void * smbios_type_4_init(void *start, unsigned int cpu_number, char *cpu_manufacturer); static void * smbios_type_11_init(void *start); static void * smbios_type_16_init(void *start, uint32_t memory_size_mb, int nr_mem_devs); static void * smbios_type_17_init(void *start, uint32_t memory_size_mb, int instance); static void * smbios_type_19_init(void *start, uint32_t memory_size_mb, int instance); static void * smbios_type_20_init(void *start, uint32_t memory_size_mb, int instance); static void * smbios_type_22_init(void *start); static void * smbios_type_32_init(void *start); static void * smbios_type_39_init(void *start); static void * smbios_type_vendor_oem_init(void *start); static void * smbios_type_127_init(void *start); static uint32_t *smbios_pt_addr = NULL; static uint32_t smbios_pt_length = 0; static void smbios_pt_init(void) { const char *s; s = xenstore_read(HVM_XS_SMBIOS_PT_ADDRESS, NULL); if ( s == NULL ) goto reset; smbios_pt_addr = (uint32_t*)(uint32_t)strtoll(s, NULL, 0); if ( smbios_pt_addr == NULL ) goto reset; s = xenstore_read(HVM_XS_SMBIOS_PT_LENGTH, NULL); if ( s == NULL ) goto reset; smbios_pt_length = (uint32_t)strtoll(s, NULL, 0); if ( smbios_pt_length == 0 ) goto reset; return; reset: smbios_pt_addr = NULL; smbios_pt_length = 0; } static void* get_smbios_pt_struct(uint8_t type, uint32_t *length_out) { uint32_t *sep = smbios_pt_addr; uint32_t total = 0; uint8_t *ptr; if ( sep == NULL ) return NULL; while ( total < smbios_pt_length ) { ptr = (uint8_t*)(sep + 1); if ( ptr[0] == type ) { *length_out = *sep; return ptr; } total += (*sep + sizeof(uint32_t)); sep = (uint32_t*)(ptr + *sep); } return NULL; } static void get_cpu_manufacturer(char *buf, int len) { char id[12]; uint32_t eax = 0; cpuid(0, &eax, (uint32_t *)&id[0], (uint32_t *)&id[8], (uint32_t *)&id[4]); if ( memcmp(id, "GenuineIntel", 12) == 0 ) strncpy(buf, "Intel", len); else if ( memcmp(id, "AuthenticAMD", 12) == 0 ) strncpy(buf, "AMD", len); else strncpy(buf, "unknown", len); } static int write_smbios_tables(void *ep, void *start, uint32_t vcpus, uint64_t memsize, uint8_t uuid[16], char *xen_version, uint32_t xen_major_version, uint32_t xen_minor_version, unsigned *nr_structs, unsigned *max_struct_size) { unsigned cpu_num; char *p, *q; char cpu_manufacturer[15]; int i, nr_mem_devs; smbios_pt_init(); get_cpu_manufacturer(cpu_manufacturer, 15); p = (char *)start; #define do_struct(fn) do { \ q = (fn); \ if ( q != p ) \ (*nr_structs)++; \ if ( (q - p) > *max_struct_size ) \ *max_struct_size = q - p; \ p = q; \ } while (0) do_struct(smbios_type_0_init(p, xen_version, xen_major_version, xen_minor_version)); do_struct(smbios_type_1_init(p, xen_version, uuid)); do_struct(smbios_type_2_init(p)); do_struct(smbios_type_3_init(p)); for ( cpu_num = 1; cpu_num <= vcpus; cpu_num++ ) do_struct(smbios_type_4_init(p, cpu_num, cpu_manufacturer)); do_struct(smbios_type_11_init(p)); /* Each 'memory device' covers up to 16GB of address space. */ nr_mem_devs = (memsize + 0x3fff) >> 14; do_struct(smbios_type_16_init(p, memsize, nr_mem_devs)); for ( i = 0; i < nr_mem_devs; i++ ) { uint32_t dev_memsize = 0x4000; /* all but last covers 16GB */ if ( (i == (nr_mem_devs - 1)) && ((memsize & 0x3fff) != 0) ) dev_memsize = memsize & 0x3fff; /* last dev is <16GB */ do_struct(smbios_type_17_init(p, dev_memsize, i)); do_struct(smbios_type_19_init(p, dev_memsize, i)); do_struct(smbios_type_20_init(p, dev_memsize, i)); } do_struct(smbios_type_22_init(p)); do_struct(smbios_type_32_init(p)); do_struct(smbios_type_39_init(p)); do_struct(smbios_type_vendor_oem_init(p)); do_struct(smbios_type_127_init(p)); #undef do_struct return ((char *)p - (char *)start); } /* Calculate how much pseudo-physical memory (in MB) is allocated to us. */ static uint64_t get_memsize(void) { uint64_t sz; sz = (uint64_t)hvm_info->low_mem_pgend << PAGE_SHIFT; if ( hvm_info->high_mem_pgend ) sz += (((uint64_t)hvm_info->high_mem_pgend << PAGE_SHIFT) - (1ull << 32)); /* * Round up to the nearest MB. The user specifies domU pseudo-physical * memory in megabytes, so not doing this could easily lead to reporting * one less MB than the user specified. */ return (sz + (1ul << 20) - 1) >> 20; } void hvm_write_smbios_tables( unsigned long ep, unsigned long smbios_start, unsigned long smbios_end) { xen_domain_handle_t uuid; uint16_t xen_major_version, xen_minor_version; uint32_t xen_version; char xen_extra_version[XEN_EXTRAVERSION_LEN]; /* guess conservatively on buffer length for Xen version string */ char xen_version_str[80]; /* temporary variables used to build up Xen version string */ char *p = NULL; /* points to next point of insertion */ unsigned len = 0; /* length of string already composed */ char tmp[16]; /* holds result of itoa() */ unsigned tmp_len; /* length of next string to add */ unsigned nr_structs = 0, max_struct_size = 0; hypercall_xen_version(XENVER_guest_handle, uuid); BUILD_BUG_ON(sizeof(xen_domain_handle_t) != 16); /* xen_version major and minor */ xen_version = hypercall_xen_version(XENVER_version, NULL); xen_major_version = (uint16_t) (xen_version >> 16); xen_minor_version = (uint16_t) xen_version; hypercall_xen_version(XENVER_extraversion, xen_extra_version); /* build up human-readable Xen version string */ p = xen_version_str; len = 0; itoa(tmp, xen_major_version); tmp_len = strlen(tmp); len += tmp_len; if ( len >= sizeof(xen_version_str) ) goto error_out; strcpy(p, tmp); p += tmp_len; len++; if ( len >= sizeof(xen_version_str) ) goto error_out; *p = '.'; p++; itoa(tmp, xen_minor_version); tmp_len = strlen(tmp); len += tmp_len; if ( len >= sizeof(xen_version_str) ) goto error_out; strcpy(p, tmp); p += tmp_len; tmp_len = strlen(xen_extra_version); len += tmp_len; if ( len >= sizeof(xen_version_str) ) goto error_out; strcpy(p, xen_extra_version); p += tmp_len; xen_version_str[sizeof(xen_version_str)-1] = '\0'; /* scratch_start is a safe large memory area for scratch. */ len = write_smbios_tables((void *)ep, (void *)scratch_start, hvm_info->nr_vcpus, get_memsize(), uuid, xen_version_str, xen_major_version, xen_minor_version, &nr_structs, &max_struct_size); if ( smbios_start && smbios_start + len > smbios_end ) goto error_out; if ( !smbios_start ) smbios_start = (unsigned long)mem_alloc(len, 0); memcpy((void *)smbios_start, (void *)scratch_start, len); smbios_entry_point_init( (void *)ep, max_struct_size, len, smbios_start, nr_structs); return; error_out: printf("Could not write SMBIOS tables, error in hvmloader.c:" "hvm_write_smbios_tables()\n"); } static void smbios_entry_point_init(void *start, uint16_t max_structure_size, uint16_t structure_table_length, uint32_t structure_table_address, uint16_t number_of_structures) { uint8_t sum; int i; struct smbios_entry_point *ep = start; memset(ep, 0, sizeof(*ep)); memcpy(ep->anchor_string, "_SM_", 4); ep->length = 0x1f; ep->smbios_major_version = 2; ep->smbios_minor_version = 4; ep->max_structure_size = max_structure_size; ep->entry_point_revision = 0; memcpy(ep->intermediate_anchor_string, "_DMI_", 5); ep->structure_table_length = structure_table_length; ep->structure_table_address = structure_table_address; ep->number_of_structures = number_of_structures; ep->smbios_bcd_revision = 0x24; sum = 0; for ( i = 0; i < 0x10; i++ ) sum += ((int8_t *)start)[i]; ep->checksum = -sum; sum = 0; for ( i = 0x10; i < ep->length; i++ ) sum += ((int8_t *)start)[i]; ep->intermediate_checksum = -sum; } /* Type 0 -- BIOS Information */ static void * smbios_type_0_init(void *start, const char *xen_version, uint32_t xen_major_version, uint32_t xen_minor_version) { struct smbios_type_0 *p = (struct smbios_type_0 *)start; static const char *smbios_release_date = __SMBIOS_DATE__; const char *s; void *pts; uint32_t length; pts = get_smbios_pt_struct(0, &length); if ( (pts != NULL)&&(length > 0) ) { memcpy(start, pts, length); p->header.handle = SMBIOS_HANDLE_TYPE0; return (start + length); } memset(p, 0, sizeof(*p)); p->header.type = 0; p->header.length = sizeof(struct smbios_type_0); p->header.handle = SMBIOS_HANDLE_TYPE0; p->vendor_str = 1; p->version_str = 2; p->starting_address_segment = 0xe800; p->release_date_str = 3; p->rom_size = 0; /* BIOS Characteristics. */ p->characteristics[0] = 0x80; /* PCI is supported */ p->characteristics[2] = 0x08; /* EDD is supported */ /* Extended Characteristics: Enable Targeted Content Distribution. */ p->characteristics_extension_bytes[1] = 0x04; p->major_release = (uint8_t) xen_major_version; p->minor_release = (uint8_t) xen_minor_version; p->embedded_controller_major = 0xff; p->embedded_controller_minor = 0xff; start += sizeof(struct smbios_type_0); s = xenstore_read(HVM_XS_BIOS_VENDOR, "Xen"); strcpy((char *)start, s); start += strlen(s) + 1; s = xenstore_read(HVM_XS_BIOS_VERSION, xen_version); strcpy((char *)start, s); start += strlen(s) + 1; strcpy((char *)start, smbios_release_date); start += strlen(smbios_release_date) + 1; *((uint8_t *)start) = 0; return start + 1; } /* Type 1 -- System Information */ static void * smbios_type_1_init(void *start, const char *xen_version, uint8_t uuid[16]) { char uuid_str[37]; struct smbios_type_1 *p = (struct smbios_type_1 *)start; const char *s; void *pts; uint32_t length; pts = get_smbios_pt_struct(1, &length); if ( (pts != NULL)&&(length > 0) ) { memcpy(start, pts, length); p->header.handle = SMBIOS_HANDLE_TYPE1; return (start + length); } memset(p, 0, sizeof(*p)); p->header.type = 1; p->header.length = sizeof(struct smbios_type_1); p->header.handle = SMBIOS_HANDLE_TYPE1; p->manufacturer_str = 1; p->product_name_str = 2; p->version_str = 3; p->serial_number_str = 4; memcpy(p->uuid, uuid, 16); p->wake_up_type = 0x06; /* power switch */ p->sku_str = 0; p->family_str = 0; start += sizeof(struct smbios_type_1); s = xenstore_read(HVM_XS_SYSTEM_MANUFACTURER, "Xen"); strcpy((char *)start, s); start += strlen(s) + 1; s = xenstore_read(HVM_XS_SYSTEM_PRODUCT_NAME, "HVM domU"); strcpy((char *)start, s); start += strlen(s) + 1; s = xenstore_read(HVM_XS_SYSTEM_VERSION, xen_version); strcpy((char *)start, s); start += strlen(s) + 1; uuid_to_string(uuid_str, uuid); s = xenstore_read(HVM_XS_SYSTEM_SERIAL_NUMBER, uuid_str); strcpy((char *)start, s); start += strlen(s) + 1; *((uint8_t *)start) = 0; return start+1; } /* Type 2 -- System Board */ static void * smbios_type_2_init(void *start) { struct smbios_type_2 *p = (struct smbios_type_2 *)start; uint8_t *ptr; void *pts; uint32_t length; pts = get_smbios_pt_struct(2, &length); if ( (pts != NULL)&&(length > 0) ) { memcpy(start, pts, length); p->header.handle = SMBIOS_HANDLE_TYPE2; /* Set current chassis handle if present */ if ( p->header.length > 13 ) { ptr = ((uint8_t*)start) + 11; if ( *((uint16_t*)ptr) != 0 ) *((uint16_t*)ptr) = SMBIOS_HANDLE_TYPE3; } return (start + length); } /* Only present when passed in */ return start; } /* Type 3 -- System Enclosure */ static void * smbios_type_3_init(void *start) { struct smbios_type_3 *p = (struct smbios_type_3 *)start; const char *s; void *pts; uint32_t length; pts = get_smbios_pt_struct(3, &length); if ( (pts != NULL)&&(length > 0) ) { memcpy(start, pts, length); p->header.handle = SMBIOS_HANDLE_TYPE3; return (start + length); } memset(p, 0, sizeof(*p)); p->header.type = 3; p->header.length = sizeof(struct smbios_type_3); p->header.handle = SMBIOS_HANDLE_TYPE3; p->manufacturer_str = 1; p->type = 0x01; /* other */ p->version_str = 0; p->serial_number_str = 0; p->asset_tag_str = 0; p->boot_up_state = 0x03; /* safe */ p->power_supply_state = 0x03; /* safe */ p->thermal_state = 0x03; /* safe */ p->security_status = 0x02; /* unknown */ start += sizeof(struct smbios_type_3); s = xenstore_read(HVM_XS_ENCLOSURE_MANUFACTURER, "Xen"); strcpy((char *)start, s); start += strlen(s) + 1; /* No internal defaults for this if the value is not set */ s = xenstore_read(HVM_XS_ENCLOSURE_SERIAL_NUMBER, NULL); if ( (s != NULL)&&(*s != '\0') ) { strcpy((char *)start, s); start += strlen(s) + 1; p->serial_number_str = 2; } *((uint8_t *)start) = 0; return start+1; } /* Type 4 -- Processor Information */ static void * smbios_type_4_init( void *start, unsigned int cpu_number, char *cpu_manufacturer) { char buf[80]; struct smbios_type_4 *p = (struct smbios_type_4 *)start; uint32_t eax, ebx, ecx, edx; memset(p, 0, sizeof(*p)); p->header.type = 4; p->header.length = sizeof(struct smbios_type_4); p->header.handle = SMBIOS_HANDLE_TYPE4 + cpu_number; p->socket_designation_str = 1; p->processor_type = 0x03; /* CPU */ p->processor_family = 0x01; /* other */ p->manufacturer_str = 2; cpuid(1, &eax, &ebx, &ecx, &edx); p->cpuid[0] = eax; p->cpuid[1] = edx; p->version_str = 0; p->voltage = 0; p->external_clock = 0; p->max_speed = p->current_speed = get_cpu_mhz(); p->status = 0x41; /* socket populated, CPU enabled */ p->upgrade = 0x01; /* other */ start += sizeof(struct smbios_type_4); strncpy(buf, "CPU ", sizeof(buf)); if ( (sizeof(buf) - strlen("CPU ")) >= 3 ) itoa(buf + strlen("CPU "), cpu_number); strcpy((char *)start, buf); start += strlen(buf) + 1; strcpy((char *)start, cpu_manufacturer); start += strlen(cpu_manufacturer) + 1; *((uint8_t *)start) = 0; return start+1; } /* Type 11 -- OEM Strings */ static void * smbios_type_11_init(void *start) { struct smbios_type_11 *p = (struct smbios_type_11 *)start; char path[20]; const char *s; int i; void *pts; uint32_t length; pts = get_smbios_pt_struct(11, &length); if ( (pts != NULL)&&(length > 0) ) { memcpy(start, pts, length); p->header.handle = SMBIOS_HANDLE_TYPE11; return (start + length); } p->header.type = 11; p->header.length = sizeof(struct smbios_type_11); p->header.handle = SMBIOS_HANDLE_TYPE11; p->count = 0; start += sizeof(struct smbios_type_11); /* Pull out as many oem-* strings we find in xenstore */ for ( i = 1; i < 100; i++ ) { snprintf(path, sizeof(path), HVM_XS_OEM_STRINGS, i); if ( ((s = xenstore_read(path, NULL)) == NULL) || (*s == '\0') ) break; strcpy((char *)start, s); start += strlen(s) + 1; p->count++; } /* Make sure there's at least one type-11 string */ if ( p->count == 0 ) { strcpy((char *)start, "Xen"); start += strlen("Xen") + 1; p->count++; } *((uint8_t *)start) = 0; return start+1; } /* Type 16 -- Physical Memory Array */ static void * smbios_type_16_init(void *start, uint32_t memsize, int nr_mem_devs) { struct smbios_type_16 *p = (struct smbios_type_16*)start; memset(p, 0, sizeof(*p)); p->header.type = 16; p->header.handle = SMBIOS_HANDLE_TYPE16; p->header.length = sizeof(struct smbios_type_16); p->location = 0x01; /* other */ p->use = 0x03; /* system memory */ p->error_correction = 0x06; /* Multi-bit ECC to make Microsoft happy */ p->maximum_capacity = memsize * 1024; p->memory_error_information_handle = 0xfffe; /* none provided */ p->number_of_memory_devices = nr_mem_devs; start += sizeof(struct smbios_type_16); *((uint16_t *)start) = 0; return start + 2; } /* Type 17 -- Memory Device */ static void * smbios_type_17_init(void *start, uint32_t memory_size_mb, int instance) { char buf[16]; struct smbios_type_17 *p = (struct smbios_type_17 *)start; memset(p, 0, sizeof(*p)); p->header.type = 17; p->header.length = sizeof(struct smbios_type_17); p->header.handle = SMBIOS_HANDLE_TYPE17 + instance; p->physical_memory_array_handle = 0x1000; p->total_width = 64; p->data_width = 64; ASSERT((memory_size_mb & ~0x7fff) == 0); p->size = memory_size_mb; p->form_factor = 0x09; /* DIMM */ p->device_set = 0; p->device_locator_str = 1; p->bank_locator_str = 0; p->memory_type = 0x07; /* RAM */ p->type_detail = 0; start += sizeof(struct smbios_type_17); strcpy(start, "DIMM "); start += strlen("DIMM "); itoa(buf, instance); strcpy(start, buf); start += strlen(buf) + 1; *((uint8_t *)start) = 0; return start+1; } /* Type 19 -- Memory Array Mapped Address */ static void * smbios_type_19_init(void *start, uint32_t memory_size_mb, int instance) { struct smbios_type_19 *p = (struct smbios_type_19 *)start; memset(p, 0, sizeof(*p)); p->header.type = 19; p->header.length = sizeof(struct smbios_type_19); p->header.handle = SMBIOS_HANDLE_TYPE19 + instance; p->starting_address = instance << 24; p->ending_address = p->starting_address + (memory_size_mb << 10) - 1; p->memory_array_handle = 0x1000; p->partition_width = 1; start += sizeof(struct smbios_type_19); *((uint16_t *)start) = 0; return start + 2; } /* Type 20 -- Memory Device Mapped Address */ static void * smbios_type_20_init(void *start, uint32_t memory_size_mb, int instance) { struct smbios_type_20 *p = (struct smbios_type_20 *)start; memset(p, 0, sizeof(*p)); p->header.type = 20; p->header.length = sizeof(struct smbios_type_20); p->header.handle = SMBIOS_HANDLE_TYPE20 + instance; p->starting_address = instance << 24; p->ending_address = p->starting_address + (memory_size_mb << 10) - 1; p->memory_device_handle = 0x1100 + instance; p->memory_array_mapped_address_handle = 0x1300 + instance; p->partition_row_position = 1; p->interleave_position = 0; p->interleaved_data_depth = 0; start += sizeof(struct smbios_type_20); *((uint16_t *)start) = 0; return start+2; } /* Type 22 -- Portable Battery */ static void * smbios_type_22_init(void *start) { struct smbios_type_22 *p = (struct smbios_type_22 *)start; static const char *smbios_release_date = __SMBIOS_DATE__; const char *s; void *pts; uint32_t length; pts = get_smbios_pt_struct(22, &length); if ( (pts != NULL)&&(length > 0) ) { memcpy(start, pts, length); p->header.handle = SMBIOS_HANDLE_TYPE22; return (start + length); } s = xenstore_read(HVM_XS_SMBIOS_DEFAULT_BATTERY, "0"); if ( strncmp(s, "1", 1) != 0 ) return start; memset(p, 0, sizeof(*p)); p->header.type = 22; p->header.length = sizeof(struct smbios_type_22); p->header.handle = SMBIOS_HANDLE_TYPE22; p->location_str = 1; p->manufacturer_str = 2; p->manufacturer_date_str = 3; p->serial_number_str = 0; p->device_name_str = 4; p->device_chemistry = 0x2; /* unknown */ p->device_capacity = 0; /* unknown */ p->device_voltage = 0; /* unknown */ p->sbds_version_number = 0; p->max_error = 0xff; /* unknown */ p->sbds_serial_number = 0; p->sbds_manufacturer_date = 0; p->sbds_device_chemistry = 0; p->design_capacity_multiplier = 0; p->oem_specific = 0; start += sizeof(struct smbios_type_22); strcpy((char *)start, "Primary"); start += strlen("Primary") + 1; s = xenstore_read(HVM_XS_BATTERY_MANUFACTURER, "Xen"); strcpy((char *)start, s); start += strlen(s) + 1; strcpy((char *)start, smbios_release_date); start += strlen(smbios_release_date) + 1; s = xenstore_read(HVM_XS_BATTERY_DEVICE_NAME, "XEN-VBAT"); strcpy((char *)start, s); start += strlen(s) + 1; *((uint8_t *)start) = 0; return start+1; } /* Type 32 -- System Boot Information */ static void * smbios_type_32_init(void *start) { struct smbios_type_32 *p = (struct smbios_type_32 *)start; memset(p, 0, sizeof(*p)); p->header.type = 32; p->header.length = sizeof(struct smbios_type_32); p->header.handle = SMBIOS_HANDLE_TYPE32; memset(p->reserved, 0, 6); p->boot_status = 0; /* no errors detected */ start += sizeof(struct smbios_type_32); *((uint16_t *)start) = 0; return start+2; } /* Type 39 -- Power Supply */ static void * smbios_type_39_init(void *start) { struct smbios_type_39 *p = (struct smbios_type_39 *)start; void *pts; uint32_t length; pts = get_smbios_pt_struct(39, &length); if ( (pts != NULL)&&(length > 0) ) { memcpy(start, pts, length); p->header.handle = SMBIOS_HANDLE_TYPE39; return (start + length); } /* Only present when passed in */ return start; } static void * smbios_type_vendor_oem_init(void *start) { uint32_t *sep = smbios_pt_addr; uint32_t total = 0; uint8_t *ptr; if ( sep == NULL ) return start; while ( total < smbios_pt_length ) { ptr = (uint8_t*)(sep + 1); if ( ptr[0] >= 128 ) { /* Vendor/OEM table, copy it in. Note the handle values cannot * be changed since it is unknown what is in each of these tables * but they could contain handle references to other tables. This * means a slight risk of collision with the tables above but that * would have to be dealt with on a case by case basis. */ memcpy(start, ptr, *sep); start += *sep; } total += (*sep + sizeof(uint32_t)); sep = (uint32_t*)(ptr + *sep); } return start; } /* Type 127 -- End of Table */ static void * smbios_type_127_init(void *start) { struct smbios_type_127 *p = (struct smbios_type_127 *)start; memset(p, 0, sizeof(*p)); p->header.type = 127; p->header.length = sizeof(struct smbios_type_127); p->header.handle = SMBIOS_HANDLE_TYPE127; start += sizeof(struct smbios_type_127); *((uint16_t *)start) = 0; return start + 2; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/smp.c0000664000175000017500000001020113256712137016347 0ustar smbsmb/* * smp.c: Secondary processor bringup and initialisation. * * Copyright (c) 2008, Citrix Systems, Inc. * * Authors: * Keir Fraser * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "util.h" #include "config.h" #include "apic_regs.h" #define AP_BOOT_EIP 0x1000 extern char ap_boot_start[], ap_boot_end[]; static int ap_callin, ap_cpuid; asm ( " .text \n" " .code16 \n" "ap_boot_start: .code16 \n" " mov %cs,%ax \n" " mov %ax,%ds \n" " lgdt gdt_desr-ap_boot_start\n" " xor %ax, %ax \n" " inc %ax \n" " lmsw %ax \n" " ljmpl $0x08,$1f \n" "gdt_desr: \n" " .word gdt_end - gdt - 1 \n" " .long gdt \n" "ap_boot_end: .code32 \n" "1: mov $0x10,%eax \n" " mov %eax,%ds \n" " mov %eax,%es \n" " mov %eax,%ss \n" " movl $stack_top,%esp \n" " movl %esp,%ebp \n" " call ap_start \n" "1: hlt \n" " jmp 1b \n" " \n" " .align 8 \n" "gdt: \n" " .quad 0x0000000000000000 \n" " .quad 0x00cf9a000000ffff \n" /* 0x08: Flat code segment */ " .quad 0x00cf92000000ffff \n" /* 0x10: Flat data segment */ "gdt_end: \n" " \n" " .bss \n" " .align 8 \n" "stack: \n" " .skip 0x4000 \n" "stack_top: \n" " .text \n" ); void ap_start(void); /* non-static avoids unused-function compiler warning */ /*static*/ void ap_start(void) { printf(" - CPU%d ... ", ap_cpuid); cacheattr_init(); printf("done.\n"); wmb(); ap_callin = 1; } static void lapic_wait_ready(void) { while ( lapic_read(APIC_ICR) & APIC_ICR_BUSY ) cpu_relax(); } static void boot_cpu(unsigned int cpu) { unsigned int icr2 = SET_APIC_DEST_FIELD(LAPIC_ID(cpu)); /* Initialise shared variables. */ ap_cpuid = cpu; ap_callin = 0; wmb(); /* Wake up the secondary processor: INIT-SIPI-SIPI... */ lapic_wait_ready(); lapic_write(APIC_ICR2, icr2); lapic_write(APIC_ICR, APIC_DM_INIT); lapic_wait_ready(); lapic_write(APIC_ICR2, icr2); lapic_write(APIC_ICR, APIC_DM_STARTUP | (AP_BOOT_EIP >> 12)); lapic_wait_ready(); lapic_write(APIC_ICR2, icr2); lapic_write(APIC_ICR, APIC_DM_STARTUP | (AP_BOOT_EIP >> 12)); lapic_wait_ready(); /* * Wait for the secondary processor to complete initialisation. * Do not touch shared resources meanwhile. */ while ( !ap_callin ) cpu_relax(); /* Take the secondary processor offline. */ lapic_write(APIC_ICR2, icr2); lapic_write(APIC_ICR, APIC_DM_INIT); lapic_wait_ready(); } void smp_initialise(void) { unsigned int i, nr_cpus = hvm_info->nr_vcpus; memcpy((void *)AP_BOOT_EIP, ap_boot_start, ap_boot_end - ap_boot_start); printf("Multiprocessor initialisation:\n"); ap_start(); for ( i = 1; i < nr_cpus; i++ ) boot_cpu(i); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/smbios_types.h0000664000175000017500000001556313256712137020315 0ustar smbsmb/* * smbios_types.h - data structure definitions for Xen HVM SMBIOS support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . * * Copyright (C) IBM Corporation, 2006 * * Authors: Andrew D. Ball * * See the SMBIOS 2.4 spec for more detail: * http://www.dmtf.org/standards/smbios/ */ #ifndef SMBIOS_TYPES_H #define SMBIOS_TYPES_H #include /* SMBIOS entry point -- must be written to a 16-bit aligned address between 0xf0000 and 0xfffff. */ struct smbios_entry_point { char anchor_string[4]; uint8_t checksum; uint8_t length; uint8_t smbios_major_version; uint8_t smbios_minor_version; uint16_t max_structure_size; uint8_t entry_point_revision; uint8_t formatted_area[5]; char intermediate_anchor_string[5]; uint8_t intermediate_checksum; uint16_t structure_table_length; uint32_t structure_table_address; uint16_t number_of_structures; uint8_t smbios_bcd_revision; } __attribute__ ((packed)); /* This goes at the beginning of every SMBIOS structure. */ struct smbios_structure_header { uint8_t type; uint8_t length; uint16_t handle; } __attribute__ ((packed)); /* SMBIOS type 0 - BIOS Information */ struct smbios_type_0 { struct smbios_structure_header header; uint8_t vendor_str; uint8_t version_str; uint16_t starting_address_segment; uint8_t release_date_str; uint8_t rom_size; uint8_t characteristics[8]; uint8_t characteristics_extension_bytes[2]; uint8_t major_release; uint8_t minor_release; uint8_t embedded_controller_major; uint8_t embedded_controller_minor; } __attribute__ ((packed)); /* SMBIOS type 1 - System Information */ struct smbios_type_1 { struct smbios_structure_header header; uint8_t manufacturer_str; uint8_t product_name_str; uint8_t version_str; uint8_t serial_number_str; uint8_t uuid[16]; uint8_t wake_up_type; uint8_t sku_str; uint8_t family_str; } __attribute__ ((packed)); /* SMBIOS type 2 - Base Board Information */ struct smbios_type_2 { struct smbios_structure_header header; uint8_t manufacturer_str; uint8_t product_name_str; uint8_t version_str; uint8_t serial_number_str; } __attribute__ ((packed)); /* SMBIOS type 3 - System Enclosure */ struct smbios_type_3 { struct smbios_structure_header header; uint8_t manufacturer_str; uint8_t type; uint8_t version_str; uint8_t serial_number_str; uint8_t asset_tag_str; uint8_t boot_up_state; uint8_t power_supply_state; uint8_t thermal_state; uint8_t security_status; } __attribute__ ((packed)); /* SMBIOS type 4 - Processor Information */ struct smbios_type_4 { struct smbios_structure_header header; uint8_t socket_designation_str; uint8_t processor_type; uint8_t processor_family; uint8_t manufacturer_str; uint32_t cpuid[2]; uint8_t version_str; uint8_t voltage; uint16_t external_clock; uint16_t max_speed; uint16_t current_speed; uint8_t status; uint8_t upgrade; } __attribute__ ((packed)); /* SMBIOS type 11 - OEM Strings */ struct smbios_type_11 { struct smbios_structure_header header; uint8_t count; } __attribute__ ((packed)); /* SMBIOS type 16 - Physical Memory Array * Associated with one type 17 (Memory Device). */ struct smbios_type_16 { struct smbios_structure_header header; uint8_t location; uint8_t use; uint8_t error_correction; uint32_t maximum_capacity; uint16_t memory_error_information_handle; uint16_t number_of_memory_devices; } __attribute__ ((packed)); /* SMBIOS type 17 - Memory Device * Associated with one type 19 */ struct smbios_type_17 { struct smbios_structure_header header; uint16_t physical_memory_array_handle; uint16_t memory_error_information_handle; uint16_t total_width; uint16_t data_width; uint16_t size; uint8_t form_factor; uint8_t device_set; uint8_t device_locator_str; uint8_t bank_locator_str; uint8_t memory_type; uint16_t type_detail; } __attribute__ ((packed)); /* SMBIOS type 19 - Memory Array Mapped Address */ struct smbios_type_19 { struct smbios_structure_header header; uint32_t starting_address; uint32_t ending_address; uint16_t memory_array_handle; uint8_t partition_width; } __attribute__ ((packed)); /* SMBIOS type 20 - Memory Device Mapped Address */ struct smbios_type_20 { struct smbios_structure_header header; uint32_t starting_address; uint32_t ending_address; uint16_t memory_device_handle; uint16_t memory_array_mapped_address_handle; uint8_t partition_row_position; uint8_t interleave_position; uint8_t interleaved_data_depth; } __attribute__ ((packed)); /* SMBIOS type 22 - Portable battery */ struct smbios_type_22 { struct smbios_structure_header header; uint8_t location_str; uint8_t manufacturer_str; uint8_t manufacturer_date_str; uint8_t serial_number_str; uint8_t device_name_str; uint8_t device_chemistry; uint16_t device_capacity; uint16_t device_voltage; uint8_t sbds_version_number; uint8_t max_error; uint16_t sbds_serial_number; uint16_t sbds_manufacturer_date; uint8_t sbds_device_chemistry; uint8_t design_capacity_multiplier; uint32_t oem_specific; } __attribute__ ((packed)); /* SMBIOS type 32 - System Boot Information */ struct smbios_type_32 { struct smbios_structure_header header; uint8_t reserved[6]; uint8_t boot_status; } __attribute__ ((packed)); /* SMBIOS type 39 - Power Supply */ struct smbios_type_39 { struct smbios_structure_header header; uint8_t power_unit_group; uint8_t location_str; uint8_t device_name_str; uint8_t manufacturer_str; uint8_t serial_number_str; uint8_t asset_tag_number_str; uint8_t model_part_number_str; uint8_t revision_level_str; uint16_t max_capacity; uint16_t characteristics; uint16_t input_voltage_probe_handle; uint16_t cooling_device_handle; uint16_t input_current_probe_handle; } __attribute__ ((packed)); /* SMBIOS type 127 -- End-of-table */ struct smbios_type_127 { struct smbios_structure_header header; } __attribute__ ((packed)); #endif /* SMBIOS_TYPES_H */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/seabios.c0000664000175000017500000001124613256712137017207 0ustar smbsmb/* * HVM SeaBIOS support. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005, International Business Machines Corporation. * Copyright (c) 2006, Keir Fraser, XenSource Inc. * Copyright (c) 2011, Citrix Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "config.h" #include "config-seabios.h" #include "util.h" #include "smbios_types.h" #include #include extern unsigned char dsdt_anycpu_qemu_xen[]; extern int dsdt_anycpu_qemu_xen_len; struct seabios_info { char signature[14]; /* XenHVMSeaBIOS\0 */ uint8_t length; /* Length of this struct */ uint8_t checksum; /* Set such that the sum over bytes 0..length == 0 */ /* * Physical address of an array of tables_nr elements. * * Each element is a 32 bit value contianing the physical address * of a BIOS table. */ uint32_t tables; uint32_t tables_nr; /* * Physical address of the e820 table, contains e820_nr entries. */ uint32_t e820; uint32_t e820_nr; } __attribute__ ((packed)); #define MAX_TABLES 4 static void seabios_setup_bios_info(void) { struct seabios_info *info = (void *)BIOS_INFO_PHYSICAL_ADDRESS; *info = (struct seabios_info) { .signature = "XenHVMSeaBIOS", .length = sizeof(*info) }; info->tables = (uint32_t)scratch_alloc(MAX_TABLES*sizeof(uint32_t), 0); } static void seabios_finish_bios_info(void) { struct seabios_info *info = (void *)BIOS_INFO_PHYSICAL_ADDRESS; uint32_t i; uint8_t checksum; checksum = 0; for ( i = 0; i < info->length; i++ ) checksum += ((uint8_t *)(info))[i]; info->checksum = -checksum; } static void add_table(uint32_t t) { struct seabios_info *info = (void *)BIOS_INFO_PHYSICAL_ADDRESS; uint32_t *ts = (uint32_t *)info->tables; ASSERT(info->tables_nr < MAX_TABLES); ts[info->tables_nr] = t; info->tables_nr++; } static void seabios_acpi_build_tables(void) { uint32_t rsdp = (uint32_t)scratch_alloc(sizeof(struct acpi_20_rsdp), 0); struct acpi_config config = { .dsdt_anycpu = dsdt_anycpu_qemu_xen, .dsdt_anycpu_len = dsdt_anycpu_qemu_xen_len, .dsdt_15cpu = NULL, .dsdt_15cpu_len = 0, }; hvmloader_acpi_build_tables(&config, rsdp); add_table(rsdp); } static void seabios_create_mp_tables(void) { add_table(create_mp_tables(NULL)); } static void seabios_create_smbios_tables(void) { uint32_t ep = (uint32_t)scratch_alloc(sizeof(struct smbios_entry_point), 0); hvm_write_smbios_tables(ep, 0UL, 0UL); add_table(ep); } static void seabios_create_pir_tables(void) { add_table(create_pir_tables()); } static void seabios_setup_e820(void) { struct seabios_info *info = (void *)BIOS_INFO_PHYSICAL_ADDRESS; struct e820entry *e820 = scratch_alloc(sizeof(struct e820entry)*16, 0); info->e820 = (uint32_t)e820; /* Upper boundary already checked by seabios_load(). */ BUG_ON(seabios_config.bios_address < 0x000c0000); /* SeaBIOS reserves memory in e820 as necessary so no low reservation. */ info->e820_nr = build_e820_table(e820, 0, seabios_config.bios_address); dump_e820_table(e820, info->e820_nr); } static void seabios_load(const struct bios_config *bios, void *bios_addr, uint32_t bios_length) { unsigned int bios_dest = 0x100000 - bios_length; BUG_ON(bios_dest + bios_length > HVMLOADER_PHYSICAL_ADDRESS); memcpy((void *)bios_dest, bios_addr, bios_length); seabios_config.bios_address = bios_dest; seabios_config.image_size = bios_length; } struct bios_config seabios_config = { .name = "SeaBIOS", .load_roms = NULL, .bios_load = seabios_load, .bios_info_setup = seabios_setup_bios_info, .bios_info_finish = seabios_finish_bios_info, .e820_setup = seabios_setup_e820, .acpi_build_tables = seabios_acpi_build_tables, .create_mp_tables = seabios_create_mp_tables, .create_smbios_tables = seabios_create_smbios_tables, .create_pir_tables = seabios_create_pir_tables, }; /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/mp_tables.c0000664000175000017500000002305013256712137017524 0ustar smbsmb/* * mp_tables.c: Dynamically writes MP table info into the ROMBIOS. * * In order to work with various VCPU counts, this code reads the VCPU count * for the HVM partition and creates the correct MP tables for the VCPU count * and places the information into a predetermined location set aside in the * ROMBIOS during build time. * * Please note that many of the values, such as the CPU's * family/model/stepping, are hard-coded based upon the values that were used * in the ROMBIOS and may need to be modified or calculated dynamically to * correspond with what an HVM guest's CPUID returns. * * Travis Betak, travis.betak@amd.com * Copyright (c) 2006, AMD. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include #include "config.h" /* number of non-processor MP table entries */ #define NR_NONPROC_ENTRIES 18 #define ENTRY_TYPE_PROCESSOR 0 #define ENTRY_TYPE_BUS 1 #define ENTRY_TYPE_IOAPIC 2 #define ENTRY_TYPE_IO_INTR 3 #define ENTRY_TYPE_LOCAL_INTR 4 #define CPU_FLAG_ENABLED 0x01 #define CPU_FLAG_BSP 0x02 /* TODO change this to correspond with what the guest's see's from CPUID */ #define CPU_SIG_FAMILY 0x06 #define CPU_SIG_MODEL 0x00 #define CPU_SIG_STEPPING 0x00 #define CPU_SIGNATURE ((CPU_SIG_FAMILY << 8) \ | (CPU_SIG_MODEL << 4) \ | (CPU_SIG_STEPPING)) #define CPU_FEATURE_FPU (1U << 0) #define CPU_FEATURE_MCE (1U << 7) #define CPU_FEATURE_CX8 (1U << 8) #define CPU_FEATURE_APIC (1U << 9) #define CPU_FEATURES (CPU_FEATURE_FPU | CPU_FEATURE_APIC) #define BUS_TYPE_LENGTH 6 #define BUS_TYPE_STR_ISA "ISA " #define BUS_ID_ISA 0 #define INTR_TYPE_INT 0 #define INTR_TYPE_NMI 1 #define INTR_TYPE_SMI 2 #define INTR_TYPE_EXTINT 3 #define INTR_MAX_NR 16 #include "util.h" /* * The following structures are defined in the MuliProcessor Specifiation v1.4 */ /* MP Floating Pointer Structure */ struct mp_floating_pointer_struct { uint8_t signature[4]; uint32_t mp_table; uint8_t length; uint8_t revision; uint8_t checksum; uint8_t feature[5]; }; /* MP Configuration Table */ struct mp_config_table { uint8_t signature[4]; uint16_t length; uint8_t revision; uint8_t checksum; uint8_t oem_id[8]; uint8_t vendor_id[12]; uint32_t oem_table; uint16_t oem_table_sz; uint16_t nr_entries; uint32_t lapic; uint16_t extended_length; uint8_t extended_checksum; uint8_t reserved; }; /* MP Processor Entry */ struct mp_proc_entry { uint8_t type; uint8_t lapic_id; uint8_t lapic_version; uint8_t cpu_flags; uint32_t cpu_signature; uint32_t feature_flags; uint8_t reserved[8]; }; /* MP Bus Entry */ struct mp_bus_entry { uint8_t type; uint8_t bus_id; uint8_t bus_type_str[6]; }; /* MP IOAPIC Entry */ struct mp_ioapic_entry { uint8_t type; uint8_t ioapic_id; uint8_t ioapic_version; uint8_t ioapic_flags; uint32_t ioapic_addr; }; /* MP IO Interrupt Entry */ struct mp_io_intr_entry { uint8_t type; uint8_t intr_type; uint16_t io_intr_flags; uint8_t src_bus_id; uint8_t src_bus_irq; uint8_t dst_ioapic_id; uint8_t dst_ioapic_intin; }; /* MP Local Interrupt Entry */ struct mp_local_intr_entry { uint8_t type; uint8_t intr_type; uint16_t local_intr_flags; uint8_t src_bus_id; uint8_t src_bus_irq; uint8_t dst_lapic_id; uint8_t dst_lapic_lintin; }; static void fill_mp_config_table(struct mp_config_table *mpct, int length) { int vcpu_nr, i; uint8_t checksum; vcpu_nr = hvm_info->nr_vcpus; /* fill in the MP configuration table signature, "PCMP" */ mpct->signature[0] = 'P'; mpct->signature[1] = 'C'; mpct->signature[2] = 'M'; mpct->signature[3] = 'P'; mpct->length = length; mpct->revision = 4; /* fill in the OEM ID string, "_HVMCPU_" */ mpct->oem_id[0] = '_'; mpct->oem_id[3] = 'M'; mpct->oem_id[6] = 'U'; mpct->oem_id[1] = 'H'; mpct->oem_id[4] = 'C'; mpct->oem_id[7] = '_'; mpct->oem_id[2] = 'V'; mpct->oem_id[5] = 'P'; /* fill in the Vendor ID string, "XEN " */ mpct->vendor_id[0] = 'X'; mpct->vendor_id[6] = ' '; mpct->vendor_id[1] = 'E'; mpct->vendor_id[7] = ' '; mpct->vendor_id[2] = 'N'; mpct->vendor_id[8] = ' '; mpct->vendor_id[3] = ' '; mpct->vendor_id[9] = ' '; mpct->vendor_id[4] = ' '; mpct->vendor_id[10] = ' '; mpct->vendor_id[5] = ' '; mpct->vendor_id[11] = ' '; mpct->oem_table = 0; mpct->oem_table_sz = 0; mpct->nr_entries = vcpu_nr + NR_NONPROC_ENTRIES; mpct->lapic = LAPIC_BASE_ADDRESS; mpct->extended_length = 0; mpct->extended_checksum = 0; /* Finally, fill in the checksum. */ mpct->checksum = checksum = 0; for ( i = 0; i < length; i++ ) checksum += ((uint8_t *)(mpct))[i]; mpct->checksum = -checksum; } /* fills in an MP processor entry for VCPU 'vcpu_id' */ static void fill_mp_proc_entry(struct mp_proc_entry *mppe, int vcpu_id) { mppe->type = ENTRY_TYPE_PROCESSOR; mppe->lapic_id = LAPIC_ID(vcpu_id); mppe->lapic_version = 0x11; mppe->cpu_flags = CPU_FLAG_ENABLED; if ( vcpu_id == 0 ) mppe->cpu_flags |= CPU_FLAG_BSP; mppe->cpu_signature = CPU_SIGNATURE; mppe->feature_flags = CPU_FEATURES; } /* fills in an MP bus entry of type 'type' and bus ID 'bus_id' */ static void fill_mp_bus_entry( struct mp_bus_entry *mpbe, int bus_id, const char *type) { int i; mpbe->type = ENTRY_TYPE_BUS; mpbe->bus_id = bus_id; for ( i = 0; i < BUS_TYPE_LENGTH; i++ ) mpbe->bus_type_str[i] = type[i]; /* FIXME length check? */ } /* fills in an MP IOAPIC entry for IOAPIC 'ioapic_id' */ static void fill_mp_ioapic_entry(struct mp_ioapic_entry *mpie) { mpie->type = ENTRY_TYPE_IOAPIC; mpie->ioapic_id = IOAPIC_ID; mpie->ioapic_version = ioapic_version; mpie->ioapic_flags = 1; /* enabled */ mpie->ioapic_addr = ioapic_base_address; } /* fill in the mp floating processor structure */ static void fill_mpfps(struct mp_floating_pointer_struct *mpfps, uint32_t mpct) { int i; uint8_t checksum; mpfps->signature[0] = '_'; mpfps->signature[1] = 'M'; mpfps->signature[2] = 'P'; mpfps->signature[3] = '_'; mpfps->mp_table = mpct; mpfps->length = 1; mpfps->revision = 4; mpfps->checksum = 0; for (i = 0; i < 5; ++i) mpfps->feature[i] = 0; /* compute the checksum for our new table */ checksum = 0; for ( i = 0; i < sizeof(struct mp_floating_pointer_struct); i++ ) checksum += ((uint8_t *)(mpfps))[i]; mpfps->checksum = -checksum; } /* create_mp_tables - creates MP tables for the guest based upon config data */ unsigned long create_mp_tables(void *_mpfps) { char *p; int vcpu_nr, i, length; void *base; struct mp_io_intr_entry *mpiie; struct mp_floating_pointer_struct *mpfps; vcpu_nr = hvm_info->nr_vcpus; printf("Creating MP tables ...\n"); if ( _mpfps == NULL ) { int sz; sz = sizeof(struct mp_floating_pointer_struct); sz += sizeof(struct mp_config_table); sz += sizeof(struct mp_proc_entry) * vcpu_nr; sz += sizeof(struct mp_bus_entry); sz += sizeof(struct mp_ioapic_entry); sz += sizeof(struct mp_io_intr_entry) * 16; _mpfps = mem_alloc(sz, 0); } mpfps = _mpfps; base = &mpfps[1]; p = base + sizeof(struct mp_config_table); for ( i = 0; i < vcpu_nr; i++ ) { fill_mp_proc_entry((struct mp_proc_entry *)p, i); p += sizeof(struct mp_proc_entry); } fill_mp_bus_entry((struct mp_bus_entry *)p, BUS_ID_ISA, BUS_TYPE_STR_ISA); p += sizeof(struct mp_bus_entry); fill_mp_ioapic_entry((struct mp_ioapic_entry *)p); p += sizeof(struct mp_ioapic_entry); /* I/O interrupt assignment: IOAPIC pin 0 is connected to 8259 ExtInt. */ mpiie = (struct mp_io_intr_entry *)p; memset(mpiie, 0, sizeof(*mpiie)); mpiie->type = ENTRY_TYPE_IO_INTR; mpiie->intr_type = INTR_TYPE_EXTINT; mpiie->dst_ioapic_id = IOAPIC_ID; p += sizeof(*mpiie); /* I/O interrupt assignment for every legacy 8259 interrupt source. */ for ( i = 0; i < 16; i++ ) { if ( i == 2 ) continue; /* skip the slave PIC connection */ mpiie = (struct mp_io_intr_entry *)p; mpiie->type = ENTRY_TYPE_IO_INTR; mpiie->intr_type = INTR_TYPE_INT; mpiie->io_intr_flags = (PCI_ISA_IRQ_MASK & (1U << i)) ? 0xf : 0x0; mpiie->src_bus_id = BUS_ID_ISA; mpiie->src_bus_irq = i; mpiie->dst_ioapic_id = IOAPIC_ID; mpiie->dst_ioapic_intin = (i == 0) ? 2 : i; p += sizeof(*mpiie); } length = p - (char *)base; fill_mp_config_table((struct mp_config_table *)base, length); fill_mpfps(mpfps, (uint32_t)base); return (unsigned long)mpfps; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/vnuma.c0000664000175000017500000000533413256712137016711 0ustar smbsmb/* * vnuma.c: obtain vNUMA information from hypervisor * * Copyright (c) 2014 Wei Liu, Citrix Systems (R&D) Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "util.h" #include "hypercall.h" #include "vnuma.h" unsigned int nr_vnodes, nr_vmemranges; unsigned int *vcpu_to_vnode, *vdistance; xen_vmemrange_t *vmemrange; void init_vnuma_info(void) { int rc; struct xen_vnuma_topology_info vnuma_topo = { .domid = DOMID_SELF }; rc = hypercall_memory_op(XENMEM_get_vnumainfo, &vnuma_topo); if ( rc != -ENOBUFS ) return; ASSERT(vnuma_topo.nr_vcpus == hvm_info->nr_vcpus); vcpu_to_vnode = scratch_alloc(sizeof(*vcpu_to_vnode) * hvm_info->nr_vcpus, 0); vdistance = scratch_alloc(sizeof(uint32_t) * vnuma_topo.nr_vnodes * vnuma_topo.nr_vnodes, 0); vmemrange = scratch_alloc(sizeof(xen_vmemrange_t) * vnuma_topo.nr_vmemranges, 0); set_xen_guest_handle(vnuma_topo.vdistance.h, vdistance); set_xen_guest_handle(vnuma_topo.vcpu_to_vnode.h, vcpu_to_vnode); set_xen_guest_handle(vnuma_topo.vmemrange.h, vmemrange); rc = hypercall_memory_op(XENMEM_get_vnumainfo, &vnuma_topo); if ( rc < 0 ) { printf("Failed to retrieve vNUMA information, rc = %d\n", rc); return; } nr_vnodes = vnuma_topo.nr_vnodes; nr_vmemranges = vnuma_topo.nr_vmemranges; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/ovmf.c0000664000175000017500000001176413256712137016536 0ustar smbsmb/* * HVM OVMF UEFI support. * * Bei Guan, gbtju85@gmail.com * Andrei Warkentin, andreiw@motorola.com * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005, International Business Machines Corporation. * Copyright (c) 2006, Keir Fraser, XenSource Inc. * Copyright (c) 2011, Citrix Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "config.h" #include "smbios_types.h" #include "libacpi.h" #include "apic_regs.h" #include "../rombios/config.h" #include "util.h" #include "pci_regs.h" #include "hypercall.h" #include #include #include #define OVMF_MAXOFFSET 0x000FFFFFULL #define OVMF_END 0x100000000ULL #define LOWCHUNK_BEGIN 0x000F0000 #define LOWCHUNK_SIZE 0x00010000 #define LOWCHUNK_MAXOFFSET 0x0000FFFF #define OVMF_INFO_PHYSICAL_ADDRESS 0x00001000 extern unsigned char dsdt_anycpu_qemu_xen[]; extern int dsdt_anycpu_qemu_xen_len; #define OVMF_INFO_MAX_TABLES 4 struct ovmf_info { char signature[14]; /* XenHVMOVMF\0\0\0\0 */ uint8_t length; /* Length of this struct */ uint8_t checksum; /* Set such that the sum over bytes 0..length == 0 */ /* * Physical address of an array of tables_nr elements. * * Each element is a 64 bit value containing the physical address * of a BIOS table. */ uint64_t tables; uint32_t tables_nr; /* * Physical address of the e820 table, contains e820_nr entries. */ uint64_t e820; uint32_t e820_nr; } __attribute__ ((packed)); static void ovmf_setup_bios_info(void) { struct ovmf_info *info = (void *)OVMF_INFO_PHYSICAL_ADDRESS; *info = (struct ovmf_info) { .signature = "XenHVMOVMF", .length = sizeof(*info) }; } static void ovmf_finish_bios_info(void) { struct ovmf_info *info = (void *)OVMF_INFO_PHYSICAL_ADDRESS; uint32_t i; uint8_t checksum; checksum = 0; for ( i = 0; i < info->length; i++ ) checksum += ((uint8_t *)(info))[i]; info->checksum = -checksum; } static void ovmf_load(const struct bios_config *config, void *bios_addr, uint32_t bios_length) { xen_pfn_t mfn; uint64_t addr = OVMF_END - ((bios_length + OVMF_MAXOFFSET) & ~OVMF_MAXOFFSET); uint64_t ovmf_end = addr + bios_length; ovmf_config.bios_address = addr; ovmf_config.image_size = bios_length; /* Copy low-reset vector portion. */ memcpy((void *)LOWCHUNK_BEGIN, (uint8_t *)bios_addr + bios_length - LOWCHUNK_SIZE, LOWCHUNK_SIZE); /* Ensure we have backing page prior to moving FD. */ while ( (addr >> PAGE_SHIFT) != (ovmf_end >> PAGE_SHIFT) ) { mfn = (uint32_t) (addr >> PAGE_SHIFT); addr += PAGE_SIZE; mem_hole_populate_ram(mfn, 1); } /* Check that source and destination does not overlaps. */ BUG_ON(addr + bios_length > (unsigned)bios_addr && addr < (unsigned)bios_addr + bios_length); /* Copy FD. */ memcpy((void *)ovmf_config.bios_address, bios_addr, bios_length); } static void ovmf_acpi_build_tables(void) { struct acpi_config config = { .dsdt_anycpu = dsdt_anycpu_qemu_xen, .dsdt_anycpu_len = dsdt_anycpu_qemu_xen_len, .dsdt_15cpu = NULL, .dsdt_15cpu_len = 0 }; hvmloader_acpi_build_tables(&config, ACPI_PHYSICAL_ADDRESS); } static void ovmf_create_smbios_tables(void) { hvm_write_smbios_tables( SMBIOS_PHYSICAL_ADDRESS, SMBIOS_PHYSICAL_ADDRESS + sizeof(struct smbios_entry_point), SMBIOS_PHYSICAL_END); } static void ovmf_setup_e820(void) { struct ovmf_info *info = (void *)OVMF_INFO_PHYSICAL_ADDRESS; struct e820entry *e820 = scratch_alloc(sizeof(struct e820entry)*16, 0); info->e820 = (uint32_t)e820; /* Reserve LOWCHUNK_BEGIN to 0x100000 as well, that's reset vector. */ info->e820_nr = build_e820_table(e820, 0, LOWCHUNK_BEGIN); dump_e820_table(e820, info->e820_nr); } struct bios_config ovmf_config = { .name = "OVMF", .bios_load = ovmf_load, .load_roms = 0, .bios_info_setup = ovmf_setup_bios_info, .bios_info_finish = ovmf_finish_bios_info, .e820_setup = ovmf_setup_e820, .acpi_build_tables = ovmf_acpi_build_tables, .create_mp_tables = NULL, .create_smbios_tables = ovmf_create_smbios_tables, .create_pir_tables = NULL, }; /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/ctype.h0000664000175000017500000000211413256712137016705 0ustar smbsmb#ifndef __HVMLOADER_CTYPE_H__ #define __HVMLOADER_CTYPE_H__ #define _U 0x01 /* upper */ #define _L 0x02 /* lower */ #define _D 0x04 /* digit */ #define _C 0x08 /* cntrl */ #define _P 0x10 /* punct */ #define _S 0x20 /* white space (space/lf/tab) */ #define _X 0x40 /* hex digit */ #define _SP 0x80 /* hard space (0x20) */ extern const unsigned char _ctype[]; #define __ismask(x) (_ctype[(int)(unsigned char)(x)]) #define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0) #define isalpha(c) ((__ismask(c)&(_U|_L)) != 0) #define iscntrl(c) ((__ismask(c)&(_C)) != 0) #define isdigit(c) ((__ismask(c)&(_D)) != 0) #define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0) #define islower(c) ((__ismask(c)&(_L)) != 0) #define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0) #define ispunct(c) ((__ismask(c)&(_P)) != 0) #define isspace(c) ((__ismask(c)&(_S)) != 0) #define isupper(c) ((__ismask(c)&(_U)) != 0) #define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0) #endif /* __HVMLOADER_CTYPE_H__ */ xen-4.9.2/tools/firmware/hvmloader/tests.c0000664000175000017500000002237013256712137016724 0ustar smbsmb/* * tests.c: HVM environment tests. * * Copyright (c) 2008, Citrix Systems, Inc. * * Authors: * Keir Fraser * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "config.h" #include "util.h" #include #define TEST_FAIL 0 #define TEST_PASS 1 #define TEST_SKIP 2 /* * Memory layout during tests: * 4MB to 8MB is cleared. * Page directory resides at 4MB. * 2 page table pages reside at 4MB+4kB to 4MB+12kB. * Pagetables identity-map 0-8MB, except 4kB at va 6MB maps to pa 5MB. */ #define TEST_MEM_BASE (4ul << 20) #define TEST_MEM_SIZE (4ul << 20) #define PD_START TEST_MEM_BASE #define PT_START (PD_START + 4096) static void setup_paging(void) { uint32_t *pd = (uint32_t *)PD_START; uint32_t *pt = (uint32_t *)PT_START; uint32_t i; /* Identity map 0-8MB. */ for ( i = 0; i < 2; i++ ) pd[i] = (unsigned long)pt + (i<<12) + 3; for ( i = 0; i < 2 * 1024; i++ ) pt[i] = (i << 12) + 3; /* Page at virtual 6MB maps to physical 5MB. */ pt[6u<<8] -= 0x100000u; } static void start_paging(void) { asm volatile ( "mov %%eax,%%cr3; mov %%cr0,%%eax; " "orl $0x80000000,%%eax; mov %%eax,%%cr0; " "jmp 1f; 1:" : : "a" (PD_START) : "memory" ); } static void stop_paging(void) { asm volatile ( "mov %%cr0,%%eax; andl $0x7fffffff,%%eax; mov %%eax,%%cr0; " "jmp 1f; 1:" : : : "eax", "memory" ); } /* * rep_io_test: Tests REP INSB both forwards and backwards (EF.DF={0,1}) across * a discontiguous page boundary. */ static int rep_io_test(void) { uint32_t *p; uint32_t i, p0, p1, p2; int okay = TEST_PASS; static const struct { unsigned long addr; uint32_t expected; } check[] = { { 0x00500000, 0x987654ff }, { 0x00500ffc, 0xff000000 }, { 0x005ffffc, 0xff000000 }, { 0x00601000, 0x000000ff }, { 0, 0 } }; start_paging(); /* Phys 5MB = 0xdeadbeef */ *(uint32_t *)0x500000ul = 0xdeadbeef; /* Phys 5MB = 0x98765432 */ *(uint32_t *)0x600000ul = 0x98765432; /* Phys 0x5fffff = Phys 0x500000 = 0xff (byte) */ asm volatile ( "rep insb" : "=d" (p0), "=c" (p1), "=D" (p2) : "0" (0x5f), "1" (2), "2" (0x5ffffful) : "memory" ); /* Phys 0x500fff = Phys 0x601000 = 0xff (byte) */ asm volatile ( "std ; rep insb ; cld" : "=d" (p0), "=c" (p1), "=D" (p2) : "0" (0x5f), "1" (2), "2" (0x601000ul) : "memory" ); stop_paging(); i = 0; for ( p = (uint32_t *)0x4ff000ul; p < (uint32_t *)0x602000ul; p++ ) { uint32_t expected = 0; if ( check[i].addr == (unsigned long)p ) { expected = check[i].expected; i++; } if ( *p != expected ) { printf("Bad value at 0x%08lx: saw %08x expected %08x\n", (unsigned long)p, *p, expected); okay = TEST_FAIL; } } return okay; } static int shadow_gs_test(void) { uint64_t *pd = (uint64_t *)PD_START; uint32_t i, eax, ebx, ecx, edx; /* Skip this test if the CPU does not support long mode. */ cpuid(0x80000000, &eax, &ebx, &ecx, &edx); if ( eax < 0x80000001 ) return TEST_SKIP; cpuid(0x80000001, &eax, &ebx, &ecx, &edx); if ( !(edx & (1u<<29)) ) return TEST_SKIP; /* Long mode pagetable setup: Identity map 0-8MB with 2MB mappings. */ *pd = (unsigned long)pd + 0x1007; /* Level 4 */ pd += 512; *pd = (unsigned long)pd + 0x1007; /* Level 3 */ pd += 512; for ( i = 0; i < 4; i++ ) /* Level 2 */ *pd++ = (i << 21) + 0x1e3; asm volatile ( /* CR4.PAE=1 */ "mov $0x20,%%ebx; " "mov %%ebx,%%cr4; " /* CR3 */ "mov %%eax,%%cr3; " /* EFER.LME=1 */ "mov $0xc0000080,%%ecx; rdmsr; btsl $8,%%eax; wrmsr; " /* CR0.PG=1 */ "mov %%cr0,%%eax; btsl $31,%%eax; mov %%eax,%%cr0; " "jmp 1f; 1: " /* GS_BASE=2; SHADOW_GS_BASE=3 */ "mov $0xc0000101,%%ecx; xor %%edx,%%edx; mov $2,%%eax; wrmsr; " "mov $0xc0000102,%%ecx; xor %%edx,%%edx; mov $3,%%eax; wrmsr; " /* Push LRETQ stack frame. */ "pushl $0; pushl $"STR(SEL_CODE32)"; pushl $0; pushl $2f; " /* Jump to 64-bit mode. */ "ljmp $"STR(SEL_CODE64)",$1f; 1: " /* Swap GS_BASE and SHADOW_GS_BASE */ ".byte 0x0f,0x01,0xf8; " /* SWAPGS */ /* Jump to 32-bit mode. */ ".byte 0x89, 0xe4; " /* MOV ESP,ESP */ ".byte 0x48, 0xcb; 2: " /* LRETQ */ /* Read SHADOW_GS_BASE: should now contain 2 */ "mov $0xc0000102,%%ecx; rdmsr; mov %%eax,%%ebx; " /* CR0.PG=0 */ "mov %%cr0,%%eax; btcl $31,%%eax; mov %%eax,%%cr0; " "jmp 1f; 1:" /* EFER.LME=0 */ "mov $0xc0000080,%%ecx; rdmsr; btcl $8,%%eax; wrmsr; " /* CR4.PAE=0 */ "xor %%eax,%%eax; mov %%eax,%%cr4; " : "=b" (ebx) : "a" (PD_START) : "ecx", "edx", "memory" ); return (ebx == 2) ? TEST_PASS : TEST_FAIL; } void perform_tests(void) { unsigned int i, passed, skipped; static struct { int (* const test)(void); const char *description; } tests[] = { { rep_io_test, "REP INSB across page boundaries" }, { shadow_gs_test, "GS base MSRs and SWAPGS" }, { NULL, NULL } }; printf("Testing HVM environment:\n"); BUILD_BUG_ON(SCRATCH_PHYSICAL_ADDRESS > HVMLOADER_PHYSICAL_ADDRESS); if ( hvm_info->low_mem_pgend < ((TEST_MEM_BASE + TEST_MEM_SIZE) >> PAGE_SHIFT) ) { printf("Skipping tests due to insufficient memory (<%luMB)\n", (TEST_MEM_BASE + TEST_MEM_SIZE) >> 20); return; } if ( (unsigned long)_end > TEST_MEM_BASE ) { printf("Skipping tests due to overlap with base image\n"); return; } if ( hvm_start_info->cmdline_paddr && hvm_start_info->cmdline_paddr < TEST_MEM_BASE + TEST_MEM_SIZE && ((hvm_start_info->cmdline_paddr + strlen((char *)(uintptr_t)hvm_start_info->cmdline_paddr)) >= TEST_MEM_BASE) ) { printf("Skipping tests due to overlap with command line\n"); return; } if ( hvm_start_info->rsdp_paddr ) { printf("Skipping tests due to non-zero RSDP address\n"); return; } if ( hvm_start_info->nr_modules ) { const struct hvm_modlist_entry *modlist = (void *)(uintptr_t)hvm_start_info->modlist_paddr; if ( hvm_start_info->modlist_paddr > UINTPTR_MAX || ((UINTPTR_MAX - (uintptr_t)modlist) / sizeof(*modlist) < hvm_start_info->nr_modules) ) { printf("Skipping tests due to inaccessible module list\n"); return; } if ( TEST_MEM_BASE < (uintptr_t)(modlist + hvm_start_info->nr_modules) && (uintptr_t)modlist < TEST_MEM_BASE + TEST_MEM_SIZE ) { printf("Skipping tests due to overlap with module list\n"); return; } for ( i = 0; i < hvm_start_info->nr_modules; ++i ) { if ( TEST_MEM_BASE < modlist[i].paddr + modlist[i].size && modlist[i].paddr < TEST_MEM_BASE + TEST_MEM_SIZE ) { printf("Skipping tests due to overlap with module %u\n", i); return; } if ( modlist[i].cmdline_paddr && modlist[i].cmdline_paddr < TEST_MEM_BASE + TEST_MEM_SIZE && ((modlist[i].cmdline_paddr + strlen((char *)(uintptr_t)modlist[i].cmdline_paddr)) >= TEST_MEM_BASE) ) { printf("Skipping tests due to overlap with module %u cmdline\n", i); return; } } } passed = skipped = 0; for ( i = 0; tests[i].test; i++ ) { printf(" - %s ... ", tests[i].description); memset((char *)(4ul << 20), 0, 4ul << 20); setup_paging(); switch ( (*tests[i].test)() ) { case TEST_PASS: printf("passed\n"); passed++; break; case TEST_FAIL: printf("failed\n"); break; case TEST_SKIP: printf("skipped\n"); skipped++; break; } } printf("Passed %d of %d tests\n", passed, i); if ( skipped != 0 ) printf("Skipped %d of %d tests\n", skipped, i); if ( (passed + skipped) != i ) { printf("FAILED %d of %d tests\n", i - passed - skipped, i); BUG(); } } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/hvmloader.c0000664000175000017500000003272013256712137017543 0ustar smbsmb/* * hvmloader.c: HVM bootloader. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005, International Business Machines Corporation. * * Copyright (c) 2006, Keir Fraser, XenSource Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "util.h" #include "hypercall.h" #include "config.h" #include "pci_regs.h" #include "apic_regs.h" #include "vnuma.h" #include #include #include #include const struct hvm_start_info *hvm_start_info; asm ( " .text \n" " .globl _start \n" "_start: \n" /* C runtime kickoff. */ " cld \n" " cli \n" " lgdt gdt_desr \n" " mov $"STR(SEL_DATA32)",%ax \n" " mov %ax,%ds \n" " mov %ax,%es \n" " mov %ax,%fs \n" " mov %ax,%gs \n" " mov %ax,%ss \n" " ljmp $"STR(SEL_CODE32)",$1f \n" "1: movl $stack_top,%esp \n" " movl %esp,%ebp \n" /* store HVM start info ptr */ " mov %ebx, hvm_start_info \n" " call main \n" /* Relocate real-mode trampoline to 0x0. */ " mov $trampoline_start,%esi \n" " xor %edi,%edi \n" " mov $trampoline_end,%ecx \n" " sub %esi,%ecx \n" " rep movsb \n" /* Load real-mode compatible segment state (base 0x0000, limit 0xffff). */ " mov $"STR(SEL_DATA16)",%ax \n" " mov %ax,%ds \n" " mov %ax,%es \n" " mov %ax,%fs \n" " mov %ax,%gs \n" " mov %ax,%ss \n" /* Initialise all 32-bit GPRs to zero. */ " xor %eax,%eax \n" " xor %ebx,%ebx \n" " xor %ecx,%ecx \n" " xor %edx,%edx \n" " xor %esp,%esp \n" " xor %ebp,%ebp \n" " xor %esi,%esi \n" " xor %edi,%edi \n" /* Enter real mode, reload all segment registers and IDT. */ " ljmp $"STR(SEL_CODE16)",$0x0\n" "trampoline_start: .code16 \n" " mov %eax,%cr0 \n" " ljmp $0,$1f-trampoline_start\n" "1: mov %ax,%ds \n" " mov %ax,%es \n" " mov %ax,%fs \n" " mov %ax,%gs \n" " mov %ax,%ss \n" " lidt 1f-trampoline_start \n" " ljmp $0xf000,$0xfff0 \n" "1: .word 0x3ff,0,0 \n" "trampoline_end: .code32 \n" " \n" "gdt_desr: \n" " .word gdt_end - gdt - 1 \n" " .long gdt \n" " \n" " .align 8 \n" "gdt: \n" " .quad 0x0000000000000000 \n" " .quad 0x008f9a000000ffff \n" /* Ring 0 16b code, base 0 limit 4G */ " .quad 0x008f92000000ffff \n" /* Ring 0 16b data, base 0 limit 4G */ " .quad 0x00cf9a000000ffff \n" /* Ring 0 32b code, base 0 limit 4G */ " .quad 0x00cf92000000ffff \n" /* Ring 0 32b data, base 0 limit 4G */ " .quad 0x00af9a000000ffff \n" /* Ring 0 64b code */ "gdt_end: \n" " \n" " .bss \n" " .align 8 \n" "stack: \n" " .skip 0x4000 \n" "stack_top: \n" " .text \n" ); unsigned long scratch_start = SCRATCH_PHYSICAL_ADDRESS; uint32_t ioapic_base_address = 0xfec00000; uint8_t ioapic_version; static void init_hypercalls(void) { uint32_t eax, ebx, ecx, edx; unsigned long i; char signature[13]; xen_extraversion_t extraversion; uint32_t base; for ( base = 0x40000000; base < 0x40010000; base += 0x100 ) { cpuid(base, &eax, &ebx, &ecx, &edx); *(uint32_t *)(signature + 0) = ebx; *(uint32_t *)(signature + 4) = ecx; *(uint32_t *)(signature + 8) = edx; signature[12] = '\0'; if ( !strcmp("XenVMMXenVMM", signature) ) break; } BUG_ON(strcmp("XenVMMXenVMM", signature) || ((eax - base) < 2)); /* Fill in hypercall transfer pages. */ cpuid(base + 2, &eax, &ebx, &ecx, &edx); for ( i = 0; i < eax; i++ ) wrmsr(ebx, HYPERCALL_PHYSICAL_ADDRESS + (i << 12) + i); /* Print version information. */ cpuid(base + 1, &eax, &ebx, &ecx, &edx); hypercall_xen_version(XENVER_extraversion, extraversion); printf("Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion); } /* Replace possibly erroneous memory-size CMOS fields with correct values. */ static void cmos_write_memory_size(void) { uint32_t base_mem = 640, ext_mem, alt_mem; alt_mem = ext_mem = hvm_info->low_mem_pgend << PAGE_SHIFT; ext_mem = (ext_mem > 0x0100000) ? (ext_mem - 0x0100000) >> 10 : 0; if ( ext_mem > 0xffff ) ext_mem = 0xffff; alt_mem = (alt_mem > 0x1000000) ? (alt_mem - 0x1000000) >> 16 : 0; /* All BIOSes: conventional memory (CMOS *always* reports 640kB). */ cmos_outb(0x15, (uint8_t)(base_mem >> 0)); cmos_outb(0x16, (uint8_t)(base_mem >> 8)); /* All BIOSes: extended memory (1kB chunks above 1MB). */ cmos_outb(0x17, (uint8_t)( ext_mem >> 0)); cmos_outb(0x18, (uint8_t)( ext_mem >> 8)); cmos_outb(0x30, (uint8_t)( ext_mem >> 0)); cmos_outb(0x31, (uint8_t)( ext_mem >> 8)); /* Some BIOSes: alternative extended memory (64kB chunks above 16MB). */ cmos_outb(0x34, (uint8_t)( alt_mem >> 0)); cmos_outb(0x35, (uint8_t)( alt_mem >> 8)); } /* * Set up an empty TSS area for virtual 8086 mode to use. Its content is * going to be managed by Xen, but zero fill it just in case. */ static void init_vm86_tss(void) { /* * Have the TSS cover the ISA port range, which makes it * - 104 bytes base structure * - 32 bytes interrupt redirection bitmap * - 128 bytes I/O bitmap * - one trailing byte * or a total of to 265 bytes. As it needs to be a multiple of the requested * alignment, this ends up requiring 384 bytes. */ #define TSS_SIZE (3 * 128) void *tss; tss = mem_alloc(TSS_SIZE, 128); memset(tss, 0, TSS_SIZE); hvm_param_set(HVM_PARAM_VM86_TSS_SIZED, ((uint64_t)TSS_SIZE << 32) | virt_to_phys(tss)); printf("vm86 TSS at %08lx\n", virt_to_phys(tss)); #undef TSS_SIZE } static void apic_setup(void) { /* * This would the The Right Thing To Do (tm), if only qemu negotiated * with Xen where the IO-APIC actually sits (which is currently hard * coded in Xen and can't be controlled externally). Uncomment this code * once that changed. ioapic_base_address |= (pci_readb(PCI_ISA_DEVFN, 0x80) & 0x3f) << 10; */ ioapic_version = ioapic_read(0x01) & 0xff; /* Set the IOAPIC ID to the static value used in the MP/ACPI tables. */ ioapic_write(0x00, IOAPIC_ID); /* NMIs are delivered direct to the BSP. */ lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | 0xFF); lapic_write(APIC_LVT0, (APIC_MODE_EXTINT << 8) | APIC_LVT_MASKED); lapic_write(APIC_LVT1, APIC_MODE_NMI << 8); /* 8259A ExtInts are delivered through IOAPIC pin 0 (Virtual Wire Mode). */ ioapic_write(0x10, APIC_DM_EXTINT); ioapic_write(0x11, SET_APIC_ID(LAPIC_ID(0))); } struct bios_info { const char *key; const struct bios_config *bios; } bios_configs[] = { #ifdef ENABLE_ROMBIOS { "rombios", &rombios_config, }, #endif { "seabios", &seabios_config, }, { "ovmf", &ovmf_config, }, { NULL, NULL } }; static const struct bios_config *detect_bios(void) { const struct bios_info *b; const char *bios; bios = xenstore_read("hvmloader/bios", "rombios"); for ( b = &bios_configs[0]; b->key != NULL; b++ ) if ( !strcmp(bios, b->key) ) return b->bios; printf("Unknown BIOS %s, no ROM image found\n", bios); BUG(); return NULL; } static void acpi_enable_sci(void) { uint8_t pm1a_cnt_val; #define PIIX4_SMI_CMD_IOPORT 0xb2 #define PIIX4_ACPI_ENABLE 0xf1 /* * PIIX4 emulation in QEMU has SCI_EN=0 by default. We have no legacy * SMM implementation, so give ACPI control to the OSPM immediately. */ pm1a_cnt_val = inb(ACPI_PM1A_CNT_BLK_ADDRESS_V1); if ( !(pm1a_cnt_val & ACPI_PM1C_SCI_EN) ) outb(PIIX4_SMI_CMD_IOPORT, PIIX4_ACPI_ENABLE); pm1a_cnt_val = inb(ACPI_PM1A_CNT_BLK_ADDRESS_V1); BUG_ON(!(pm1a_cnt_val & ACPI_PM1C_SCI_EN)); } const struct hvm_modlist_entry *get_module_entry( const struct hvm_start_info *info, const char *name) { const struct hvm_modlist_entry *modlist = (struct hvm_modlist_entry *)(uintptr_t)info->modlist_paddr; unsigned int i; if ( !modlist || info->modlist_paddr > UINTPTR_MAX || (UINTPTR_MAX - (uintptr_t)info->modlist_paddr) / sizeof(*modlist) < info->nr_modules ) return NULL; for ( i = 0; i < info->nr_modules; i++ ) { char *module_name = (char*)(uintptr_t)modlist[i].cmdline_paddr; /* Skip if the module or its cmdline is missing. */ if ( !module_name || !modlist[i].paddr ) continue; /* Skip if the cmdline cannot be read. */ if ( modlist[i].cmdline_paddr > UINTPTR_MAX || (modlist[i].cmdline_paddr + strlen(name)) > UINTPTR_MAX ) continue; if ( !strcmp(name, module_name) ) { if ( modlist[i].paddr > UINTPTR_MAX || modlist[i].size > UINTPTR_MAX || (modlist[i].paddr + modlist[i].size - 1) > UINTPTR_MAX ) { printf("Cannot load \"%s\" from 0x"PRIllx" (0x"PRIllx")\n", name, PRIllx_arg(modlist[i].paddr), PRIllx_arg(modlist[i].size)); BUG(); } return &modlist[i]; } } return NULL; } int main(void) { const struct bios_config *bios; int acpi_enabled; const struct hvm_modlist_entry *bios_module; /* Initialise hypercall stubs with RET, rendering them no-ops. */ memset((void *)HYPERCALL_PHYSICAL_ADDRESS, 0xc3 /* RET */, PAGE_SIZE); printf("HVM Loader\n"); BUG_ON(hvm_start_info->magic != XEN_HVM_START_MAGIC_VALUE); init_hypercalls(); memory_map_setup(); xenbus_setup(); bios = detect_bios(); printf("System requested %s\n", bios->name); printf("CPU speed is %u MHz\n", get_cpu_mhz()); apic_setup(); pci_setup(); smp_initialise(); perform_tests(); if ( bios->bios_info_setup ) bios->bios_info_setup(); if ( bios->create_smbios_tables ) { printf("Writing SMBIOS tables ...\n"); bios->create_smbios_tables(); } printf("Loading %s ...\n", bios->name); bios_module = get_module_entry(hvm_start_info, "firmware"); if ( bios_module ) { uint32_t paddr = bios_module->paddr; bios->bios_load(bios, (void*)paddr, bios_module->size); } #ifdef ENABLE_ROMBIOS else if ( bios == &rombios_config ) { bios->bios_load(bios, NULL, 0); } #endif else { /* * If there is no BIOS module supplied and if there is no embeded BIOS * image, then we failed. Only rombios might have an embedded bios blob. */ printf("no BIOS ROM image found\n"); BUG(); } if ( (hvm_info->nr_vcpus > 1) || hvm_info->apic_mode ) { if ( bios->create_mp_tables ) bios->create_mp_tables(); if ( bios->create_pir_tables ) bios->create_pir_tables(); } if ( bios->load_roms ) bios->load_roms(); acpi_enabled = !strncmp(xenstore_read("platform/acpi", "1"), "1", 1); if ( acpi_enabled ) { init_vnuma_info(); if ( bios->acpi_build_tables ) { printf("Loading ACPI ...\n"); bios->acpi_build_tables(); } acpi_enable_sci(); hvm_param_set(HVM_PARAM_ACPI_IOPORTS_LOCATION, 1); } init_vm86_tss(); cmos_write_memory_size(); printf("BIOS map:\n"); if ( SCRATCH_PHYSICAL_ADDRESS != scratch_start ) printf(" %05x-%05lx: Scratch space\n", SCRATCH_PHYSICAL_ADDRESS, scratch_start); printf(" %05x-%05x: Main BIOS\n", bios->bios_address, bios->bios_address + bios->image_size - 1); if ( bios->e820_setup ) bios->e820_setup(); if ( bios->bios_info_finish ) bios->bios_info_finish(); xenbus_shutdown(); printf("Invoking %s ...\n", bios->name); return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/pir.c0000664000175000017500000000372413256712137016356 0ustar smbsmb/* * pir.c: Support for genrating $PIR tables. * * Copyright (c) 2011 Citrix Systems Inc * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include "config.h" #include "pir_types.h" #include "util.h" /* * The structure of these tables is described in * http://www.microsoft.com/taiwan/whdc/archive/pciirq.mspx */ unsigned long create_pir_tables(void) { int length = sizeof(struct pir_table) + sizeof(struct pir_slot) * NR_PIR_SLOTS; struct pir_table *pir = scratch_alloc(length, 0); int i, checksum; memset(pir, 0, length); memcpy(pir->signature, "$PIR", 4); pir->version = 0x0100; pir->length = length; pir->router_bus = 0; pir->router_devfn = PCI_ISA_DEVFN; pir->router_vid = 0x8086; pir->router_did = 0x122e; pir->pci_irqs = 0x0000; for ( i = 0 ; i < NR_PIR_SLOTS; i++ ) { struct pir_slot *slot = &pir->slots[i]; slot->slot = i; slot->bus = 0; slot->dev = i<<3; slot->link_a = 0x60 + (i+1)%4; slot->bitmap_a = PCI_ISA_IRQ_MASK; slot->link_b = 0x60 + (i+2)%4; slot->bitmap_b = PCI_ISA_IRQ_MASK; slot->link_c = 0x60 + (i+3)%4; slot->bitmap_c = PCI_ISA_IRQ_MASK; slot->link_d = 0x60 + (i+4)%4; slot->bitmap_d = PCI_ISA_IRQ_MASK; } checksum = 0; for ( i = 0; i < length; i++ ) checksum += ((int8_t *)pir)[i]; pir->checksum = -checksum; return (unsigned long)pir; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/cacheattr.c0000664000175000017500000001035013256712137017513 0ustar smbsmb/* * cacheattr.c: MTRR and PAT initialisation. * * Copyright (c) 2008, Citrix Systems, Inc. * * Authors: * Keir Fraser * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "util.h" #include "config.h" #define MSR_MTRRphysBase(reg) (0x200 + 2 * (reg)) #define MSR_MTRRphysMask(reg) (0x200 + 2 * (reg) + 1) #define MSR_MTRRcap 0x00fe #define MSR_MTRRfix64K_00000 0x0250 #define MSR_MTRRfix16K_80000 0x0258 #define MSR_MTRRfix16K_A0000 0x0259 #define MSR_MTRRfix4K_C0000 0x0268 #define MSR_MTRRfix4K_C8000 0x0269 #define MSR_MTRRfix4K_D0000 0x026a #define MSR_MTRRfix4K_D8000 0x026b #define MSR_MTRRfix4K_E0000 0x026c #define MSR_MTRRfix4K_E8000 0x026d #define MSR_MTRRfix4K_F0000 0x026e #define MSR_MTRRfix4K_F8000 0x026f #define MSR_PAT 0x0277 #define MSR_MTRRdefType 0x02ff unsigned int cpu_phys_addr(void) { uint32_t eax, ebx, ecx, edx; unsigned int phys_bits = 36; /* Find the physical address size for this CPU. */ cpuid(0x80000000, &eax, &ebx, &ecx, &edx); if ( eax >= 0x80000008 ) { cpuid(0x80000008, &eax, &ebx, &ecx, &edx); phys_bits = (uint8_t)eax; } return phys_bits; } void cacheattr_init(void) { uint32_t eax, ebx, ecx, edx; uint64_t mtrr_cap, mtrr_def, content, addr_mask; unsigned int i, nr_var_ranges, phys_bits; /* Does the CPU support architectural MTRRs? */ cpuid(0x00000001, &eax, &ebx, &ecx, &edx); if ( !(edx & (1u << 12)) ) return; phys_bits = cpu_phys_addr(); printf("%u-bit phys ... ", phys_bits); addr_mask = ((1ull << phys_bits) - 1) & ~((1ull << 12) - 1); mtrr_cap = rdmsr(MSR_MTRRcap); mtrr_def = (1u << 11) | 6; /* E, default type WB */ /* Fixed-range MTRRs supported? */ if ( mtrr_cap & (1u << 8) ) { /* 0x00000-0x9ffff: Write Back (WB) */ content = 0x0606060606060606ull; wrmsr(MSR_MTRRfix64K_00000, content); wrmsr(MSR_MTRRfix16K_80000, content); /* 0xa0000-0xbffff: Write Combining (WC) */ if ( mtrr_cap & (1u << 10) ) /* WC supported? */ content = 0x0101010101010101ull; wrmsr(MSR_MTRRfix16K_A0000, content); /* 0xc0000-0xfffff: Write Back (WB) */ content = 0x0606060606060606ull; for ( i = 0; i < 8; i++ ) wrmsr(MSR_MTRRfix4K_C0000 + i, content); mtrr_def |= 1u << 10; /* FE */ printf("fixed MTRRs ... "); } /* Variable-range MTRRs supported? */ nr_var_ranges = (uint8_t)mtrr_cap; if ( nr_var_ranges != 0 ) { uint64_t base = pci_mem_start, size; for ( i = 0; !(base >> 32) && (i < nr_var_ranges); i++ ) { size = PAGE_SIZE; while ( !(base & size) ) size <<= 1; while ( ((base + size) < base) || ((base + size - 1) >> 32) ) size >>= 1; wrmsr(MSR_MTRRphysBase(i), base); wrmsr(MSR_MTRRphysMask(i), (~(size - 1) & addr_mask) | (1u << 11)); base += size; } for ( base = pci_hi_mem_start; (base != pci_hi_mem_end) && (i < nr_var_ranges); i++ ) { size = PAGE_SIZE; while ( !(base & size) ) size <<= 1; while ( (base + size < base) || (base + size > pci_hi_mem_end) ) size >>= 1; wrmsr(MSR_MTRRphysBase(i), base); wrmsr(MSR_MTRRphysMask(i), (~(size - 1) & addr_mask) | (1u << 11)); base += size; } printf("var MTRRs [%d/%d] ... ", i, nr_var_ranges); } wrmsr(MSR_MTRRdefType, mtrr_def); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/ctype.c0000664000175000017500000000302313256712137016700 0ustar smbsmb#include "ctype.h" const unsigned char _ctype[] = { _C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ _C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ _C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ _C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ _S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ _P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ _D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ _D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ _P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ _U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ _U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ _U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ _P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ _L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ _L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ _L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ _S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ _P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ _U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ _U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ _L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ _L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ xen-4.9.2/tools/firmware/hvmloader/pci_regs.h0000664000175000017500000001123613256712137017361 0ustar smbsmb/* * pci_regs.h * * PCI standard defines * Copyright 1994, Drew Eckhardt * Copyright 1997--1999 Martin Mares * * For more information, please consult the following manuals (look at * http://www.pcisig.com/ for how to get them): * * PCI BIOS Specification * PCI Local Bus Specification * PCI to PCI Bridge Specification * PCI System Design Guide */ #ifndef __HVMLOADER_PCI_REGS_H__ #define __HVMLOADER_PCI_REGS_H__ #define PCI_VENDOR_ID 0x00 /* 16 bits */ #define PCI_DEVICE_ID 0x02 /* 16 bits */ #define PCI_COMMAND 0x04 /* 16 bits */ #define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ #define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ #define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ #define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ #define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ #define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ #define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ #define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ #define PCI_COMMAND_SERR 0x100 /* Enable SERR */ #define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ #define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ #define PCI_STATUS 0x06 /* 16 bits */ #define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ #define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ #define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ #define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ #define PCI_STATUS_PARITY 0x100 /* Detected parity error */ #define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ #define PCI_STATUS_DEVSEL_FAST 0x000 #define PCI_STATUS_DEVSEL_MEDIUM 0x200 #define PCI_STATUS_DEVSEL_SLOW 0x400 #define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ #define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ #define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ #define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ #define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ #define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ #define PCI_REVISION_ID 0x08 /* Revision ID */ #define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ #define PCI_CLASS_DEVICE 0x0a /* Device class */ #define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ #define PCI_LATENCY_TIMER 0x0d /* 8 bits */ #define PCI_HEADER_TYPE 0x0e /* 8 bits */ #define PCI_HEADER_TYPE_NORMAL 0 #define PCI_HEADER_TYPE_BRIDGE 1 #define PCI_HEADER_TYPE_CARDBUS 2 #define PCI_BIST 0x0f /* 8 bits */ #define PCI_BIST_CODE_MASK 0x0f /* Return result */ #define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ #define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ /* * Base addresses specify locations in memory or I/O space. * Decoded size can be determined by writing a value of * 0xffffffff to the register, and reading it back. Only * 1 bits are decoded. */ #define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ #define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ #define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ #define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ #define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ #define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ #define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ #define PCI_BASE_ADDRESS_SPACE_IO 0x01 #define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 #define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 #define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ #define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ #define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ #define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ #define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) #define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) /* bit 1 is reserved if address_space = 1 */ /* Header type 0 (normal devices) */ #define PCI_CARDBUS_CIS 0x28 #define PCI_SUBSYSTEM_VENDOR_ID 0x2c #define PCI_SUBSYSTEM_ID 0x2e #define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ #define PCI_ROM_ADDRESS_ENABLE 0x01 #define PCI_ROM_ADDRESS_MASK (~0x7ffUL) #define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ /* 0x35-0x3b are reserved */ #define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ #define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ #define PCI_MIN_GNT 0x3e /* 8 bits */ #define PCI_MAX_LAT 0x3f /* 8 bits */ #define PCI_INTEL_OPREGION 0xfc /* 4 bits */ #endif /* __HVMLOADER_PCI_REGS_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/pir_types.h0000664000175000017500000000332313256712137017602 0ustar smbsmb/* * pir_types.h - data structure definitions for Xen HVM $PIR support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . * * Copyright (C) Citrix Systems, 2011 * * See the PCI Interrupt Routing spec for more detail: * http://www.microsoft.com/taiwan/whdc/archive/pciirq.mspx */ #ifndef PIR_TYPES_H #define PIR_TYPES_H #include #define NR_PIR_SLOTS 6 struct pir_slot { uint8_t bus; uint8_t dev; uint8_t link_a; uint16_t bitmap_a; uint8_t link_b; uint16_t bitmap_b; uint8_t link_c; uint16_t bitmap_c; uint8_t link_d; uint16_t bitmap_d; uint8_t slot; uint8_t reserved; } __attribute__ ((packed)); struct pir_table { char signature[4]; uint16_t version; uint16_t length; uint8_t router_bus; uint8_t router_devfn; uint16_t pci_irqs; uint16_t router_vid; uint16_t router_did; uint32_t miniport_data; uint8_t reserved[11]; uint8_t checksum; struct pir_slot slots[0]; } __attribute__ ((packed)); #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/util.c0000664000175000017500000005334013256712137016540 0ustar smbsmb/* * util.c: Helper library functions for HVMLoader. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005, International Business Machines Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "util.h" #include "config.h" #include "hypercall.h" #include "ctype.h" #include "vnuma.h" #include #include #include #include #include #include #include #include /* * Check whether there exists overlap in the specified memory range. * Returns true if exists, else returns false. */ bool check_overlap(uint64_t start, uint64_t size, uint64_t reserved_start, uint64_t reserved_size) { return (start + size > reserved_start) && (start < reserved_start + reserved_size); } void wrmsr(uint32_t idx, uint64_t v) { asm volatile ( "wrmsr" : : "c" (idx), "a" ((uint32_t)v), "d" ((uint32_t)(v>>32)) ); } uint64_t rdmsr(uint32_t idx) { uint32_t lo, hi; asm volatile ( "rdmsr" : "=a" (lo), "=d" (hi) : "c" (idx) ); return (lo | ((uint64_t)hi << 32)); } void outb(uint16_t addr, uint8_t val) { asm volatile ( "outb %%al, %%dx" : : "d" (addr), "a" (val) ); } void outw(uint16_t addr, uint16_t val) { asm volatile ( "outw %%ax, %%dx" : : "d" (addr), "a" (val) ); } void outl(uint16_t addr, uint32_t val) { asm volatile ( "outl %%eax, %%dx" : : "d" (addr), "a" (val) ); } uint8_t inb(uint16_t addr) { uint8_t val; asm volatile ( "inb %%dx,%%al" : "=a" (val) : "d" (addr) ); return val; } uint16_t inw(uint16_t addr) { uint16_t val; asm volatile ( "inw %%dx,%%ax" : "=a" (val) : "d" (addr) ); return val; } uint32_t inl(uint16_t addr) { uint32_t val; asm volatile ( "inl %%dx,%%eax" : "=a" (val) : "d" (addr) ); return val; } uint8_t cmos_inb(uint8_t idx) { outb(0x70, idx); return inb(0x71); } void cmos_outb(uint8_t idx, uint8_t val) { outb(0x70, idx); outb(0x71, val); } char *itoa(char *a, unsigned int i) { unsigned int _i = i, x = 0; do { x++; _i /= 10; } while ( _i != 0 ); a += x; *a-- = '\0'; do { *a-- = (i % 10) + '0'; i /= 10; } while ( i != 0 ); return a + 1; } int strcmp(const char *cs, const char *ct) { signed char res; while ( ((res = *cs - *ct++) == 0) && (*cs++ != '\0') ) continue; return res; } int strncmp(const char *s1, const char *s2, uint32_t n) { uint32_t ctr; for (ctr = 0; ctr < n; ctr++) if (s1[ctr] != s2[ctr]) return (int)(s1[ctr] - s2[ctr]); return 0; } void *memcpy(void *dest, const void *src, unsigned n) { int t0, t1, t2; asm volatile ( "cld\n" "rep; movsl\n" "testb $2,%b4\n" "je 1f\n" "movsw\n" "1: testb $1,%b4\n" "je 2f\n" "movsb\n" "2:" : "=&c" (t0), "=&D" (t1), "=&S" (t2) : "0" (n/4), "q" (n), "1" ((long) dest), "2" ((long) src) : "memory" ); return dest; } void *memmove(void *dest, const void *src, unsigned n) { if ( (unsigned long)dest > (unsigned long)src ) while ( n-- != 0 ) ((char *)dest)[n] = ((char *)src)[n]; else memcpy(dest, src, n); return dest; } char * strcpy(char *dest, const char *src) { char *p = dest; while ( *src ) *p++ = *src++; *p = 0; return dest; } char * strncpy(char *dest, const char *src, unsigned n) { int i = 0; char *p = dest; /* write non-NUL characters from src into dest until we run out of room in dest or encounter a NUL in src */ while ( (i < n) && *src ) { *p++ = *src++; i++; } /* pad remaining bytes of dest with NUL bytes */ while ( i < n ) { *p++ = 0; i++; } return dest; } unsigned strlen(const char *s) { int i = 0; while ( *s++ ) i++; return i; } static inline int __digit(char c, int base) { int d = -1; if ( (c >= '0') && (c <= '9') ) d = c - '0'; if ( (c >= 'A') && (c <= 'Z') ) d = c - 'A' + 10; if ( (c >= 'a') && (c <= 'z') ) d = c - 'a' + 10; if (d >= base) d = -1; return d; } long long strtoll(const char *s, char **end, int base) { long long v = 0; int sign = 1; while ( (*s != '\0') && isspace(*s) ) s++; if ( *s == '\0' ) goto out; if ( *s == '-' ) { sign = -1; s++; } else { if ( *s == '+' ) s++; } if ( *s == '\0' ) goto out; if ( *s == '0' ) { s++; if ( *s == '\0' ) goto out; if ( *s == 'x' ) { if ( base != 0 && base != 16) goto out; base = 16; s++; } else { if ( base != 0 && base != 8) goto out; base = 8; } } else { if (base != 0 && base != 10) goto out; base = 10; } while ( *s != '\0' ) { int d = __digit(*s, base); if ( d < 0 ) goto out; v = (v * base) + d; s++; } out: if (end) *end = (char *)s; return sign * v; } void * memset(void *s, int c, unsigned n) { uint8_t b = (uint8_t) c; uint8_t *p = (uint8_t *)s; int i; for ( i = 0; i < n; i++ ) *p++ = b; return s; } int memcmp(const void *s1, const void *s2, unsigned n) { unsigned i; uint8_t *p1 = (uint8_t *) s1; uint8_t *p2 = (uint8_t *) s2; for ( i = 0; i < n; i++ ) { if ( p1[i] < p2[i] ) return -1; else if ( p1[i] > p2[i] ) return 1; } return 0; } void cpuid(uint32_t idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { asm volatile ( "cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "0" (idx) ); } static const char hex_digits[] = "0123456789abcdef"; /* Write a two-character hex representation of 'byte' to digits[]. Pre-condition: sizeof(digits) >= 2 */ void byte_to_hex(char *digits, uint8_t byte) { digits[0] = hex_digits[byte >> 4]; digits[1] = hex_digits[byte & 0x0f]; } /* Convert an array of 16 unsigned bytes to a DCE/OSF formatted UUID string. Pre-condition: sizeof(dest) >= 37 */ void uuid_to_string(char *dest, uint8_t *uuid) { int i = 0; char *p = dest; for ( i = 0; i < 4; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p++ = '-'; for ( i = 4; i < 6; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p++ = '-'; for ( i = 6; i < 8; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p++ = '-'; for ( i = 8; i < 10; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p++ = '-'; for ( i = 10; i < 16; i++ ) { byte_to_hex(p, uuid[i]); p += 2; } *p = '\0'; } int get_mem_mapping_layout(struct e820entry entries[], uint32_t *max_entries) { int rc; struct xen_memory_map memmap = { .nr_entries = *max_entries }; set_xen_guest_handle(memmap.buffer, entries); rc = hypercall_memory_op(XENMEM_memory_map, &memmap); *max_entries = memmap.nr_entries; return rc; } void mem_hole_populate_ram(xen_pfn_t mfn, uint32_t nr_mfns) { static int over_allocated; struct xen_add_to_physmap xatp; struct xen_memory_reservation xmr; for ( ; nr_mfns-- != 0; mfn++ ) { /* Try to allocate a brand new page in the reserved area. */ if ( !over_allocated ) { xmr.domid = DOMID_SELF; xmr.mem_flags = 0; xmr.extent_order = 0; xmr.nr_extents = 1; set_xen_guest_handle(xmr.extent_start, &mfn); if ( hypercall_memory_op(XENMEM_populate_physmap, &xmr) == 1 ) continue; over_allocated = 1; } /* Otherwise, relocate a page from the ordinary RAM map. */ if ( hvm_info->high_mem_pgend ) { xatp.idx = --hvm_info->high_mem_pgend; if ( xatp.idx == (1ull << (32 - PAGE_SHIFT)) ) hvm_info->high_mem_pgend = 0; } else { xatp.idx = --hvm_info->low_mem_pgend; } xatp.domid = DOMID_SELF; xatp.space = XENMAPSPACE_gmfn; xatp.gpfn = mfn; if ( hypercall_memory_op(XENMEM_add_to_physmap, &xatp) != 0 ) BUG(); } /* Sync memory map[]. */ adjust_memory_map(); } static uint32_t alloc_up = RESERVED_MEMORY_DYNAMIC_START - 1; static uint32_t alloc_down = RESERVED_MEMORY_DYNAMIC_END; xen_pfn_t mem_hole_alloc(uint32_t nr_mfns) { alloc_down -= nr_mfns << PAGE_SHIFT; BUG_ON(alloc_up >= alloc_down); return alloc_down >> PAGE_SHIFT; } void *mem_alloc(uint32_t size, uint32_t align) { uint32_t s, e; /* Align to at least 16 bytes. */ if ( align < 16 ) align = 16; s = (alloc_up + align) & ~(align - 1); e = s + size - 1; BUG_ON((e < s) || (e >= alloc_down)); while ( (alloc_up >> PAGE_SHIFT) != (e >> PAGE_SHIFT) ) { alloc_up += PAGE_SIZE; mem_hole_populate_ram(alloc_up >> PAGE_SHIFT, 1); } alloc_up = e; return (void *)(unsigned long)s; } void *scratch_alloc(uint32_t size, uint32_t align) { uint32_t s, e; /* Align to at least 16 bytes. */ if ( align < 16 ) align = 16; s = (scratch_start + align) & ~(align - 1); e = s + size - 1; BUG_ON(e < s); scratch_start = e; return (void *)(unsigned long)s; } uint32_t ioapic_read(uint32_t reg) { *(volatile uint32_t *)(ioapic_base_address + 0x00) = reg; return *(volatile uint32_t *)(ioapic_base_address + 0x10); } void ioapic_write(uint32_t reg, uint32_t val) { *(volatile uint32_t *)(ioapic_base_address + 0x00) = reg; *(volatile uint32_t *)(ioapic_base_address + 0x10) = val; } uint32_t lapic_read(uint32_t reg) { return *(volatile uint32_t *)(LAPIC_BASE_ADDRESS + reg); } void lapic_write(uint32_t reg, uint32_t val) { *(volatile uint32_t *)(LAPIC_BASE_ADDRESS + reg) = val; } #define PCI_CONF1_ADDRESS(bus, devfn, reg) \ (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3)) uint32_t pci_read(uint32_t devfn, uint32_t reg, uint32_t len) { outl(0xcf8, PCI_CONF1_ADDRESS(0, devfn, reg)); switch ( len ) { case 1: return inb(0xcfc + (reg & 3)); case 2: return inw(0xcfc + (reg & 2)); } return inl(0xcfc); } void pci_write(uint32_t devfn, uint32_t reg, uint32_t len, uint32_t val) { outl(0xcf8, PCI_CONF1_ADDRESS(0, devfn, reg)); switch ( len ) { case 1: outb(0xcfc + (reg & 3), val); break; case 2: outw(0xcfc + (reg & 2), val); break; case 4: outl(0xcfc, val); break; } } static char *printnum(char *p, unsigned long num, unsigned base) { unsigned long n; if ( (n = num/base) > 0 ) p = printnum(p, n, base); *p++ = hex_digits[num % base]; *p = '\0'; return p; } static void _doprint(void (*emit)(void *, char), void *arg, const char *fmt, va_list ap) { char *str, c; int lflag, zflag, nflag; char buffer[17]; unsigned long value; int i, slen, pad; for ( ; *fmt != '\0'; fmt++ ) { if ( *fmt != '%' ) { emit(arg, *fmt); continue; } pad = zflag = nflag = lflag = 0; c = *++fmt; if ( (c == '-') || isdigit(c) ) { if ( c == '-' ) { nflag = 1; c = *++fmt; } zflag = c == '0'; for ( pad = 0; isdigit(c); c = *++fmt ) pad = (pad * 10) + c - '0'; } if ( c == 'l' ) /* long extension */ { lflag = 1; c = *++fmt; } if ( (c == 'd') || (c == 'u') || (c == 'o') || (c == 'x') || (c == 'X') ) { if ( lflag ) { value = va_arg(ap, unsigned long); if ( (c == 'd') && ((long)value < 0) ) { value = -value; emit(arg, '-'); } } else { value = va_arg(ap, unsigned int); if ( (c == 'd') && ((int)value < 0) ) { value = -(int)value; emit(arg, '-'); } } str = buffer; printnum(str, value, c == 'o' ? 8 : ((c == 'x') || (c == 'X') ? 16 : 10)); slen = strlen(str); for ( i = pad - slen; i > 0; i-- ) emit(arg, zflag ? '0' : ' '); while ( *str ) { char ch = *str++; if ( (ch >= 'a') && (c == 'X') ) ch += 'A'-'a'; emit(arg, ch); } } else if ( c == 's' ) { str = va_arg(ap, char *); slen = strlen(str); if ( nflag == 0 ) for ( i = pad - slen; i > 0; i-- ) emit(arg, ' '); while ( *str ) emit(arg, *str++); if ( nflag ) for ( i = pad - slen; i > 0; i-- ) emit(arg, ' '); } else if ( c == 'c' ) { emit(arg, va_arg(ap, int)); } else { emit(arg, *fmt); } } } static void putchar(char c) { outb(0xe9, c); } static void __put(void *arg, char c) { putchar(c); } int printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _doprint(__put, NULL, fmt, ap); va_end(ap); return 0; } int vprintf(const char *fmt, va_list ap) { _doprint(__put, NULL, fmt, ap); return 0; } struct __copy_context { char *ptr; size_t emitted; size_t remaining; }; static void __copy(void *arg, char c) { struct __copy_context *ctxt = arg; ctxt->emitted++; if (ctxt->remaining == 0) return; *(ctxt->ptr++) = c; --ctxt->remaining; } int snprintf(char *buf, size_t size, const char *fmt, ...) { va_list ap; struct __copy_context ctxt; ctxt.ptr = buf; ctxt.emitted = 0; ctxt.remaining = size; va_start(ap, fmt); _doprint(__copy, &ctxt, fmt, ap); va_end(ap); if (ctxt.remaining != 0) *ctxt.ptr = '\0'; return ctxt.emitted; } static void __attribute__((noreturn)) crash(void) { struct sched_shutdown shutdown = { .reason = SHUTDOWN_crash }; printf("*** HVMLoader crashed.\n"); hypercall_sched_op(SCHEDOP_shutdown, &shutdown); printf("*** Failed to crash. Halting.\n"); for ( ; ; ) asm volatile ( "hlt" ); } void __assert_failed(char *assertion, char *file, int line) { printf("*** HVMLoader assertion '%s' failed at %s:%d\n", assertion, file, line); crash(); } void __bug(char *file, int line) { printf("*** HVMLoader bug at %s:%d\n", file, line); crash(); } static void validate_hvm_info(struct hvm_info_table *t) { uint8_t *ptr = (uint8_t *)t; uint8_t sum = 0; int i; if ( strncmp(t->signature, "HVM INFO", 8) ) { printf("Bad hvm info signature\n"); BUG(); } if ( t->length < sizeof(struct hvm_info_table) ) { printf("Bad hvm info length\n"); BUG(); } for ( i = 0; i < t->length; i++ ) sum += ptr[i]; if ( sum != 0 ) { printf("Bad hvm info checksum\n"); BUG(); } } struct hvm_info_table *get_hvm_info_table(void) { static struct hvm_info_table *table; struct hvm_info_table *t; if ( table != NULL ) return table; t = (struct hvm_info_table *)HVM_INFO_PADDR; validate_hvm_info(t); table = t; return table; } struct shared_info *get_shared_info(void) { static struct shared_info *shared_info = NULL; struct xen_add_to_physmap xatp; if ( shared_info != NULL ) return shared_info; xatp.domid = DOMID_SELF; xatp.space = XENMAPSPACE_shared_info; xatp.idx = 0; xatp.gpfn = mem_hole_alloc(1); shared_info = (struct shared_info *)(xatp.gpfn << PAGE_SHIFT); if ( hypercall_memory_op(XENMEM_add_to_physmap, &xatp) != 0 ) BUG(); return shared_info; } uint16_t get_cpu_mhz(void) { struct shared_info *shared_info = get_shared_info(); struct vcpu_time_info *info = &shared_info->vcpu_info[0].time; uint64_t cpu_khz; uint32_t tsc_to_nsec_mul, version; int8_t tsc_shift; static uint16_t cpu_mhz; if ( cpu_mhz != 0 ) return cpu_mhz; /* Get a consistent snapshot of scale factor (multiplier and shift). */ do { version = info->version; rmb(); tsc_to_nsec_mul = info->tsc_to_system_mul; tsc_shift = info->tsc_shift; rmb(); } while ((version & 1) | (version ^ info->version)); /* Compute CPU speed in kHz. */ cpu_khz = 1000000ull << 32; do_div(cpu_khz, tsc_to_nsec_mul); if ( tsc_shift < 0 ) cpu_khz = cpu_khz << -tsc_shift; else cpu_khz = cpu_khz >> tsc_shift; cpu_mhz = (uint16_t)(((uint32_t)cpu_khz + 500) / 1000); return cpu_mhz; } int uart_exists(uint16_t uart_base) { uint16_t ier = uart_base + 1; uint8_t a, b, c; a = inb(ier); outb(ier, 0); b = inb(ier); outb(ier, 0xf); c = inb(ier); outb(ier, a); return ((b == 0) && (c == 0xf)); } int lpt_exists(uint16_t lpt_base) { /* Idea taken from linux-2.6.31.5:parport_pc.c */ uint16_t control = lpt_base + 2; outb(control, 0xc); return ((inb(control) & 0xf) == 0xc); } int hpet_exists(unsigned long hpet_base) { uint32_t hpet_id = *(uint32_t *)hpet_base; return ((hpet_id >> 16) == 0x8086); } static uint8_t battery_port_exists(void) { return (inb(0x88) == 0x1F); } static unsigned long acpi_v2p(struct acpi_ctxt *ctxt, void *v) { return virt_to_phys(v); } static void *acpi_mem_alloc(struct acpi_ctxt *ctxt, uint32_t size, uint32_t align) { return mem_alloc(size, align); } static void acpi_mem_free(struct acpi_ctxt *ctxt, void *v, uint32_t size) { /* ACPI builder currently doesn't free memory so this is just a stub */ } static uint8_t acpi_lapic_id(unsigned cpu) { return LAPIC_ID(cpu); } void hvmloader_acpi_build_tables(struct acpi_config *config, unsigned int physical) { const char *s; struct acpi_ctxt ctxt; /* Allocate and initialise the acpi info area. */ mem_hole_populate_ram(ACPI_INFO_PHYSICAL_ADDRESS >> PAGE_SHIFT, 1); config->lapic_base_address = LAPIC_BASE_ADDRESS; config->lapic_id = acpi_lapic_id; config->ioapic_base_address = ioapic_base_address; config->ioapic_id = IOAPIC_ID; config->pci_isa_irq_mask = PCI_ISA_IRQ_MASK; if ( uart_exists(0x3f8) ) config->table_flags |= ACPI_HAS_COM1; if ( uart_exists(0x2f8) ) config->table_flags |= ACPI_HAS_COM2; if ( lpt_exists(0x378) ) config->table_flags |= ACPI_HAS_LPT1; if ( hpet_exists(ACPI_HPET_ADDRESS) ) config->table_flags |= ACPI_HAS_HPET; config->pci_start = pci_mem_start; config->pci_len = pci_mem_end - pci_mem_start; if ( pci_hi_mem_end > pci_hi_mem_start ) { config->pci_hi_start = pci_hi_mem_start; config->pci_hi_len = pci_hi_mem_end - pci_hi_mem_start; } s = xenstore_read("platform/generation-id", "0:0"); if ( s ) { char *end; config->vm_gid[0] = strtoll(s, &end, 0); config->vm_gid[1] = 0; if ( end && end[0] == ':' ) config->vm_gid[1] = strtoll(end+1, NULL, 0); } s = xenstore_read(HVM_XS_ACPI_PT_ADDRESS, NULL); if ( s ) { config->pt.addr = strtoll(s, NULL, 0); s = xenstore_read(HVM_XS_ACPI_PT_LENGTH, NULL); if ( s ) config->pt.length = strtoll(s, NULL, 0); } if ( battery_port_exists() ) config->table_flags |= ACPI_HAS_SSDT_PM; if ( !strncmp(xenstore_read("platform/acpi_s3", "1"), "1", 1) ) config->table_flags |= ACPI_HAS_SSDT_S3; if ( !strncmp(xenstore_read("platform/acpi_s4", "1"), "1", 1) ) config->table_flags |= ACPI_HAS_SSDT_S4; if ( !strncmp(xenstore_read("platform/acpi_laptop_slate", "0"), "1", 1) ) config->table_flags |= ACPI_HAS_SSDT_LAPTOP_SLATE; config->table_flags |= (ACPI_HAS_TCPA | ACPI_HAS_IOAPIC | ACPI_HAS_WAET | ACPI_HAS_PMTIMER | ACPI_HAS_BUTTONS | ACPI_HAS_VGA | ACPI_HAS_8042 | ACPI_HAS_CMOS_RTC); config->acpi_revision = 4; config->tis_hdr = (uint16_t *)ACPI_TIS_HDR_ADDRESS; config->numa.nr_vmemranges = nr_vmemranges; config->numa.nr_vnodes = nr_vnodes; config->numa.vcpu_to_vnode = vcpu_to_vnode; config->numa.vdistance = vdistance; config->numa.vmemrange = vmemrange; config->hvminfo = hvm_info; config->rsdp = physical; config->infop = ACPI_INFO_PHYSICAL_ADDRESS; ctxt.mem_ops.alloc = acpi_mem_alloc; ctxt.mem_ops.free = acpi_mem_free; ctxt.mem_ops.v2p = acpi_v2p; acpi_build_tables(&ctxt, config); hvm_param_set(HVM_PARAM_VM_GENERATION_ID_ADDR, config->vm_gid_addr); } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/pci.c0000664000175000017500000004675113256712137016346 0ustar smbsmb/* * pci.c: HVM PCI setup. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005, International Business Machines Corporation. * * Copyright (c) 2006, Keir Fraser, XenSource Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; If not, see . */ #include "util.h" #include "hypercall.h" #include "config.h" #include "pci_regs.h" #include #include #include #include #include unsigned long pci_mem_start = HVM_BELOW_4G_MMIO_START; unsigned long pci_mem_end = PCI_MEM_END; uint64_t pci_hi_mem_start = 0, pci_hi_mem_end = 0; enum virtual_vga virtual_vga = VGA_none; unsigned long igd_opregion_pgbase = 0; /* Check if the specified range conflicts with any reserved device memory. */ static bool check_overlap_all(uint64_t start, uint64_t size) { unsigned int i; for ( i = 0; i < memory_map.nr_map; i++ ) { if ( memory_map.map[i].type == E820_RESERVED && check_overlap(start, size, memory_map.map[i].addr, memory_map.map[i].size) ) return true; } return false; } /* Find the lowest RMRR ending above base but below 4G. */ static int find_next_rmrr(uint32_t base) { unsigned int i; int next_rmrr = -1; uint64_t end, min_end = 1ULL << 32; for ( i = 0; i < memory_map.nr_map ; i++ ) { end = memory_map.map[i].addr + memory_map.map[i].size; if ( memory_map.map[i].type == E820_RESERVED && end > base && end <= min_end ) { next_rmrr = i; min_end = end; } } return next_rmrr; } void pci_setup(void) { uint8_t is_64bar, using_64bar, bar64_relocate = 0; uint32_t devfn, bar_reg, cmd, bar_data, bar_data_upper; uint64_t base, bar_sz, bar_sz_upper, mmio_total = 0; uint32_t vga_devfn = 256; uint16_t class, vendor_id, device_id; unsigned int bar, pin, link, isa_irq; /* Resources assignable to PCI devices via BARs. */ struct resource { uint64_t base, max; } *resource, mem_resource, high_mem_resource, io_resource; /* Create a list of device BARs in descending order of size. */ struct bars { uint32_t is_64bar; uint32_t devfn; uint32_t bar_reg; uint64_t bar_sz; } *bars = (struct bars *)scratch_start; unsigned int i, nr_bars = 0; uint64_t mmio_hole_size = 0; const char *s; /* * Do we allow hvmloader to relocate guest memory in order to * increase the size of the lowmem MMIO hole? Defaulting to 1 * here will mean that non-libxl toolstacks (including xend and * home-grown ones) means that those using qemu-xen will still * experience the memory relocation bug described below; but it * also means that those using qemu-traditional will *not* * experience any change; and it also means that there is a * work-around for those using qemu-xen, namely switching to * qemu-traditional. * * If we defaulted to 0, and failing to resize the hole caused any * problems with qemu-traditional, then there is no work-around. * * Since xend can only use qemu-traditional, I think this is the * option that will have the least impact. */ bool allow_memory_relocate = 1; s = xenstore_read(HVM_XS_ALLOW_MEMORY_RELOCATE, NULL); if ( s ) allow_memory_relocate = strtoll(s, NULL, 0); printf("Relocating guest memory for lowmem MMIO space %s\n", allow_memory_relocate?"enabled":"disabled"); s = xenstore_read("platform/mmio_hole_size", NULL); if ( s ) mmio_hole_size = strtoll(s, NULL, 0); /* Program PCI-ISA bridge with appropriate link routes. */ isa_irq = 0; for ( link = 0; link < 4; link++ ) { do { isa_irq = (isa_irq + 1) & 15; } while ( !(PCI_ISA_IRQ_MASK & (1U << isa_irq)) ); pci_writeb(PCI_ISA_DEVFN, 0x60 + link, isa_irq); printf("PCI-ISA link %u routed to IRQ%u\n", link, isa_irq); } /* Program ELCR to match PCI-wired IRQs. */ outb(0x4d0, (uint8_t)(PCI_ISA_IRQ_MASK >> 0)); outb(0x4d1, (uint8_t)(PCI_ISA_IRQ_MASK >> 8)); /* Scan the PCI bus and map resources. */ for ( devfn = 0; devfn < 256; devfn++ ) { class = pci_readw(devfn, PCI_CLASS_DEVICE); vendor_id = pci_readw(devfn, PCI_VENDOR_ID); device_id = pci_readw(devfn, PCI_DEVICE_ID); if ( (vendor_id == 0xffff) && (device_id == 0xffff) ) continue; ASSERT((devfn != PCI_ISA_DEVFN) || ((vendor_id == 0x8086) && (device_id == 0x7000))); switch ( class ) { case 0x0300: /* If emulated VGA is found, preserve it as primary VGA. */ if ( (vendor_id == 0x1234) && (device_id == 0x1111) ) { vga_devfn = devfn; virtual_vga = VGA_std; } else if ( (vendor_id == 0x1013) && (device_id == 0xb8) ) { vga_devfn = devfn; virtual_vga = VGA_cirrus; } else if ( virtual_vga == VGA_none ) { vga_devfn = devfn; virtual_vga = VGA_pt; if ( vendor_id == 0x8086 ) { igd_opregion_pgbase = mem_hole_alloc(IGD_OPREGION_PAGES); /* * Write the the OpRegion offset to give the opregion * address to the device model. The device model will trap * and map the OpRegion at the give address. */ pci_writel(vga_devfn, PCI_INTEL_OPREGION, igd_opregion_pgbase << PAGE_SHIFT); } } break; case 0x0680: /* PIIX4 ACPI PM. Special device with special PCI config space. */ ASSERT((vendor_id == 0x8086) && (device_id == 0x7113)); pci_writew(devfn, 0x20, 0x0000); /* No smb bus IO enable */ pci_writew(devfn, 0xd2, 0x0000); /* No smb bus IO enable */ pci_writew(devfn, 0x22, 0x0000); pci_writew(devfn, 0x3c, 0x0009); /* Hardcoded IRQ9 */ pci_writew(devfn, 0x3d, 0x0001); pci_writel(devfn, 0x40, ACPI_PM1A_EVT_BLK_ADDRESS_V1 | 1); pci_writeb(devfn, 0x80, 0x01); /* enable PM io space */ break; case 0x0101: if ( vendor_id == 0x8086 ) { /* Intel ICHs since PIIX3: enable IDE legacy mode. */ pci_writew(devfn, 0x40, 0x8000); /* enable IDE0 */ pci_writew(devfn, 0x42, 0x8000); /* enable IDE1 */ } break; } /* Map the I/O memory and port resources. */ for ( bar = 0; bar < 7; bar++ ) { bar_sz_upper = 0; bar_reg = PCI_BASE_ADDRESS_0 + 4*bar; if ( bar == 6 ) bar_reg = PCI_ROM_ADDRESS; bar_data = pci_readl(devfn, bar_reg); if ( bar_reg != PCI_ROM_ADDRESS ) { is_64bar = !!((bar_data & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)); pci_writel(devfn, bar_reg, ~0); } else { is_64bar = 0; pci_writel(devfn, bar_reg, (bar_data | PCI_ROM_ADDRESS_MASK) & ~PCI_ROM_ADDRESS_ENABLE); } bar_sz = pci_readl(devfn, bar_reg); pci_writel(devfn, bar_reg, bar_data); if ( bar_reg != PCI_ROM_ADDRESS ) bar_sz &= (((bar_data & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) ? PCI_BASE_ADDRESS_MEM_MASK : (PCI_BASE_ADDRESS_IO_MASK & 0xffff)); else bar_sz &= PCI_ROM_ADDRESS_MASK; if (is_64bar) { bar_data_upper = pci_readl(devfn, bar_reg + 4); pci_writel(devfn, bar_reg + 4, ~0); bar_sz_upper = pci_readl(devfn, bar_reg + 4); pci_writel(devfn, bar_reg + 4, bar_data_upper); bar_sz = (bar_sz_upper << 32) | bar_sz; } bar_sz &= ~(bar_sz - 1); if ( bar_sz == 0 ) continue; for ( i = 0; i < nr_bars; i++ ) if ( bars[i].bar_sz < bar_sz ) break; if ( i != nr_bars ) memmove(&bars[i+1], &bars[i], (nr_bars-i) * sizeof(*bars)); bars[i].is_64bar = is_64bar; bars[i].devfn = devfn; bars[i].bar_reg = bar_reg; bars[i].bar_sz = bar_sz; if ( ((bar_data & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) || (bar_reg == PCI_ROM_ADDRESS) ) mmio_total += bar_sz; nr_bars++; /*The upper half is already calculated, skip it! */ if (is_64bar) bar++; } /* Map the interrupt. */ pin = pci_readb(devfn, PCI_INTERRUPT_PIN); if ( pin != 0 ) { /* This is the barber's pole mapping used by Xen. */ link = ((pin - 1) + (devfn >> 3)) & 3; isa_irq = pci_readb(PCI_ISA_DEVFN, 0x60 + link); pci_writeb(devfn, PCI_INTERRUPT_LINE, isa_irq); printf("pci dev %02x:%x INT%c->IRQ%u\n", devfn>>3, devfn&7, 'A'+pin-1, isa_irq); } /* Enable bus mastering. */ cmd = pci_readw(devfn, PCI_COMMAND); cmd |= PCI_COMMAND_MASTER; pci_writew(devfn, PCI_COMMAND, cmd); } if ( mmio_hole_size ) { uint64_t max_ram_below_4g = (1ULL << 32) - mmio_hole_size; if ( max_ram_below_4g > HVM_BELOW_4G_MMIO_START ) { printf("max_ram_below_4g=0x"PRIllx " too big for mmio_hole_size=0x"PRIllx " has been ignored.\n", PRIllx_arg(max_ram_below_4g), PRIllx_arg(mmio_hole_size)); } else { pci_mem_start = max_ram_below_4g; printf("pci_mem_start=0x%lx (was 0x%x) for mmio_hole_size=%lu\n", pci_mem_start, HVM_BELOW_4G_MMIO_START, (long)mmio_hole_size); } } else { /* * At the moment qemu-xen can't deal with relocated memory regions. * It's too close to the release to make a proper fix; for now, * only allow the MMIO hole to grow large enough to move guest memory * if we're running qemu-traditional. Items that don't fit will be * relocated into the 64-bit address space. * * This loop now does the following: * - If allow_memory_relocate, increase the MMIO hole until it's * big enough, or until it's 2GiB * - If !allow_memory_relocate, increase the MMIO hole until it's * big enough, or until it's 2GiB, or until it overlaps guest * memory */ while ( (mmio_total > (pci_mem_end - pci_mem_start)) && ((pci_mem_start << 1) != 0) && (allow_memory_relocate || (((pci_mem_start << 1) >> PAGE_SHIFT) >= hvm_info->low_mem_pgend)) ) pci_mem_start <<= 1; /* * Try to accommodate RMRRs in our MMIO region on a best-effort basis. * If we have RMRRs in the range, then make pci_mem_start just after * hvm_info->low_mem_pgend. */ if ( pci_mem_start > (hvm_info->low_mem_pgend << PAGE_SHIFT) && check_overlap_all(pci_mem_start, pci_mem_end-pci_mem_start) ) pci_mem_start = hvm_info->low_mem_pgend << PAGE_SHIFT; } if ( mmio_total > (pci_mem_end - pci_mem_start) ) { printf("Low MMIO hole not large enough for all devices," " relocating some BARs to 64-bit\n"); bar64_relocate = 1; } /* Relocate RAM that overlaps PCI space (in 64k-page chunks). */ while ( (pci_mem_start >> PAGE_SHIFT) < hvm_info->low_mem_pgend ) { struct xen_add_to_physmap xatp; unsigned int nr_pages = min_t( unsigned int, hvm_info->low_mem_pgend - (pci_mem_start >> PAGE_SHIFT), (1u << 16) - 1); if ( hvm_info->high_mem_pgend == 0 ) hvm_info->high_mem_pgend = 1ull << (32 - PAGE_SHIFT); hvm_info->low_mem_pgend -= nr_pages; printf("Relocating 0x%x pages from "PRIllx" to "PRIllx\ " for lowmem MMIO hole\n", nr_pages, PRIllx_arg(((uint64_t)hvm_info->low_mem_pgend)<high_mem_pgend)<low_mem_pgend; xatp.gpfn = hvm_info->high_mem_pgend; xatp.size = nr_pages; if ( hypercall_memory_op(XENMEM_add_to_physmap, &xatp) != 0 ) BUG(); hvm_info->high_mem_pgend += nr_pages; } /* Sync memory map[] if necessary. */ adjust_memory_map(); high_mem_resource.base = ((uint64_t)hvm_info->high_mem_pgend) << PAGE_SHIFT; if ( high_mem_resource.base < 1ull << 32 ) { if ( hvm_info->high_mem_pgend != 0 ) printf("WARNING: hvm_info->high_mem_pgend %x" " does not point into high memory!", hvm_info->high_mem_pgend); high_mem_resource.base = 1ull << 32; } printf("%sRAM in high memory; setting high_mem resource base to "PRIllx"\n", hvm_info->high_mem_pgend?"":"No ", PRIllx_arg(high_mem_resource.base)); high_mem_resource.max = 1ull << cpu_phys_addr(); mem_resource.base = pci_mem_start; mem_resource.max = pci_mem_end; io_resource.base = 0xc000; io_resource.max = 0x10000; /* Assign iomem and ioport resources in descending order of size. */ for ( i = 0; i < nr_bars; i++ ) { devfn = bars[i].devfn; bar_reg = bars[i].bar_reg; bar_sz = bars[i].bar_sz; /* * Relocate to high memory if the total amount of MMIO needed * is more than the low MMIO available. Because devices are * processed in order of bar_sz, this will preferentially * relocate larger devices to high memory first. * * NB: The code here is rather fragile, as the check here to see * whether bar_sz will fit in the low MMIO region doesn't match the * real check made below, which involves aligning the base offset of the * bar with the size of the bar itself. As it happens, this will always * be satisfied because: * - The first one will succeed because the MMIO hole can only start at * 0x{f,e,c,8}00000000. If it fits, it will be aligned properly. * - All subsequent ones will be aligned because the list is ordered * large to small, and bar_sz is always a power of 2. (At least * the code here assumes it to be.) * Should either of those two conditions change, this code will break. */ using_64bar = bars[i].is_64bar && bar64_relocate && (mmio_total > (mem_resource.max - mem_resource.base)); bar_data = pci_readl(devfn, bar_reg); if ( (bar_data & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY ) { /* Mapping high memory if PCI device is 64 bits bar */ if ( using_64bar ) { if ( high_mem_resource.base & (bar_sz - 1) ) high_mem_resource.base = high_mem_resource.base - (high_mem_resource.base & (bar_sz - 1)) + bar_sz; if ( !pci_hi_mem_start ) pci_hi_mem_start = high_mem_resource.base; resource = &high_mem_resource; bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK; } else { resource = &mem_resource; bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK; } mmio_total -= bar_sz; } else { resource = &io_resource; bar_data &= ~PCI_BASE_ADDRESS_IO_MASK; } base = (resource->base + bar_sz - 1) & ~(uint64_t)(bar_sz - 1); /* If we're using mem_resource, check for RMRR conflicts. */ if ( resource == &mem_resource) { int next_rmrr = find_next_rmrr(base); while ( next_rmrr >= 0 && check_overlap(base, bar_sz, memory_map.map[next_rmrr].addr, memory_map.map[next_rmrr].size) ) { base = memory_map.map[next_rmrr].addr + memory_map.map[next_rmrr].size; base = (base + bar_sz - 1) & ~(bar_sz - 1); next_rmrr = find_next_rmrr(base); } } bar_data |= (uint32_t)base; bar_data_upper = (uint32_t)(base >> 32); base += bar_sz; if ( (base < resource->base) || (base > resource->max) ) { printf("pci dev %02x:%x bar %02x size "PRIllx": no space for " "resource!\n", devfn>>3, devfn&7, bar_reg, PRIllx_arg(bar_sz)); continue; } resource->base = base; pci_writel(devfn, bar_reg, bar_data); if (using_64bar) pci_writel(devfn, bar_reg + 4, bar_data_upper); printf("pci dev %02x:%x bar %02x size "PRIllx": %x%08x\n", devfn>>3, devfn&7, bar_reg, PRIllx_arg(bar_sz), bar_data_upper, bar_data); /* Now enable the memory or I/O mapping. */ cmd = pci_readw(devfn, PCI_COMMAND); if ( (bar_reg == PCI_ROM_ADDRESS) || ((bar_data & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) ) cmd |= PCI_COMMAND_MEMORY; else cmd |= PCI_COMMAND_IO; pci_writew(devfn, PCI_COMMAND, cmd); } if ( pci_hi_mem_start ) { /* * Make end address alignment match the start address one's so that * fewer variable range MTRRs are needed to cover the range. */ pci_hi_mem_end = ((high_mem_resource.base - 1) | ((pci_hi_mem_start & -pci_hi_mem_start) - 1)) + 1; } if ( vga_devfn != 256 ) { /* * VGA registers live in I/O space so ensure that primary VGA * has IO enabled, even if there is no I/O BAR on that * particular device. */ cmd = pci_readw(vga_devfn, PCI_COMMAND); cmd |= PCI_COMMAND_IO; pci_writew(vga_devfn, PCI_COMMAND, cmd); } } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/firmware/hvmloader/e820.h0000664000175000017500000000112513256712137016240 0ustar smbsmb#ifndef __HVMLOADER_E820_H__ #define __HVMLOADER_E820_H__ /* * PC BIOS standard E820 types and structure. */ #define E820_RAM 1 #define E820_RESERVED 2 #define E820_ACPI 3 #define E820_NVS 4 struct e820entry { uint64_t addr; uint64_t size; uint32_t type; } __attribute__((packed)); #define E820MAX 128 struct e820map { unsigned int nr_map; struct e820entry map[E820MAX]; }; #endif /* __HVMLOADER_E820_H__ */ /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/pygrub/0000775000175000017500000000000013256712137013125 5ustar smbsmbxen-4.9.2/tools/pygrub/setup.py0000664000175000017500000000147413256712137014645 0ustar smbsmbfrom distutils.core import setup, Extension from distutils.ccompiler import new_compiler import os import sys extra_compile_args = [ "-fno-strict-aliasing", "-Werror" ] XEN_ROOT = "../.." fsimage = Extension("fsimage", extra_compile_args = extra_compile_args, include_dirs = [ XEN_ROOT + "/tools/libfsimage/common/" ], library_dirs = [ XEN_ROOT + "/tools/libfsimage/common/" ], libraries = ["fsimage"], sources = ["src/fsimage/fsimage.c"]) pkgs = [ 'grub' ] setup(name='pygrub', version='0.3', description='Boot loader that looks a lot like grub for Xen', author='Jeremy Katz', author_email='katzj@redhat.com', license='GPL', package_dir={'grub': 'src', 'fsimage': 'src'}, scripts = ["src/pygrub"], packages=pkgs, ext_modules = [ fsimage ] ) xen-4.9.2/tools/pygrub/examples/0000775000175000017500000000000013256712137014743 5ustar smbsmbxen-4.9.2/tools/pygrub/examples/rhel-7-beta.grub20000664000175000017500000000713213256712137017720 0ustar smbsmb# # DO NOT EDIT THIS FILE # # It is automatically generated by grub2-mkconfig using templates # from /etc/grub.d and settings from /etc/default/grub # ### BEGIN /etc/grub.d/00_header ### set pager=1 if [ -s $prefix/grubenv ]; then load_env fi if [ "${next_entry}" ] ; then set default="${next_entry}" set next_entry= save_env next_entry set boot_once=true else set default="${saved_entry}" fi if [ x"${feature_menuentry_id}" = xy ]; then menuentry_id_option="--id" else menuentry_id_option="" fi export menuentry_id_option if [ "${prev_saved_entry}" ]; then set saved_entry="${prev_saved_entry}" save_env saved_entry set prev_saved_entry= save_env prev_saved_entry set boot_once=true fi function savedefault { if [ -z "${boot_once}" ]; then saved_entry="${chosen}" save_env saved_entry fi } function load_video { if [ x$feature_all_video_module = xy ]; then insmod all_video else insmod efi_gop insmod efi_uga insmod ieee1275_fb insmod vbe insmod vga insmod video_bochs insmod video_cirrus fi } terminal_output console set timeout=5 ### END /etc/grub.d/00_header ### ### BEGIN /etc/grub.d/10_linux ### menuentry 'Red Hat Enterprise Linux Everything, with Linux 3.10.0-54.0.1.el7.x86_64' --class red --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-3.10.0-54.0.1.el7.x86_64-advanced-d23b8b49-4cfe-4900-8ef1-ec80bc633163' { load_video set gfxpayload=keep insmod gzio insmod part_msdos insmod xfs set root='hd0,msdos1' if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root --hint='hd0,msdos1' 89ffef78-82b3-457c-bc57-42cccc373851 else search --no-floppy --fs-uuid --set=root 89ffef78-82b3-457c-bc57-42cccc373851 fi linux16 /vmlinuz-3.10.0-54.0.1.el7.x86_64 root=/dev/mapper/rhel-root ro rd.lvm.lv=rhel/swap vconsole.keymap=uk crashkernel=auto rd.lvm.lv=rhel/root vconsole.font=latarcyrheb-sun16 LANG=en_GB.UTF-8 initrd16 /initramfs-3.10.0-54.0.1.el7.x86_64.img } menuentry 'Red Hat Enterprise Linux Everything, with Linux 0-rescue-af34f0b8cf364cdbbe6d093f8228a37f' --class red --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-0-rescue-af34f0b8cf364cdbbe6d093f8228a37f-advanced-d23b8b49-4cfe-4900-8ef1-ec80bc633163' { load_video insmod gzio insmod part_msdos insmod xfs set root='hd0,msdos1' if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root --hint='hd0,msdos1' 89ffef78-82b3-457c-bc57-42cccc373851 else search --no-floppy --fs-uuid --set=root 89ffef78-82b3-457c-bc57-42cccc373851 fi linux16 /vmlinuz-0-rescue-af34f0b8cf364cdbbe6d093f8228a37f root=/dev/mapper/rhel-root ro rd.lvm.lv=rhel/swap vconsole.keymap=uk crashkernel=auto rd.lvm.lv=rhel/root vconsole.font=latarcyrheb-sun16 initrd16 /initramfs-0-rescue-af34f0b8cf364cdbbe6d093f8228a37f.img } ### END /etc/grub.d/10_linux ### ### BEGIN /etc/grub.d/20_linux_xen ### ### END /etc/grub.d/20_linux_xen ### ### BEGIN /etc/grub.d/20_ppc_terminfo ### ### END /etc/grub.d/20_ppc_terminfo ### ### BEGIN /etc/grub.d/30_os-prober ### ### END /etc/grub.d/30_os-prober ### ### BEGIN /etc/grub.d/40_custom ### # This file provides an easy way to add custom menu entries. Simply type the # menu entries you want to add after this comment. Be careful not to change # the 'exec tail' line above. ### END /etc/grub.d/40_custom ### ### BEGIN /etc/grub.d/41_custom ### if [ -f ${config_directory}/custom.cfg ]; then source ${config_directory}/custom.cfg elif [ -z "${config_directory}" -a -f $prefix/custom.cfg ]; then source $prefix/custom.cfg; fi ### END /etc/grub.d/41_custom ### xen-4.9.2/tools/pygrub/examples/fedora-19.grub20000664000175000017500000000704113256712137017377 0ustar smbsmb# # DO NOT EDIT THIS FILE # # It is automatically generated by grub2-mkconfig using templates # from /etc/grub.d and settings from /etc/default/grub # ### BEGIN /etc/grub.d/00_header ### if [ -s $prefix/grubenv ]; then load_env fi if [ "${next_entry}" ] ; then set default="${next_entry}" set next_entry= save_env next_entry set boot_once=true else set default="${saved_entry}" fi if [ x"${feature_menuentry_id}" = xy ]; then menuentry_id_option="--id" else menuentry_id_option="" fi export menuentry_id_option if [ "${prev_saved_entry}" ]; then set saved_entry="${prev_saved_entry}" save_env saved_entry set prev_saved_entry= save_env prev_saved_entry set boot_once=true fi function savedefault { if [ -z "${boot_once}" ]; then saved_entry="${chosen}" save_env saved_entry fi } function load_video { if [ x$feature_all_video_module = xy ]; then insmod all_video else insmod efi_gop insmod efi_uga insmod ieee1275_fb insmod vbe insmod vga insmod video_bochs insmod video_cirrus fi } terminal_output console set timeout=5 ### END /etc/grub.d/00_header ### ### BEGIN /etc/grub.d/10_linux ### menuentry 'Fedora, with Linux 3.9.6-301.fc19.i686.PAE' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-3.9.6-301.fc19.i686.PAE-advanced-738519ba-06da-4402-9b68-a1c1efada512' { load_video set gfxpayload=keep insmod gzio insmod part_msdos insmod ext2 set root='hd0,msdos1' if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root --hint='hd0,msdos1' 5001334f-de37-4b0b-bd2b-c1cea036f293 else search --no-floppy --fs-uuid --set=root 5001334f-de37-4b0b-bd2b-c1cea036f293 fi linux /vmlinuz-3.9.6-301.fc19.i686.PAE root=/dev/mapper/fedora-root ro rd.lvm.lv=fedora/swap rd.md=0 rd.dm=0 vconsole.keymap=us rd.luks=0 vconsole.font=latarcyrheb-sun16 rd.lvm.lv=fedora/root initrd /initramfs-3.9.6-301.fc19.i686.PAE.img } menuentry 'Fedora, with Linux 0-rescue-8d62a0261d8147dc84eb89292ebc1666' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-0-rescue-8d62a0261d8147dc84eb89292ebc1666-advanced-738519ba-06da-4402-9b68-a1c1efada512' { load_video insmod gzio insmod part_msdos insmod ext2 set root='hd0,msdos1' if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root --hint='hd0,msdos1' 5001334f-de37-4b0b-bd2b-c1cea036f293 else search --no-floppy --fs-uuid --set=root 5001334f-de37-4b0b-bd2b-c1cea036f293 fi linux /vmlinuz-0-rescue-8d62a0261d8147dc84eb89292ebc1666 root=/dev/mapper/fedora-root ro rd.lvm.lv=fedora/swap rd.md=0 rd.dm=0 vconsole.keymap=us rd.luks=0 vconsole.font=latarcyrheb-sun16 rd.lvm.lv=fedora/root initrd /initramfs-0-rescue-8d62a0261d8147dc84eb89292ebc1666.img } ### END /etc/grub.d/10_linux ### ### BEGIN /etc/grub.d/20_linux_xen ### ### END /etc/grub.d/20_linux_xen ### ### BEGIN /etc/grub.d/20_ppc_terminfo ### ### END /etc/grub.d/20_ppc_terminfo ### ### BEGIN /etc/grub.d/30_os-prober ### ### END /etc/grub.d/30_os-prober ### ### BEGIN /etc/grub.d/40_custom ### # This file provides an easy way to add custom menu entries. Simply type the # menu entries you want to add after this comment. Be careful not to change # the 'exec tail' line above. ### END /etc/grub.d/40_custom ### ### BEGIN /etc/grub.d/41_custom ### if [ -f ${config_directory}/custom.cfg ]; then source ${config_directory}/custom.cfg elif [ -z "${config_directory}" -a -f $prefix/custom.cfg ]; then source $prefix/custom.cfg; fi ### END /etc/grub.d/41_custom ### xen-4.9.2/tools/pygrub/examples/alpine-linux-2.3.2.extlinux0000664000175000017500000000055013256712137021612 0ustar smbsmbDEFAULT menu.c32 PROMPT 0 MENU TITLE Alpine/Linux Boot Menu MENU HIDDEN MENU AUTOBOOT Alpine will be booted automatically in # seconds. TIMEOUT 30 LABEL grsec MENU DEFAULT MENU LABEL Linux 3.0.10-grsec KERNEL vmlinuz-3.0.10-grsec APPEND initrd=initramfs-3.0.10-grsec root=UUID=a97ffe64-430f-4fd3-830e-4736d9a27af0 modules=sd-mod,usb-storage,ext4 quiet xen-4.9.2/tools/pygrub/examples/fedora-16-with-xen.grub20000664000175000017500000001032313256712137021132 0ustar smbsmb# # DO NOT EDIT THIS FILE # # It is automatically generated by grub2-mkconfig using templates # from /etc/grub.d and settings from /etc/default/grub # ### BEGIN /etc/grub.d/00_header ### if [ -s $prefix/grubenv ]; then load_env fi set default="${saved_entry}" if [ "${prev_saved_entry}" ]; then set saved_entry="${prev_saved_entry}" save_env saved_entry set prev_saved_entry= save_env prev_saved_entry set boot_once=true fi function savedefault { if [ -z "${boot_once}" ]; then saved_entry="${chosen}" save_env saved_entry fi } function load_video { insmod vbe insmod vga insmod video_bochs insmod video_cirrus } set timeout=5 ### END /etc/grub.d/00_header ### ### BEGIN /etc/grub.d/10_linux ### menuentry 'Fedora Linux, with Linux 3.1.0-0.rc9.git0.0.fc16.x86_64' --class fedora --class gnu-linux --class gnu --class os { load_video set gfxpayload=keep insmod gzio insmod part_gpt insmod ext2 set root='(hd0,gpt2)' search --no-floppy --fs-uuid --set=root f14ea58a-c2b3-4348-8091-3ab733a2f49e echo 'Loading Linux 3.1.0-0.rc9.git0.0.fc16.x86_64 ...' linux /vmlinuz-3.1.0-0.rc9.git0.0.fc16.x86_64 root=/dev/mapper/VolGroup-lv_root ro rd.md=0 rd.dm=0 rd.lvm.lv=VolGroup/lv_swap quiet SYSFONT=latarcyrheb-sun16 rhgb rd.lvm.lv=VolGroup/lv_root rd.luks=0 KEYTABLE=uk LANG=en_US.UTF-8 echo 'Loading initial ramdisk ...' initrd /initramfs-3.1.0-0.rc9.git0.0.fc16.x86_64.img } menuentry 'Fedora Linux, with Linux 3.1.0-0.rc9.git0.0.fc16.x86_64 (recovery mode)' --class fedora --class gnu-linux --class gnu --class os { load_video set gfxpayload=keep insmod gzio insmod part_gpt insmod ext2 set root='(hd0,gpt2)' search --no-floppy --fs-uuid --set=root f14ea58a-c2b3-4348-8091-3ab733a2f49e echo 'Loading Linux 3.1.0-0.rc9.git0.0.fc16.x86_64 ...' linux /vmlinuz-3.1.0-0.rc9.git0.0.fc16.x86_64 root=/dev/mapper/VolGroup-lv_root ro single rd.md=0 rd.dm=0 rd.lvm.lv=VolGroup/lv_swap quiet SYSFONT=latarcyrheb-sun16 rhgb rd.lvm.lv=VolGroup/lv_root rd.luks=0 KEYTABLE=uk LANG=en_US.UTF-8 echo 'Loading initial ramdisk ...' initrd /initramfs-3.1.0-0.rc9.git0.0.fc16.x86_64.img } ### END /etc/grub.d/10_linux ### ### BEGIN /etc/grub.d/20_linux_xen ### submenu "Xen 4.1" { menuentry 'Fedora Linux, with Xen 4.1 and Linux 3.1.0-0.rc9.git0.0.fc16.x86_64' --class fedora --class gnu-linux --class gnu --class os --class xen { insmod part_gpt insmod ext2 set root='(hd0,gpt2)' search --no-floppy --fs-uuid --set=root f14ea58a-c2b3-4348-8091-3ab733a2f49e echo 'Loading Xen 4.1 ...' multiboot /xen-4.1.gz placeholder echo 'Loading Linux 3.1.0-0.rc9.git0.0.fc16.x86_64 ...' module /vmlinuz-3.1.0-0.rc9.git0.0.fc16.x86_64 placeholder root=/dev/mapper/VolGroup-lv_root ro rd.md=0 rd.dm=0 rd.lvm.lv=VolGroup/lv_swap quiet SYSFONT=latarcyrheb-sun16 rhgb rd.lvm.lv=VolGroup/lv_root rd.luks=0 KEYTABLE=uk LANG=en_US.UTF-8 echo 'Loading initial ramdisk ...' module /initramfs-3.1.0-0.rc9.git0.0.fc16.x86_64.img } menuentry 'Fedora Linux, with Xen 4.1 and Linux 3.1.0-0.rc9.git0.0.fc16.x86_64 (recovery mode)' --class fedora --class gnu-linux --class gnu --class os --class xen { insmod part_gpt insmod ext2 set root='(hd0,gpt2)' search --no-floppy --fs-uuid --set=root f14ea58a-c2b3-4348-8091-3ab733a2f49e echo 'Loading Xen 4.1 ...' multiboot /xen-4.1.gz placeholder echo 'Loading Linux 3.1.0-0.rc9.git0.0.fc16.x86_64 ...' module /vmlinuz-3.1.0-0.rc9.git0.0.fc16.x86_64 placeholder root=/dev/mapper/VolGroup-lv_root ro single rd.md=0 rd.dm=0 rd.lvm.lv=VolGroup/lv_swap quiet SYSFONT=latarcyrheb-sun16 rhgb rd.lvm.lv=VolGroup/lv_root rd.luks=0 KEYTABLE=uk LANG=en_US.UTF-8 echo 'Loading initial ramdisk ...' module /initramfs-3.1.0-0.rc9.git0.0.fc16.x86_64.img } } ### END /etc/grub.d/20_linux_xen ### ### BEGIN /etc/grub.d/30_os-prober ### ### END /etc/grub.d/30_os-prober ### ### BEGIN /etc/grub.d/40_custom ### # This file provides an easy way to add custom menu entries. Simply type the # menu entries you want to add after this comment. Be careful not to change # the 'exec tail' line above. ### END /etc/grub.d/40_custom ### ### BEGIN /etc/grub.d/41_custom ### if [ -f $prefix/custom.cfg ]; then source $prefix/custom.cfg; fi ### END /etc/grub.d/41_custom ### ### BEGIN /etc/grub.d/90_persistent ### ### END /etc/grub.d/90_persistent ### xen-4.9.2/tools/pygrub/examples/debian-wheezy-hvm.grub20000664000175000017500000000601313256712137021231 0ustar smbsmb # # DO NOT EDIT THIS FILE # # It is automatically generated by grub-mkconfig using templates # from /etc/grub.d and settings from /etc/default/grub # ### BEGIN /etc/grub.d/00_header ### if [ -s $prefix/grubenv ]; then load_env fi set default="0" if [ "${prev_saved_entry}" ]; then set saved_entry="${prev_saved_entry}" save_env saved_entry set prev_saved_entry= save_env prev_saved_entry set boot_once=true fi function savedefault { if [ -z "${boot_once}" ]; then saved_entry="${chosen}" save_env saved_entry fi } function load_video { insmod vbe insmod vga insmod video_bochs insmod video_cirrus } insmod part_msdos insmod ext2 set root='(/dev/xvda,msdos1)' search --no-floppy --fs-uuid --set=root f395d5a0-4612-4872-806c-33cd37c152f0 if loadfont /usr/share/grub/unicode.pf2 ; then set gfxmode=640x480 load_video insmod gfxterm insmod part_msdos insmod ext2 set root='(/dev/xvda,msdos1)' search --no-floppy --fs-uuid --set=root f395d5a0-4612-4872-806c-33cd37c152f0 set locale_dir=($root)/boot/grub/locale set lang=en_US insmod gettext fi terminal_output gfxterm set timeout=5 ### END /etc/grub.d/00_header ### ### BEGIN /etc/grub.d/05_debian_theme ### set menu_color_normal=cyan/blue set menu_color_highlight=white/blue ### END /etc/grub.d/05_debian_theme ### ### BEGIN /etc/grub.d/10_linux ### menuentry 'Debian GNU/Linux, with Linux 3.2.0-4-amd64' --class debian --class gnu-linux --class gnu --class os { load_video insmod gzio insmod part_msdos insmod ext2 set root='(/dev/xvda,msdos1)' search --no-floppy --fs-uuid --set=root f395d5a0-4612-4872-806c-33cd37c152f0 echo 'Loading Linux 3.2.0-4-amd64 ...' linux /boot/vmlinuz-3.2.0-4-amd64 root=UUID=f395d5a0-4612-4872-806c-33cd37c152f0 ro echo 'Loading initial ramdisk ...' initrd /boot/initrd.img-3.2.0-4-amd64 } menuentry 'Debian GNU/Linux, with Linux 3.2.0-4-amd64 (recovery mode)' --class debian --class gnu-linux --class gnu --class os { load_video insmod gzio insmod part_msdos insmod ext2 set root='(/dev/xvda,msdos1)' search --no-floppy --fs-uuid --set=root f395d5a0-4612-4872-806c-33cd37c152f0 echo 'Loading Linux 3.2.0-4-amd64 ...' linux /boot/vmlinuz-3.2.0-4-amd64 root=UUID=f395d5a0-4612-4872-806c-33cd37c152f0 ro single echo 'Loading initial ramdisk ...' initrd /boot/initrd.img-3.2.0-4-amd64 } ### END /etc/grub.d/10_linux ### ### BEGIN /etc/grub.d/20_linux_xen ### ### END /etc/grub.d/20_linux_xen ### ### BEGIN /etc/grub.d/30_os-prober ### ### END /etc/grub.d/30_os-prober ### ### BEGIN /etc/grub.d/40_custom ### # This file provides an easy way to add custom menu entries. Simply type the # menu entries you want to add after this comment. Be careful not to change # the 'exec tail' line above. ### END /etc/grub.d/40_custom ### ### BEGIN /etc/grub.d/41_custom ### if [ -f $prefix/custom.cfg ]; then source $prefix/custom.cfg; fi ### END /etc/grub.d/41_custom ### xen-4.9.2/tools/pygrub/examples/ubuntu-14.04-lts.grub20000664000175000017500000001561413256712137020503 0ustar smbsmb# # DO NOT EDIT THIS FILE # # It is automatically generated by grub-mkconfig using templates # from /etc/grub.d and settings from /etc/default/grub # ### BEGIN /etc/grub.d/00_header ### if [ -s $prefix/grubenv ]; then set have_grubenv=true load_env fi if [ "${next_entry}" ] ; then set default="${next_entry}" set next_entry= save_env next_entry set boot_once=true else set default="0" fi if [ x"${feature_menuentry_id}" = xy ]; then menuentry_id_option="--id" else menuentry_id_option="" fi export menuentry_id_option if [ "${prev_saved_entry}" ]; then set saved_entry="${prev_saved_entry}" save_env saved_entry set prev_saved_entry= save_env prev_saved_entry set boot_once=true fi function savedefault { if [ -z "${boot_once}" ]; then saved_entry="${chosen}" save_env saved_entry fi } function recordfail { set recordfail=1 if [ -n "${have_grubenv}" ]; then if [ -z "${boot_once}" ]; then save_env recordfail; fi; fi } function load_video { if [ x$feature_all_video_module = xy ]; then insmod all_video else insmod efi_gop insmod efi_uga insmod ieee1275_fb insmod vbe insmod vga insmod video_bochs insmod video_cirrus fi } if [ x$feature_default_font_path = xy ] ; then font=unicode else insmod part_msdos insmod lvm insmod ext2 set root='lvmid/VFRfK8-JAgW-a2Rt-svO9-f06E-Frur-fzowWw/2zIHcW-s2DX-h7hm-V32p-6nz5-bH1A-fNVEWg' if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root --hint='lvmid/VFRfK8-JAgW-a2Rt-svO9-f06E-Frur-fzowWw/2zIHcW-s2DX-h7hm-V32p-6nz5-bH1A-fNVEWg' c7a4b4ca-71da-4d03-b374-a6b76ebcfc90 else search --no-floppy --fs-uuid --set=root c7a4b4ca-71da-4d03-b374-a6b76ebcfc90 fi font="/usr/share/grub/unicode.pf2" fi if loadfont $font ; then set gfxmode=auto load_video insmod gfxterm fi terminal_output gfxterm if [ "${recordfail}" = 1 ] ; then set timeout=-1 else if [ x$feature_timeout_style = xy ] ; then set timeout_style=menu set timeout=5 # Fallback normal timeout code in case the timeout_style feature is # unavailable. else set timeout=5 fi fi ### END /etc/grub.d/00_header ### ### BEGIN /etc/grub.d/05_debian_theme ### set menu_color_normal=white/black set menu_color_highlight=black/light-gray ### END /etc/grub.d/05_debian_theme ### ### BEGIN /etc/grub.d/10_linux ### function gfxmode { set gfxpayload="${1}" if [ "${1}" = "keep" ]; then set vt_handoff=vt.handoff=7 else set vt_handoff= fi } if [ "${recordfail}" != 1 ]; then if [ -e ${prefix}/gfxblacklist.txt ]; then if hwmatch ${prefix}/gfxblacklist.txt 3; then if [ ${match} = 0 ]; then set linux_gfx_mode=keep else set linux_gfx_mode=text fi else set linux_gfx_mode=text fi else set linux_gfx_mode=keep fi else set linux_gfx_mode=text fi export linux_gfx_mode menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-simple-c7a4b4ca-71da-4d03-b374-a6b76ebcfc90' { load_video gfxmode $linux_gfx_mode insmod gzio insmod part_msdos insmod ext2 if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a else search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a fi linux /vmlinuz-3.13.0-44-generic root=/dev/mapper/BoxenSys00-root ro biosdevname=0 quiet initrd /initrd.img-3.13.0-44-generic } submenu 'Advanced options for Ubuntu' --unrestricted $menuentry_id_option 'gnulinux-advanced-c7a4b4ca-71da-4d03-b374-a6b76ebcfc90' { menuentry 'Ubuntu, with Linux 3.13.0-44-generic' --class ubuntu --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.13.0-44-generic-advanced-c7a4b4ca-71da-4d03-b374-a6b76ebcfc90' { load_video gfxmode $linux_gfx_mode insmod gzio insmod part_msdos insmod ext2 if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a else search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a fi echo 'Loading Linux 3.13.0-44-generic ...' linux /vmlinuz-3.13.0-44-generic root=/dev/mapper/BoxenSys00-root ro biosdevname=0 quiet echo 'Loading initial ramdisk ...' initrd /initrd.img-3.13.0-44-generic } menuentry 'Ubuntu, with Linux 3.13.0-44-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.13.0-44-generic-recovery-c7a4b4ca-71da-4d03-b374-a6b76ebcfc90' { load_video insmod gzio insmod part_msdos insmod ext2 if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a else search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a fi echo 'Loading Linux 3.13.0-44-generic ...' linux /vmlinuz-3.13.0-44-generic root=/dev/mapper/BoxenSys00-root ro recovery nomodeset biosdevname=0 echo 'Loading initial ramdisk ...' initrd /initrd.img-3.13.0-44-generic } } ### END /etc/grub.d/10_linux ### ### BEGIN /etc/grub.d/20_linux_xen ### ### END /etc/grub.d/20_linux_xen ### ### BEGIN /etc/grub.d/20_memtest86+ ### menuentry 'Memory test (memtest86+)' { insmod part_msdos insmod ext2 if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a else search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a fi knetbsd /memtest86+.elf } menuentry 'Memory test (memtest86+, serial console 115200)' { insmod part_msdos insmod ext2 if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a else search --no-floppy --fs-uuid --set=root 86ba9198-4319-4809-908d-6dbe6938b19a fi linux16 /memtest86+.bin console=ttyS0,115200n8 } ### END /etc/grub.d/20_memtest86+ ### ### BEGIN /etc/grub.d/30_os-prober ### ### END /etc/grub.d/30_os-prober ### ### BEGIN /etc/grub.d/30_uefi-firmware ### ### END /etc/grub.d/30_uefi-firmware ### ### BEGIN /etc/grub.d/40_custom ### # This file provides an easy way to add custom menu entries. Simply type the # menu entries you want to add after this comment. Be careful not to change # the 'exec tail' line above. set superusers="root" password_pbkdf2 root grub.pbkdf2.sha512. ### END /etc/grub.d/40_custom ### ### BEGIN /etc/grub.d/40_custom.bakPW ### # This file provides an easy way to add custom menu entries. Simply type the # menu entries you want to add after this comment. Be careful not to change # the 'exec tail' line above. set superusers="root" password_pbkdf2 root dummy ### END /etc/grub.d/40_custom.bakPW ### ### BEGIN /etc/grub.d/41_custom ### if [ -f ${config_directory}/custom.cfg ]; then source ${config_directory}/custom.cfg elif [ -z "${config_directory}" -a -f $prefix/custom.cfg ]; then source $prefix/custom.cfg; fi ### END /etc/grub.d/41_custom ### xen-4.9.2/tools/pygrub/Makefile0000664000175000017500000000145013256712137014565 0ustar smbsmb XEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) PY_LDFLAGS = $(LDFLAGS) $(APPEND_LDFLAGS) .PHONY: all all: build .PHONY: build build: CC="$(CC)" CFLAGS="$(PY_CFLAGS)" $(PYTHON) setup.py build .PHONY: install install: all CC="$(CC)" CFLAGS="$(PY_CFLAGS)" LDFLAGS="$(PY_LDFLAGS)" $(PYTHON) \ setup.py install $(PYTHON_PREFIX_ARG) --root="$(DESTDIR)" \ --install-scripts=$(LIBEXEC_BIN) --force set -e; if [ $(bindir) != $(LIBEXEC_BIN) -a \ "`readlink -f $(DESTDIR)/$(bindir)`" != \ "`readlink -f $(LIBEXEC_BIN)`" ]; then \ ln -sf $(LIBEXEC_BIN)/pygrub $(DESTDIR)/$(bindir); \ fi .PHONY: clean clean: rm -rf build tmp *.pyc *.pyo *.o *.a *~ a.out $(DEPS) .PHONY: distclean distclean: clean -include $(DEPS) xen-4.9.2/tools/pygrub/README0000664000175000017500000000130413256712137014003 0ustar smbsmbpygrub is a grub-like bootloader for xen. This tool is to use to boot domU images. To compile pygrub, you will need the following packages installed: 1) Libraries of ext2fs, which is the following package (depend on your Linux distribution): - e2fslibs-dev on Debian based distributions (Debian, Ubuntu, Linspire, Libranet, Xandros, etc...) - e2fsprogs-devel on RedHat, Fedora Core - libext2fs2-devel on Mandriva/Mandrake - e2fsprogs on Gentoo 2) Libraries of reiserfs, which is the following package (depend on your Linux distribution): - libreiserfs-dev on Debian based distributions (Debian, Ubuntu, Xandros, Libranet, Xandros, etc...) - progsreiserfs-devel on RedHat - progsreiserfs on Gentoo xen-4.9.2/tools/pygrub/src/0000775000175000017500000000000013256712137013714 5ustar smbsmbxen-4.9.2/tools/pygrub/src/fsimage/0000775000175000017500000000000013256712137015327 5ustar smbsmbxen-4.9.2/tools/pygrub/src/fsimage/fsimage.c0000664000175000017500000002027313256712137017112 0ustar smbsmb/* * 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. * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #if (PYTHON_API_VERSION >= 1011) #define PY_PAD 0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L #else #define PY_PAD 0L,0L,0L,0L #endif typedef struct fsimage_fs { PyObject_HEAD fsi_t *fs; } fsimage_fs_t; typedef struct fsimage_file { PyObject_HEAD fsimage_fs_t *fs; fsi_file_t *file; } fsimage_file_t; struct foo { int ref; int size; long hash; int state; }; static PyObject * fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "size", "offset", NULL }; int bufsize; int size = 0; uint64_t offset = 0; ssize_t bytesread = 0; PyObject * buffer; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iL", kwlist, &size, &offset)) return (NULL); bufsize = size ? size : 4096; if ((buffer = PyString_FromStringAndSize(NULL, bufsize)) == NULL) return (NULL); while (1) { int err; void *buf = PyString_AS_STRING(buffer) + bytesread; err = fsi_pread_file(file->file, buf, bufsize, bytesread + offset); if (err == -1) { Py_DECREF(buffer); PyErr_SetFromErrno(PyExc_IOError); return (NULL); } else if (err == 0) { break; } bytesread += err; if (size != 0) { bufsize -= bytesread; if (bufsize == 0) break; } else { if (_PyString_Resize(&buffer, bytesread + bufsize) < 0) return (NULL); } } _PyString_Resize(&buffer, bytesread); return (buffer); } PyDoc_STRVAR(fsimage_file_read__doc__, "read(file, [size=size, offset=off])\n" "\n" "Read size bytes (or all bytes if not set) from the given " "file. If offset is specified as well, read from the given " "offset.\n"); static struct PyMethodDef fsimage_file_methods[] = { { "read", (PyCFunction) fsimage_file_read, METH_VARARGS|METH_KEYWORDS, fsimage_file_read__doc__ }, { NULL, NULL, 0, NULL } }; static PyObject * fsimage_file_getattr(fsimage_file_t *file, char *name) { return (Py_FindMethod(fsimage_file_methods, (PyObject *)file, name)); } static void fsimage_file_dealloc(fsimage_file_t *file) { if (file->file != NULL) fsi_close_file(file->file); Py_XDECREF(file->fs); PyObject_DEL(file); } static char fsimage_file_type__doc__[] = "Filesystem image file"; PyTypeObject fsimage_file_type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "fsimage.file", /* tp_name */ sizeof(fsimage_file_t), /* tp_size */ 0, /* tp_itemsize */ (destructor) fsimage_file_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc) fsimage_file_getattr, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ fsimage_file_type__doc__, PY_PAD }; static PyObject * fsimage_fs_open_file(fsimage_fs_t *fs, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "name", NULL }; fsimage_file_t *file; char *name; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) return (NULL); file = (fsimage_file_t *)PyObject_NEW(fsimage_file_t, &fsimage_file_type); if (file == NULL) return (NULL); file->fs = fs; Py_INCREF(file->fs); if ((file->file = fsi_open_file(fs->fs, name)) == NULL) { Py_DECREF(file->fs); file->fs = NULL; PyErr_SetFromErrno(PyExc_IOError); return (NULL); } return ((PyObject *)file); } static PyObject * fsimage_fs_file_exists(fsimage_fs_t *fs, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "name", NULL }; char *name; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) return (NULL); if (fsi_file_exists(fs->fs, name)) { Py_INCREF(Py_True); return (Py_True); } Py_INCREF(Py_False); return (Py_False); } PyDoc_STRVAR(fsimage_fs_open_file__doc__, "open_file(fs, filename) - lookup name in the given fs and return the file"); PyDoc_STRVAR(fsimage_fs_file_exists__doc__, "file_exists(fs, name) - lookup name in the given fs and return " "True if it exists"); static struct PyMethodDef fsimage_fs_methods[] = { { "open_file", (PyCFunction) fsimage_fs_open_file, METH_VARARGS|METH_KEYWORDS, fsimage_fs_open_file__doc__ }, { "file_exists", (PyCFunction) fsimage_fs_file_exists, METH_VARARGS|METH_KEYWORDS, fsimage_fs_file_exists__doc__ }, { NULL, NULL, 0, NULL } }; static PyObject * fsimage_fs_getattr(fsimage_fs_t *fs, char *name) { return (Py_FindMethod(fsimage_fs_methods, (PyObject *)fs, name)); } static void fsimage_fs_dealloc (fsimage_fs_t *fs) { if (fs->fs != NULL) fsi_close_fsimage(fs->fs); PyObject_DEL(fs); } PyDoc_STRVAR(fsimage_fs_type__doc__, "Filesystem image"); PyTypeObject fsimage_fs_type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "fsimage.fs", /* tp_name */ sizeof(fsimage_fs_t), /* tp_size */ 0, /* tp_itemsize */ (destructor) fsimage_fs_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc) fsimage_fs_getattr, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ fsimage_fs_type__doc__, PY_PAD }; static PyObject * fsimage_open(PyObject *o, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "name", "offset", "options", NULL }; char *name; char *options = NULL; uint64_t offset = 0; fsimage_fs_t *fs; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|Ls", kwlist, &name, &offset, &options)) return (NULL); if ((fs = PyObject_NEW(fsimage_fs_t, &fsimage_fs_type)) == NULL) return (NULL); if ((fs->fs = fsi_open_fsimage(name, offset, options)) == NULL) { PyErr_SetFromErrno(PyExc_IOError); return (NULL); } return (PyObject *)fs; } static PyObject * fsimage_getbootstring(PyObject *o, PyObject *args) { PyObject *fs; char *bootstring; fsi_t *fsi; if (!PyArg_ParseTuple(args, "O", &fs)) return (NULL); fsi = ((fsimage_fs_t *)fs)->fs; bootstring = fsi_fs_bootstring(fsi); return Py_BuildValue("s", bootstring); } PyDoc_STRVAR(fsimage_open__doc__, "open(name, [offset=off]) - Open the given file as a filesystem image.\n" "\n" "name - name of file to open.\n" "offset - offset of file system within file image.\n" "options - mount options string.\n"); PyDoc_STRVAR(fsimage_getbootstring__doc__, "getbootstring(fs) - Return the boot string needed for this file system " "or NULL if none is needed.\n"); static struct PyMethodDef fsimage_module_methods[] = { { "open", (PyCFunction)fsimage_open, METH_VARARGS|METH_KEYWORDS, fsimage_open__doc__ }, { "getbootstring", (PyCFunction)fsimage_getbootstring, METH_VARARGS, fsimage_getbootstring__doc__ }, { NULL, NULL, 0, NULL } }; PyMODINIT_FUNC initfsimage(void) { Py_InitModule("fsimage", fsimage_module_methods); } xen-4.9.2/tools/pygrub/src/pygrub0000775000175000017500000010066713256712137015164 0ustar smbsmb#! /usr/bin/env python # # pygrub - simple python-based bootloader for Xen # # Copyright 2005-2006 Red Hat, Inc. # Jeremy Katz # # This software may be freely redistributed under the terms of the GNU # general public license. # # You should have received a copy of the GNU General Public License # along with this program; If not, see . # import os, sys, string, struct, tempfile, re, traceback, stat, errno import copy import logging import platform import xen.lowlevel.xc import curses, _curses, curses.wrapper, curses.textpad, curses.ascii import getopt import fsimage import grub.GrubConf import grub.LiloConf import grub.ExtLinuxConf PYGRUB_VER = 0.6 FS_READ_MAX = 1024 * 1024 SECTOR_SIZE = 512 def read_size_roundup(fd, size): if platform.system() != 'FreeBSD': return size st = os.fstat(fd) if (not stat.S_ISCHR(st.st_mode)): return size # Round up to sector size if it's a raw character device return (((size)+((SECTOR_SIZE)-1))&(~((SECTOR_SIZE)-1))) def enable_cursor(ison): if ison: val = 2 else: val = 0 try: curses.curs_set(val) except _curses.error: pass DISK_TYPE_RAW, DISK_TYPE_HYBRIDISO, DISK_TYPE_DOS = range(3) def identify_disk_image(file): """Detect DOS partition table or HybridISO format.""" fd = os.open(file, os.O_RDONLY) buf = os.read(fd, read_size_roundup(fd, 0x8006)) os.close(fd) if len(buf) >= 512 and \ struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,): # HybridISO contains a DOS partition table for booting from USB devices, but really is an ISO image if len(buf) >= 0x8006 and buf[0x8001:0x8006] == 'CD001': return DISK_TYPE_HYBRIDISO return DISK_TYPE_DOS return DISK_TYPE_RAW DK_LABEL_LOC=1 DKL_MAGIC=0xdabe V_ROOT=0x2 def get_solaris_slice(file, offset): """Find the root slice in a Solaris VTOC.""" fd = os.open(file, os.O_RDONLY) os.lseek(fd, offset + (DK_LABEL_LOC * SECTOR_SIZE), 0) buf = os.read(fd, 512) os.close(fd) if struct.unpack("0: buf = os.read(fd, read_size_roundup(fd, partsize)) offsets.append(struct.unpack(" ") screen.noutrefresh() win = curses.newwin(1, 74, startx, starty + 2) curses.textpad.Textbox.__init__(self, win) self.line = list(line) self.pos = len(line) self.cancelled = False self.show_text() def show_text(self): """Show the text. One of our advantages over standard textboxes is that we can handle lines longer than the window.""" self.win.erase() p = self.pos off = 0 while p > 70: p -= 55 off += 55 l = self.line[off:off+70] self.win.addstr(0, 0, string.join(l, (""))) if self.pos > 70: self.win.addch(0, 0, curses.ACS_LARROW) self.win.move(0, p) def do_command(self, ch): # we handle escape as well as moving the line around, so have # to override some of the default handling self.lastcmd = ch if ch == 27: # esc self.cancelled = True return 0 elif curses.ascii.isprint(ch): self.line.insert(self.pos, chr(ch)) self.pos += 1 elif ch == curses.ascii.SOH: # ^a self.pos = 0 elif ch in (curses.ascii.STX,curses.KEY_LEFT): if self.pos > 0: self.pos -= 1 elif ch in (curses.ascii.BS,curses.KEY_BACKSPACE): if self.pos > 0: self.pos -= 1 if self.pos < len(self.line): self.line.pop(self.pos) elif ch == curses.ascii.EOT: # ^d if self.pos < len(self.line): self.line.pop(self.pos) elif ch == curses.ascii.ENQ: # ^e self.pos = len(self.line) elif ch in (curses.ascii.ACK, curses.KEY_RIGHT): if self.pos < len(self.line): self.pos +=1 elif ch == curses.ascii.VT: # ^k self.line = self.line[:self.pos] else: return curses.textpad.Textbox.do_command(self, ch) self.show_text() return 1 def edit(self): curses.doupdate() r = curses.textpad.Textbox.edit(self) if self.cancelled: return None return string.join(self.line, "") class Grub: ENTRY_WIN_LINES = 8 def __init__(self, file, fs = None): self.screen = None self.entry_win = None self.text_win = None if file: self.read_config(file, fs) def draw_main_windows(self): if self.screen is None: #only init stuff once self.screen = curses.initscr() self.screen.timeout(1000) if hasattr(curses, 'use_default_colors'): try: curses.use_default_colors() except: pass # Not important if we can't use colour enable_cursor(False) self.entry_win = curses.newwin(Grub.ENTRY_WIN_LINES + 2, 74, 2, 1) self.text_win = curses.newwin(10, 70, 12, 5) curses.def_prog_mode() curses.reset_prog_mode() self.screen.erase() # create basic grub screen with a box of entries and a textbox self.screen.addstr(1, 4, "pyGRUB version %s" %(PYGRUB_VER,)) self.entry_win.box() self.screen.noutrefresh() def fill_entry_list(self): self.entry_win.erase() self.entry_win.box() maxy = self.entry_win.getmaxyx()[0]-3 # maxy - 2 for the frame + index if self.selected_image > self.start_image + maxy: self.start_image = self.selected_image if self.selected_image < self.start_image: self.start_image = self.selected_image for y in range(self.start_image, len(self.cf.images)): i = self.cf.images[y] if y > self.start_image + maxy: break if y == self.selected_image: self.entry_win.attron(curses.A_REVERSE) self.entry_win.addstr(y + 1 - self.start_image, 2, i.title.expandtabs().ljust(70)) if y == self.selected_image: self.entry_win.attroff(curses.A_REVERSE) self.entry_win.noutrefresh() def edit_entry(self, origimg): def draw(): self.draw_main_windows() self.text_win.addstr(0, 0, "Use the U and D keys to select which entry is highlighted.") self.text_win.addstr(1, 0, "Press 'b' to boot, 'e' to edit the selected command in the") self.text_win.addstr(2, 0, "boot sequence, 'c' for a command-line, 'o' to open a new line") self.text_win.addstr(3, 0, "after ('O' for before) the selected line, 'd' to remove the") self.text_win.addstr(4, 0, "selected line, or escape to go back to the main menu.") self.text_win.addch(0, 8, curses.ACS_UARROW) self.text_win.addch(0, 14, curses.ACS_DARROW) (y, x) = self.text_win.getmaxyx() self.text_win.move(y - 1, x - 1) self.text_win.noutrefresh() curline = 0 pos = 0 img = copy.deepcopy(origimg) while 1: draw() self.entry_win.erase() rs = 0 re = len(img.lines) idp = 1 if re > Grub.ENTRY_WIN_LINES: rs = curline - pos re = rs + Grub.ENTRY_WIN_LINES for idx in range(rs, re): # current line should be highlighted if idx == curline: self.entry_win.attron(curses.A_REVERSE) # trim the line l = img.lines[idx].expandtabs().ljust(70) if len(l) > 70: l = l[:69] + ">" self.entry_win.addstr(idp, 2, l) if idx == curline: self.entry_win.attroff(curses.A_REVERSE) idp += 1 self.entry_win.box() self.entry_win.noutrefresh() curses.doupdate() c = self.screen.getch() if c in (ord('q'), 27): # 27 == esc break elif c == curses.KEY_UP: curline -= 1 if pos > 0: pos -= 1 elif c == curses.KEY_DOWN: curline += 1 if pos < Grub.ENTRY_WIN_LINES - 1: pos += 1 elif c == ord('b'): self.isdone = True break elif c == ord('e'): l = self.edit_line(img.lines[curline]) if l is not None: img.set_from_line(l, replace = curline) elif c == ord('d'): img.lines.pop(curline) elif c == ord('o'): img.lines.insert(curline+1, "") curline += 1 elif c == ord('O'): img.lines.insert(curline, "") elif c == ord('c'): self.command_line_mode() if self.isdone: return # bound at the top and bottom if curline < 0: curline = 0 elif curline >= len(img.lines): curline = len(img.lines) - 1 if self.isdone: origimg.reset(img.lines) def edit_line(self, line): self.screen.erase() self.screen.addstr(1, 2, "[ Minimal BASH-like line editing is supported. ") self.screen.addstr(2, 2, " ESC at any time cancels. ENTER at any time accepts your changes. ]") self.screen.noutrefresh() t = GrubLineEditor(self.screen, 5, 2, line) enable_cursor(True) ret = t.edit() if ret: return ret return None def command_line_mode(self): self.screen.erase() self.screen.addstr(1, 2, "[ Minimal BASH-like line editing is supported. ESC at any time ") self.screen.addstr(2, 2, " exits. Typing 'boot' will boot with your entered commands. ] ") self.screen.noutrefresh() y = 5 lines = [] while 1: t = GrubLineEditor(self.screen, y, 2) enable_cursor(True) ret = t.edit() if ret: if ret in ("quit", "return"): break elif ret != "boot": y += 1 lines.append(ret) continue # if we got boot, then we want to boot the entered image img = self.cf.new_image("entered", lines) self.cf.add_image(img) self.selected_image = len(self.cf.images) - 1 self.isdone = True break # else, we cancelled and should just go back break def read_config(self, fn, fs = None): """Read the given file to parse the config. If fs = None, then we're being given a raw config file rather than a disk image.""" if not os.access(fn, os.R_OK): raise RuntimeError, "Unable to access %s" %(fn,) cfg_list = map(lambda x: (x,grub.GrubConf.Grub2ConfigFile), ["/boot/grub/grub.cfg", "/grub/grub.cfg", "/boot/grub2/grub.cfg", "/grub2/grub.cfg"]) + \ map(lambda x: (x,grub.ExtLinuxConf.ExtLinuxConfigFile), ["/boot/isolinux/isolinux.cfg", "/boot/extlinux/extlinux.conf", "/boot/extlinux.conf", "/extlinux/extlinux.conf", "/extlinux.conf"]) + \ map(lambda x: (x,grub.GrubConf.GrubConfigFile), ["/boot/grub/menu.lst", "/boot/grub/grub.conf", "/grub/menu.lst", "/grub/grub.conf"]) if not fs: # set the config file and parse it for f,parser in cfg_list: self.cf = parser() self.cf.filename = fn self.cf.parse() return for f,parser in cfg_list: if fs.file_exists(f): print >>sys.stderr, "Using %s to parse %s" % (parser,f) self.cf = parser() self.cf.filename = f break if self.__dict__.get('cf', None) is None: raise RuntimeError, "couldn't find bootloader config file in the image provided." f = fs.open_file(self.cf.filename) # limit read size to avoid pathological cases buf = f.read(FS_READ_MAX) del f self.cf.parse(buf) def image_index(self): if isinstance(self.cf.default, int): sel = self.cf.default elif self.cf.default.isdigit(): sel = int(self.cf.default) else: # We don't fully support submenus. Look for the leaf value in # "submenu0>submenu1>...>menuentry" and hope that it's unique. title = self.cf.default while 1: try: title = re.search('(\S)>(\S.+$)',title).group(2) except AttributeError: break # Map string to index in images array sel = 0 for i in range(len(self.cf.images)): if self.cf.images[i].title == title: sel = i break # If the selected (default) image doesn't exist we select the first entry if sel > len(self.cf.images): logging.warning("Default image not found") sel = 0 return sel def run(self): timeout = int(self.cf.timeout) self.selected_image = self.image_index() self.isdone = False while not self.isdone: self.run_main(timeout) timeout = -1 return self.selected_image def run_main(self, timeout = -1): def draw(): # set up the screen self.draw_main_windows() if not self.cf.hasPassword() or self.cf.hasPasswordAccess(): self.text_win.addstr(0, 0, "Use the U and D keys to select which entry is highlighted.") self.text_win.addstr(1, 0, "Press enter to boot the selected OS, 'e' to edit the") self.text_win.addstr(2, 0, "commands before booting, 'a' to modify the kernel arguments ") self.text_win.addstr(3, 0, "before booting, or 'c' for a command line.") else: self.text_win.addstr(0, 0, "Use the U and D keys to select which entry is highlighted.") self.text_win.addstr(1, 0, "Press enter to boot the selected OS or `p` to enter a") self.text_win.addstr(2, 0, "password to unlock the next set of features.") self.text_win.addch(0, 8, curses.ACS_UARROW) self.text_win.addch(0, 14, curses.ACS_DARROW) (y, x) = self.text_win.getmaxyx() self.text_win.move(y - 1, x - 1) self.text_win.noutrefresh() # now loop until we hit the timeout or get a go from the user mytime = 0 self.start_image = 0 while (timeout == -1 or mytime < int(timeout)): draw() if timeout != -1 and mytime != -1: self.screen.addstr(20, 5, "Will boot selected entry in %2d seconds" %(int(timeout) - mytime)) else: self.screen.addstr(20, 5, " " * 80) self.fill_entry_list() curses.doupdate() c = self.screen.getch() if c == -1: # Timed out waiting for a keypress if mytime != -1: mytime += 1 if mytime >= int(timeout): self.isdone = True break else: # received a keypress: stop the timer mytime = -1 self.screen.timeout(-1) # handle keypresses if c == ord('c') and self.cf.hasPasswordAccess(): self.command_line_mode() break elif c == ord('a') and self.cf.hasPasswordAccess(): # find the kernel line, edit it and then boot img = self.cf.images[self.selected_image] for line in img.lines: if line.startswith("kernel") or line.startswith("linux"): l = self.edit_line(line) if l is not None: img.set_from_line(l, replace = True) self.isdone = True break break elif c == ord('e') and self.cf.hasPasswordAccess(): img = self.cf.images[self.selected_image] self.edit_entry(img) break elif c == ord('p') and self.cf.hasPassword(): self.text_win.addstr(6, 1, "Password: ") pwd = self.text_win.getstr(6, 8) if not self.cf.checkPassword(pwd): self.text_win.addstr(6, 1, "Password: ") if self.cf.passExc is not None: self.text_win.addstr(7, 0, "Exception: %s" % self.cf.passExc) else: self.text_win.addstr(7, 0, "Failed!") self.cf.setPasswordAccess( False ) else: self.cf.setPasswordAccess( True ) break elif c in (curses.KEY_ENTER, ord('\n'), ord('\r')): self.isdone = True break elif c == curses.KEY_UP: self.selected_image -= 1 elif c == curses.KEY_DOWN: self.selected_image += 1 # elif c in (ord('q'), 27): # 27 == esc # self.selected_image = -1 # self.isdone = True # break # bound at the top and bottom if self.selected_image < 0: self.selected_image = 0 elif self.selected_image >= len(self.cf.images): self.selected_image = len(self.cf.images) - 1 def get_entry_idx(cf, entry): # first, see if the given entry is numeric try: idx = string.atoi(entry) return idx except ValueError: pass # it's not, now check the labels for a match for i in range(len(cf.images)): if entry == cf.images[i].title: return i return None def run_grub(file, entry, fs, cfg_args): global g global sel def run_main(scr, *args): global sel global g sel = g.run() g = Grub(file, fs) if list_entries: for i in range(len(g.cf.images)): img = g.cf.images[i] print "title: %s" % img.title print " root: %s" % img.root print " kernel: %s" % img.kernel[1] print " args: %s" % img.args print " initrd: %s" % img.initrd[1] if interactive and not list_entries: curses.wrapper(run_main) else: sel = g.image_index() # set the entry to boot as requested if entry is not None: idx = get_entry_idx(g.cf, entry) if idx is not None and idx >= 0 and idx < len(g.cf.images): sel = idx if sel == -1: print "No kernel image selected!" sys.exit(1) try: img = g.cf.images[sel] except IndexError: img = g.cf.images[0] grubcfg = { "kernel": None, "ramdisk": None, "args": "" } grubcfg["kernel"] = img.kernel[1] if img.initrd: grubcfg["ramdisk"] = img.initrd[1] if img.args: grubcfg["args"] += img.args if cfg_args: grubcfg["args"] += " " + cfg_args return grubcfg def supports64bitPVguest(): xc = xen.lowlevel.xc.xc() caps = xc.xeninfo()['xen_caps'].split(" ") for cap in caps: if cap == "xen-3.0-x86_64": return True return False # If nothing has been specified, look for a Solaris domU. If found, perform the # necessary tweaks. def sniff_solaris(fs, cfg): if not fs.file_exists("/platform/i86xpv/kernel/unix") and \ not fs.file_exists("/platform/i86xpv/kernel/amd64/unix"): return cfg if not cfg["kernel"]: if supports64bitPVguest() and \ fs.file_exists("/platform/i86xpv/kernel/amd64/unix"): cfg["kernel"] = "/platform/i86xpv/kernel/amd64/unix" cfg["ramdisk"] = "/platform/i86pc/amd64/boot_archive" elif fs.file_exists("/platform/i86xpv/kernel/unix"): cfg["kernel"] = "/platform/i86xpv/kernel/unix" cfg["ramdisk"] = "/platform/i86pc/boot_archive" else: return cfg # Unpleasant. Typically we'll have 'root=foo -k' or 'root=foo /kernel -k', # and we need to maintain Xen properties (root= and ip=) and the kernel # before any user args. xenargs = "" userargs = "" if not cfg["args"]: cfg["args"] = cfg["kernel"] else: for arg in cfg["args"].split(): if re.match("^root=", arg) or re.match("^ip=", arg): xenargs += arg + " " elif arg != cfg["kernel"]: userargs += arg + " " cfg["args"] = xenargs + " " + cfg["kernel"] + " " + userargs return cfg def sniff_netware(fs, cfg): if not fs.file_exists("/nwserver/xnloader.sys"): return cfg if not cfg["kernel"]: cfg["kernel"] = "/nwserver/xnloader.sys" return cfg def format_sxp(kernel, ramdisk, args): s = "linux (kernel %s)" % repr(kernel) if ramdisk: s += "(ramdisk %s)" % repr(ramdisk) if args: s += "(args %s)" % repr(args) return s def format_simple(kernel, ramdisk, args, sep): for check in (kernel, ramdisk, args): if check is not None and sep in check: raise RuntimeError, "simple format cannot represent delimiter-containing value" s = ("kernel %s" % kernel) + sep if ramdisk: s += ("ramdisk %s" % ramdisk) + sep if args: s += ("args %s" % args) + sep s += sep return s if __name__ == "__main__": sel = None def usage(): print >> sys.stderr, "Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] " %(sys.argv[0],) def copy_from_image(fs, file_to_read, file_type, output_directory, not_really): if not_really: if fs.file_exists(file_to_read): return "<%s:%s>" % (file_type, file_to_read) else: sys.exit("The requested %s file does not exist" % file_type) try: datafile = fs.open_file(file_to_read) except Exception, e: print >>sys.stderr, e sys.exit("Error opening %s in guest" % file_to_read) (tfd, ret) = tempfile.mkstemp(prefix="boot_"+file_type+".", dir=output_directory) dataoff = 0 while True: data = datafile.read(FS_READ_MAX, dataoff) if len(data) == 0: os.close(tfd) del datafile return ret try: os.write(tfd, data) except Exception, e: print >>sys.stderr, e os.close(tfd) os.unlink(ret) del datafile sys.exit("Error writing temporary copy of "+file_type) dataoff += len(data) try: opts, args = getopt.gnu_getopt(sys.argv[1:], 'qilnh::', ["quiet", "interactive", "list-entries", "not-really", "help", "output=", "output-format=", "output-directory=", "offset=", "entry=", "kernel=", "ramdisk=", "args=", "isconfig", "debug"]) except getopt.GetoptError: usage() sys.exit(1) if len(args) < 1: usage() sys.exit(1) file = args[0] fs = None output = None entry = None interactive = True list_entries = False isconfig = False part_offs = None debug = False not_really = False output_format = "sxp" output_directory = "/var/run/xen/pygrub" # what was passed in incfg = { "kernel": None, "ramdisk": None, "args": "" } # what grub or sniffing chose chosencfg = { "kernel": None, "ramdisk": None, "args": None } # what to boot bootcfg = { "kernel": None, "ramdisk": None, "args": None } for o, a in opts: if o in ("-q", "--quiet"): interactive = False elif o in ("-i", "--interactive"): interactive = True elif o in ("-l", "--list-entries"): list_entries = True elif o in ("-n", "--not-really"): not_really = True elif o in ("-h", "--help"): usage() sys.exit() elif o in ("--output",): output = a elif o in ("--kernel",): incfg["kernel"] = a elif o in ("--ramdisk",): incfg["ramdisk"] = a elif o in ("--args",): incfg["args"] = a elif o in ("--offset",): try: part_offs = [ int(a) ] except ValueError: print "offset value must be an integer" usage() sys.exit(1) elif o in ("--entry",): entry = a # specifying the entry to boot implies non-interactive interactive = False elif o in ("--isconfig",): isconfig = True elif o in ("--debug",): debug = True elif o in ("--output-format",): if a not in ["sxp", "simple", "simple0"]: print "unknown output format %s" % a usage() sys.exit(1) output_format = a elif o in ("--output-directory",): if not os.path.isdir(a): print "%s is not an existing directory" % a sys.exit(1) output_directory = a if debug: logging.basicConfig(level=logging.DEBUG) try: os.makedirs(output_directory, 0700) except OSError,e: if (e.errno == errno.EEXIST) and os.path.isdir(output_directory): pass else: raise if output is None or output == "-": fd = sys.stdout.fileno() else: fd = os.open(output, os.O_WRONLY) # debug if isconfig: chosencfg = run_grub(file, entry, fs, incfg["args"]) print " kernel: %s" % chosencfg["kernel"] if chosencfg["ramdisk"]: print " initrd: %s" % chosencfg["ramdisk"] print " args: %s" % chosencfg["args"] sys.exit(0) # if boot filesystem is set then pass to fsimage.open bootfsargs = '"%s"' % incfg["args"] bootfsgroup = re.findall('zfs-bootfs=(.*?)[\s\,\"]', bootfsargs) if bootfsgroup: bootfsoptions = bootfsgroup[0] else: bootfsoptions = "" # get list of offsets into file which start partitions if part_offs is None: part_offs = get_partition_offsets(file) for offset in part_offs: try: fs = fsimage.open(file, offset, bootfsoptions) chosencfg = sniff_solaris(fs, incfg) if not chosencfg["kernel"]: chosencfg = sniff_netware(fs, incfg) if not chosencfg["kernel"]: chosencfg = run_grub(file, entry, fs, incfg["args"]) # Break as soon as we've found the kernel so that we continue # to use this fsimage object if chosencfg["kernel"]: break fs = None except: # IOErrors raised by fsimage.open # RuntimeErrors raised by run_grub if no menu.lst present if debug: traceback.print_exc() fs = None continue if list_entries: sys.exit(0) # Did looping through partitions find us a kernel? if fs is None: raise RuntimeError, "Unable to find partition containing kernel" bootcfg["kernel"] = copy_from_image(fs, chosencfg["kernel"], "kernel", output_directory, not_really) if chosencfg["ramdisk"]: try: bootcfg["ramdisk"] = copy_from_image(fs, chosencfg["ramdisk"], "ramdisk", output_directory, not_really) except: if not not_really: os.unlink(bootcfg["kernel"]) raise else: initrd = None args = None if chosencfg["args"]: zfsinfo = fsimage.getbootstring(fs) if zfsinfo is not None: e = re.compile("zfs-bootfs=[\w\-\.\:@/]+" ) (chosencfg["args"],count) = e.subn(zfsinfo, chosencfg["args"]) if count == 0: chosencfg["args"] += " -B %s" % zfsinfo args = chosencfg["args"] if output_format == "sxp": ostring = format_sxp(bootcfg["kernel"], bootcfg["ramdisk"], args) elif output_format == "simple": ostring = format_simple(bootcfg["kernel"], bootcfg["ramdisk"], args, "\n") elif output_format == "simple0": ostring = format_simple(bootcfg["kernel"], bootcfg["ramdisk"], args, "\0") sys.stdout.flush() os.write(fd, ostring) xen-4.9.2/tools/pygrub/src/__init__.py0000664000175000017500000000000013256712137016013 0ustar smbsmbxen-4.9.2/tools/pygrub/src/GrubConf.py0000664000175000017500000003425513256712137016004 0ustar smbsmb# # GrubConf.py - Simple grub.conf parsing # # Copyright 2009 Citrix Systems Inc. # Copyright 2005-2006 Red Hat, Inc. # Jeremy Katz # # This software may be freely redistributed under the terms of the GNU # general public license. # # You should have received a copy of the GNU General Public License # along with this program; If not, see . # import os, sys import logging import re def grub_split(s, maxsplit = -1): eq = s.find('=') if eq == -1: return s.split(None, maxsplit) # see which of a space or tab is first sp = s.find(' ') tab = s.find('\t') if (tab != -1 and tab < sp) or (tab != -1 and sp == -1): sp = tab if eq != -1 and eq < sp or (eq != -1 and sp == -1): return s.split('=', maxsplit) else: return s.split(None, maxsplit) def grub_exact_split(s, num): ret = grub_split(s, num - 1) if len(ret) < num: return ret + [""] * (num - len(ret)) return ret def get_path(s): """Returns a tuple of (GrubDiskPart, path) corresponding to string.""" if not s.startswith('('): return (None, s) idx = s.find(')') if idx == -1: raise ValueError, "Unable to find matching ')'" d = s[:idx] return (GrubDiskPart(d), s[idx + 1:]) class GrubDiskPart(object): def __init__(self, str): if str.find(',') != -1: (self.disk, self.part) = str.split(",", 2) else: self.disk = str self.part = None def __repr__(self): if self.part is not None: return "d%dp%d" %(self.disk, self.part) else: return "d%d" %(self.disk,) def get_disk(self): return self._disk def set_disk(self, val): val = val.replace("(", "").replace(")", "") if val.startswith("/dev/xvd"): disk = val[len("/dev/xvd")] self._disk = ord(disk)-ord('a') else: self._disk = int(val[2:]) disk = property(get_disk, set_disk) def get_part(self): return self._part def set_part(self, val): if val is None: self._part = val return val = val.replace("(", "").replace(")", "") if val[:5] == "msdos": val = val[5:] if val[:3] == "gpt": val = val[3:] self._part = int(val) part = property(get_part, set_part) class _GrubImage(object): def __init__(self, title, lines): self.reset(lines) self.title = title.strip() def __repr__(self): return ("title: %s\n" " root: %s\n" " kernel: %s\n" " args: %s\n" " initrd: %s\n" %(self.title, self.root, self.kernel, self.args, self.initrd)) def _parse(self, lines): map(self.set_from_line, lines) def reset(self, lines): self._root = self._initrd = self._kernel = self._args = None self.lines = [] self._parse(lines) def set_root(self, val): self._root = GrubDiskPart(val) def get_root(self): return self._root root = property(get_root, set_root) def set_kernel(self, val): if val.find(" ") == -1: self._kernel = get_path(val) self._args = None return (kernel, args) = val.split(None, 1) self._kernel = get_path(kernel) self._args = args def get_kernel(self): return self._kernel def get_args(self): return self._args kernel = property(get_kernel, set_kernel) args = property(get_args) def set_initrd(self, val): self._initrd = get_path(val) def get_initrd(self): return self._initrd initrd = property(get_initrd, set_initrd) class GrubImage(_GrubImage): def __init__(self, title, lines): _GrubImage.__init__(self, title, lines) def set_from_line(self, line, replace = None): (com, arg) = grub_exact_split(line, 2) if self.commands.has_key(com): if self.commands[com] is not None: setattr(self, self.commands[com], arg.strip()) else: logging.info("Ignored image directive %s" %(com,)) else: logging.warning("Unknown image directive %s" %(com,)) # now put the line in the list of lines if replace is None: self.lines.append(line) else: self.lines.pop(replace) self.lines.insert(replace, line) # set up command handlers commands = { "root": "root", "rootnoverify": "root", "kernel": "kernel", "initrd": "initrd", "chainloader": None, "module": None} class _GrubConfigFile(object): def __init__(self, fn = None): self.filename = fn self.images = [] self.timeout = -1 self._default = 0 self.passwordAccess = True self.passExc = None if fn is not None: self.parse() def parse(self, buf = None): raise RuntimeError, "unimplemented parse function" def hasPasswordAccess(self): return self.passwordAccess def setPasswordAccess(self, val): self.passwordAccess = val def hasPassword(self): return hasattr(self, 'password') def checkPassword(self, password): # Always allow if no password defined in grub.conf if not self.hasPassword(): return True pwd = getattr(self, 'password').split() # We check whether password is in MD5 hash for comparison if pwd[0] == '--md5': try: import crypt if crypt.crypt(password, pwd[1]) == pwd[1]: return True except Exception, e: self.passExc = "Can't verify password: %s" % str(e) return False # ... and if not, we compare it as a plain text if pwd[0] == password: return True return False def set(self, line): (com, arg) = grub_exact_split(line, 2) if self.commands.has_key(com): if self.commands[com] is not None: setattr(self, self.commands[com], arg.strip()) else: logging.info("Ignored directive %s" %(com,)) else: logging.warning("Unknown directive %s" %(com,)) def add_image(self, image): self.images.append(image) def _get_default(self): return self._default def _set_default(self, val): if val == "saved": self._default = 0 else: self._default = val if self._default < 0: raise ValueError, "default must be positive number" default = property(_get_default, _set_default) def set_splash(self, val): self._splash = get_path(val) def get_splash(self): return self._splash splash = property(get_splash, set_splash) # set up command handlers commands = { "default": "default", "timeout": "timeout", "fallback": "fallback", "hiddenmenu": "hiddenmenu", "splashimage": "splash", "password": "password" } for c in ("bootp", "color", "device", "dhcp", "hide", "ifconfig", "pager", "partnew", "parttype", "rarp", "serial", "setkey", "terminal", "terminfo", "tftpserver", "unhide"): commands[c] = None del c class GrubConfigFile(_GrubConfigFile): def __init__(self, fn = None): _GrubConfigFile.__init__(self,fn) def new_image(self, title, lines): return GrubImage(title, lines) def parse(self, buf = None): if buf is None: if self.filename is None: raise ValueError, "No config file defined to parse!" f = open(self.filename, 'r') lines = f.readlines() f.close() else: lines = buf.split("\n") img = None title = "" for l in lines: l = l.strip() # skip blank lines if len(l) == 0: continue # skip comments if l.startswith('#'): continue # new image if l.startswith("title"): if img is not None: self.add_image(GrubImage(title, img)) img = [] title = l[6:] continue if img is not None: img.append(l) continue (com, arg) = grub_exact_split(l, 2) if self.commands.has_key(com): if self.commands[com] is not None: setattr(self, self.commands[com], arg.strip()) else: logging.info("Ignored directive %s" %(com,)) else: logging.warning("Unknown directive %s" %(com,)) if img: self.add_image(GrubImage(title, img)) if self.hasPassword(): self.setPasswordAccess(False) def grub2_handle_set(arg): (com,arg) = grub_split(arg,2) com="set:" + com m = re.match("([\"\'])(.*)\\1", arg) if m is not None: arg=m.group(2) return (com,arg) class Grub2Image(_GrubImage): def __init__(self, title, lines): _GrubImage.__init__(self, title, lines) def set_from_line(self, line, replace = None): (com, arg) = grub_exact_split(line, 2) if com == "set": (com,arg) = grub2_handle_set(arg) if self.commands.has_key(com): if self.commands[com] is not None: setattr(self, self.commands[com], arg.strip()) else: logging.info("Ignored image directive %s" %(com,)) elif com.startswith('set:'): pass else: logging.warning("Unknown image directive %s" %(com,)) # now put the line in the list of lines if replace is None: self.lines.append(line) else: self.lines.pop(replace) self.lines.insert(replace, line) commands = {'set:root': 'root', 'linux': 'kernel', 'linux16': 'kernel', 'initrd': 'initrd', 'initrd16': 'initrd', 'echo': None, 'insmod': None, 'search': None} class Grub2ConfigFile(_GrubConfigFile): def __init__(self, fn = None): _GrubConfigFile.__init__(self, fn) def new_image(self, title, lines): return Grub2Image(title, lines) def parse(self, buf = None): if buf is None: if self.filename is None: raise ValueError, "No config file defined to parse!" f = open(self.filename, 'r') lines = f.readlines() f.close() else: lines = buf.split("\n") in_function = False img = None title = "" menu_level=0 for l in lines: l = l.strip() # skip blank lines if len(l) == 0: continue # skip comments if l.startswith('#'): continue # skip function declarations if l.startswith('function'): in_function = True continue if in_function: if l.startswith('}'): in_function = False continue # new image title_match = re.match('^menuentry ["\'](.*?)["\'] (.*){', l) if title_match: if img is not None: raise RuntimeError, "syntax error: cannot nest menuentry (%d %s)" % (len(img),img) img = [] title = title_match.group(1) continue if l.startswith("submenu"): menu_level += 1 continue if l.startswith("}"): if img is None: if menu_level > 0: menu_level -= 1 continue else: raise RuntimeError, "syntax error: closing brace without menuentry" self.add_image(Grub2Image(title, img)) img = None continue if img is not None: img.append(l) continue (com, arg) = grub_exact_split(l, 2) if com == "set": (com,arg) = grub2_handle_set(arg) if self.commands.has_key(com): if self.commands[com] is not None: arg_strip = arg.strip() if arg_strip == "${saved_entry}" or arg_strip == "${next_entry}": logging.warning("grub2's saved_entry/next_entry not supported") arg = "0" setattr(self, self.commands[com], arg_strip) else: logging.info("Ignored directive %s" %(com,)) elif com.startswith('set:'): pass else: logging.warning("Unknown directive %s" %(com,)) if img is not None: raise RuntimeError, "syntax error: end of file with open menuentry(%d %s)" % (len(img),img) if self.hasPassword(): self.setPasswordAccess(False) commands = {'set:default': 'default', 'set:root': 'root', 'set:timeout': 'timeout', 'terminal': None, 'insmod': None, 'load_env': None, 'save_env': None, 'search': None, 'if': None, 'fi': None, } if __name__ == "__main__": if len(sys.argv) < 3: raise RuntimeError, "Need a grub version (\"grub\" or \"grub2\") and a grub.conf or grub.cfg to read" if sys.argv[1] == "grub": g = GrubConfigFile(sys.argv[2]) elif sys.argv[1] == "grub2": g = Grub2ConfigFile(sys.argv[2]) else: raise RuntimeError, "Unknown config type %s" % sys.argv[1] for i in g.images: print i #, i.title, i.root, i.kernel, i.args, i.initrd xen-4.9.2/tools/pygrub/src/LiloConf.py0000664000175000017500000001217313256712137015777 0ustar smbsmb# #LiloConf.py # import sys, re, os import logging import GrubConf class LiloImage(object): def __init__(self, lines, path): self.reset(lines, path) def __repr__(self): return ("title: %s\n" " root: %s\n" " kernel: %s\n" " args: %s\n" " initrd: %s\n" %(self.title, self.root, self.kernel, self.args, self.initrd)) def reset(self, lines, path): self._initrd = self._kernel = self._readonly = None self._args = "" self.title = "" self.lines = [] self.path = path self.root = "" map(self.set_from_line, lines) def set_from_line(self, line, replace = None): (com, arg) = GrubConf.grub_exact_split(line, 2) if self.commands.has_key(com): if self.commands[com] is not None: setattr(self, self.commands[com], re.sub('^"(.+)"$', r"\1", arg.strip())) else: logging.info("Ignored image directive %s" %(com,)) else: logging.warning("Unknown image directive %s" %(com,)) # now put the line in the list of lines if replace is None: self.lines.append(line) else: self.lines.pop(replace) self.lines.insert(replace, line) def set_kernel(self, val): self._kernel = (None, self.path + "/" + val) def get_kernel(self): return self._kernel kernel = property(get_kernel, set_kernel) def set_initrd(self, val): self._initrd = (None, self.path + "/" + val) def get_initrd(self): return self._initrd initrd = property(get_initrd, set_initrd) def set_args(self, val): self._args = val def get_args(self): args = self._args if self.root: args += " root=" + self.root if self.readonly: args += " ro" return args args = property(get_args, set_args) def set_readonly(self, val): self._readonly = 1 def get_readonly(self): return self._readonly readonly = property(get_readonly, set_readonly) # set up command handlers commands = { "label": "title", "root": "root", "rootnoverify": "root", "image": "kernel", "initrd": "initrd", "append": "args", "read-only": "readonly", "chainloader": None, "module": None} class LiloConfigFile(object): def __init__(self, fn = None): self.filename = fn self.images = [] self.timeout = -1 self._default = 0 if fn is not None: self.parse() def parse(self, buf = None): if buf is None: if self.filename is None: raise ValueError, "No config file defined to parse!" f = open(self.filename, 'r') lines = f.readlines() f.close() else: lines = buf.split("\n") path = os.path.dirname(self.filename) img = [] for l in lines: l = l.strip() # skip blank lines if len(l) == 0: continue # skip comments if l.startswith('#'): continue # new image if l.startswith("image"): if len(img) > 0: self.add_image(LiloImage(img, path)) img = [l] continue if len(img) > 0: img.append(l) continue (com, arg) = GrubConf.grub_exact_split(l, 2) if self.commands.has_key(com): if self.commands[com] is not None: setattr(self, self.commands[com], arg.strip()) else: logging.info("Ignored directive %s" %(com,)) else: logging.warning("Unknown directive %s" %(com,)) if len(img) > 0: self.add_image(LiloImage(img, path)) def hasPassword(self): return False def hasPasswordAccess(self): return True def add_image(self, image): self.images.append(image) def new_image(self, title, lines): # LiloImage constructor doesn't have title but since path # is being used by get_{kernel|initrd} functions we pass # empty string rather than None (see lines above) return LiloImage(lines, "") def _get_default(self): for i in range(len(self.images)): if self.images[i].title == self._default: return i return 0 def _set_default(self, val): self._default = val default = property(_get_default, _set_default) commands = { "default": "default", "timeout": "timeout", "prompt": None, "relocatable": None, } if __name__ == "__main__": if len(sys.argv) < 2: raise RuntimeError, "Need a lilo.conf to read" g = LiloConfigFile(sys.argv[1]) for i in g.images: print i #, i.title, i.root, i.kernel, i.args, i.initrd print g.default xen-4.9.2/tools/pygrub/src/ExtLinuxConf.py0000664000175000017500000001502713256712137016661 0ustar smbsmb# # ExtLinuxConf.py - Simple syslinux config parsing # # Copyright 2010 Citrix Systems Ltd. # # This software may be freely redistributed under the terms of the GNU # general public license. # # You should have received a copy of the GNU General Public License # along with this program; If not, see . # import sys, re, os import logging import GrubConf class ExtLinuxImage(object): def __init__(self, lines, path): self.reset(lines, path) def __repr__(self): return ("title: %s\n" " root: %s\n" " kernel: %s\n" " args: %s\n" " initrd: %s\n" %(self.title, self.root, self.kernel, self.args, self.initrd)) def reset(self, lines, path): self._initrd = self._kernel = self._readonly = None self._args = "" self.title = "" self.lines = [] self.path = path self.root = "" map(self.set_from_line, lines) def set_from_line(self, line, replace = None): (com, arg) = GrubConf.grub_exact_split(line, 2) com = com.lower() # Special handling for mboot.c32 if com.lower() == "append" and self.kernel is not None: (_,kernel) = self.kernel if kernel.endswith("mboot.c32"): kernel = None args = None initrd = None modules = arg.split("---") if len(modules) == 3: # Assume Xen + Kernel + Initrd (_,kernel,initrd) = modules elif len(modules) == 2: # Assume Kernel + Initrd (kernel,initrd) = modules if kernel: setattr(self, "kernel", kernel.strip()) if initrd: setattr(self, "initrd", initrd.strip()) # Bypass regular self.commands handling com = None elif "initrd=" in arg: # find initrd image in append line args = arg.strip().split(" ") for a in args: if a.lower().startswith("initrd="): setattr(self, "initrd", a.replace("initrd=", "")) arg = arg.replace(a, "") if com is not None and self.commands.has_key(com): if self.commands[com] is not None: setattr(self, self.commands[com], re.sub('^"(.+)"$', r"\1", arg.strip())) else: logging.info("Ignored image directive %s" %(com,)) elif com is not None: logging.warning("Unknown image directive %s" %(com,)) # now put the line in the list of lines if replace is None: self.lines.append(line) else: self.lines.pop(replace) self.lines.insert(replace, line) def set_kernel(self, val): if val.find(" ") == -1: self._kernel = (None,val) self._args = None return (kernel, args) = val.split(None, 1) self._kernel = (None,kernel) self._args = args def get_kernel(self): return self._kernel def set_args(self, val): self._args = val def get_args(self): return self._args kernel = property(get_kernel, set_kernel) args = property(get_args, set_args) def set_initrd(self, val): self._initrd = (None,val) def get_initrd(self): return self._initrd initrd = property(get_initrd, set_initrd) def set_readonly(self, val): self._readonly = 1 def get_readonly(self): return self._readonly readonly = property(get_readonly, set_readonly) # set up command handlers commands = { "label": "title", "kernel": "kernel", "append": "args"} class ExtLinuxConfigFile(object): def __init__(self, fn = None): self.filename = fn self.images = [] self.timeout = -1 self._default = 0 if fn is not None: self.parse() def new_image(self, title, lines): # ExtLinuxImage constructor doesn't have title but since path # is being used by get_{kernel|initrd} functions we pass # empty string rather than None (see lines above) return ExtLinuxImage(lines, "") def parse(self, buf = None): if buf is None: if self.filename is None: raise ValueError, "No config file defined to parse!" f = open(self.filename, 'r') lines = f.readlines() f.close() else: lines = buf.split("\n") path = os.path.dirname(self.filename) img = [] for l in lines: l = l.strip() # skip blank lines if len(l) == 0: continue # skip comments if l.startswith('#'): continue # new image if l.lower().startswith("label"): if len(img) > 0: self.add_image(ExtLinuxImage(img, path)) img = [l] continue if len(img) > 0: img.append(l) continue (com, arg) = GrubConf.grub_exact_split(l, 2) com = com.lower() if self.commands.has_key(com): if self.commands[com] is not None: setattr(self, self.commands[com], arg.strip()) else: logging.info("Ignored directive %s" %(com,)) else: logging.warning("Unknown directive %s" %(com,)) if len(img) > 0: self.add_image(ExtLinuxImage(img, path)) def hasPassword(self): return False def hasPasswordAccess(self): return True def add_image(self, image): self.images.append(image) def _get_default(self): for i in range(len(self.images)): if self.images[i].title == self._default: return i return 0 def _set_default(self, val): self._default = val default = property(_get_default, _set_default) commands = { "default": "default", "timeout": "timeout", "serial": None, "prompt": None, "display": None, "f1": None, "f2": None, } if __name__ == "__main__": if len(sys.argv) < 2: raise RuntimeError, "Need a configuration file to read" g = ExtLinuxConfigFile(sys.argv[1]) for i in g.images: print i print g.default xen-4.9.2/tools/cross-install0000775000175000017500000000023113256712137014334 0ustar smbsmb#!/bin/sh # prepend CROSS_BIN_PATH to find the right "strip" if [ -n "$CROSS_BIN_PATH" ]; then PATH="$CROSS_BIN_PATH:$PATH" fi exec $_INSTALL "$@" xen-4.9.2/tools/Makefile0000664000175000017500000002567613256712137013275 0ustar smbsmbXEN_ROOT = $(CURDIR)/.. export PKG_CONFIG_DIR = $(CURDIR)/pkg-config include $(XEN_ROOT)/tools/Rules.mk SUBDIRS-y := SUBDIRS-y += libs SUBDIRS-y += libxc SUBDIRS-y += flask SUBDIRS-y += fuzz SUBDIRS-y += xenstore SUBDIRS-y += misc SUBDIRS-y += examples SUBDIRS-y += hotplug SUBDIRS-y += xentrace SUBDIRS-$(CONFIG_XCUTILS) += xcutils SUBDIRS-$(CONFIG_X86) += firmware SUBDIRS-y += console SUBDIRS-y += xenmon SUBDIRS-y += xenstat SUBDIRS-$(CONFIG_Linux) += memshr SUBDIRS-$(CONFIG_BLKTAP2) += blktap2 SUBDIRS-$(CONFIG_NetBSD) += xenbackendd SUBDIRS-y += libfsimage SUBDIRS-$(CONFIG_Linux) += libvchan # do not recurse in to a dir we are about to delete ifneq "$(MAKECMDGOALS)" "distclean" SUBDIRS-$(CONFIG_QEMU_TRAD) += qemu-xen-traditional-dir SUBDIRS-$(CONFIG_QEMU_XEN) += qemu-xen-dir endif SUBDIRS-y += xenpmd SUBDIRS-y += libxl SUBDIRS-$(CONFIG_GOLANG) += golang SUBDIRS-y += xl SUBDIRS-y += helpers SUBDIRS-$(CONFIG_X86) += xenpaging SUBDIRS-$(CONFIG_X86) += debugger/gdbsx SUBDIRS-$(CONFIG_X86) += debugger/kdd SUBDIRS-$(CONFIG_TESTS) += tests # These don't cross-compile ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) SUBDIRS-y += python SUBDIRS-y += pygrub SUBDIRS-$(OCAML_TOOLS) += ocaml endif ifeq ($(CONFIG_RUMP),y) SUBDIRS-y := libxc xenstore endif # For the sake of linking, set the sys-root ifneq ($(CROSS_COMPILE),) CROSS_BIN_PATH ?= /usr/$(CROSS_COMPILE:-=)/bin CROSS_SYS_ROOT ?= /usr/$(CROSS_COMPILE:-=)/sys-root export CROSS_SYS_ROOT # exported for check/funcs.sh export CROSS_BIN_PATH # exported for cross-install.sh endif .PHONY: build all build all: subdirs-all .PHONY: install install: $(INSTALL_DIR) -m 700 $(DESTDIR)$(XEN_DUMP_DIR) $(INSTALL_DIR) $(DESTDIR)$(XEN_LOG_DIR) $(INSTALL_DIR) $(DESTDIR)$(XEN_RUN_DIR) $(INSTALL_DIR) $(DESTDIR)$(XEN_LIB_DIR) $(INSTALL_DIR) $(DESTDIR)$(XEN_RUN_STORED) $(INSTALL_DIR) $(DESTDIR)$(PKG_INSTALLDIR) $(MAKE) subdirs-install .PHONY: uninstall uninstall: D=$(DESTDIR) uninstall: [ -d $(D)$(XEN_CONFIG_DIR) ] && mv -f $(D)$(XEN_CONFIG_DIR) $(D)$(XEN_CONFIG_DIR).old-`date +%s` || true rm -rf $(D)$(CONFIG_DIR)/init.d/xendomains $(D)$(CONFIG_DIR)/init.d/xend rm -rf $(D)$(CONFIG_DIR)/init.d/xencommons $(D)$(CONFIG_DIR)/init.d/xen-watchdog rm -f $(D)$(CONFIG_DIR)/udev/rules.d/xen-backend.rules rm -f $(D)$(CONFIG_DIR)/udev/rules.d/xend.rules rm -f $(D)$(SYSCONFIG_DIR)/xendomains rm -f $(D)$(sbindir)/xendomains rm -f $(D)$(SYSCONFIG_DIR)/xencommons rm -rf $(D)$(XEN_LIB_DIR) rm -rf $(D)$(XEN_LIB_STORED) rm -rf $(D)$(XEN_RUN_STORED) rm -rf $(D)$(PKG_INSTALLDIR) rm -rf $(D)$(bindir)/cpuperf-perfcntr $(D)$(bindir)/cpuperf-xen rm -rf $(D)$(bindir)/xc_shadow rm -rf $(D)$(bindir)/pygrub rm -rf $(D)$(bindir)/setsize $(D)$(bindir)/tbctl rm -rf $(D)$(bindir)/xsls rm -rf $(D)$(bindir)/xenstore* $(D)$(bindir)/xentrace* rm -rf $(D)$(bindir)/xen-detect $(D)$(bindir)/xencons rm -rf $(D)$(bindir)/xenpvnetboot $(D)$(bindir)/qemu-*-xen rm -rf $(D)$(includedir)/xenctrl* $(D)$(includedir)/xenguest.h rm -rf $(D)$(includedir)/xs_lib.h $(D)$(includedir)/xs.h rm -rf $(D)$(includedir)/xenstore-compat/xs_lib.h $(D)$(includedir)/xenstore-compat/xs.h rm -rf $(D)$(includedir)/xenstore_lib.h $(D)$(includedir)/xenstore.h rm -rf $(D)$(includedir)/xen rm -rf $(D)$(includedir)/_libxl* $(D)$(includedir)/libxl* rm -rf $(D)$(includedir)/xenstat.h $(D)$(includedir)/xentoollog.h rm -rf $(D)$(libdir)/libxenctrl* $(D)$(libdir)/libxenguest* rm -rf $(D)$(libdir)/libxenstore* $(D)$(libdir)/libxlutil* rm -rf $(D)$(libdir)/python/xen $(D)$(libdir)/python/grub rm -rf $(D)$(LIBEXEC) rm -rf $(D)$(sbindir)/setmask rm -rf $(D)$(sbindir)/xen* $(D)$(sbindir)/netfix $(D)$(sbindir)/xm rm -rf $(D)$(SHAREDIR)/doc/xen rm -rf $(D)$(SHAREDIR)/xen rm -rf $(D)$(SHAREDIR)/qemu-xen rm -rf $(D)$(MAN1DIR)/xen* rm -rf $(D)$(MAN8DIR)/xen* .PHONY: clean clean: subdirs-clean rm -rf pkg-config .PHONY: distclean distclean: subdirs-distclean clean rm -rf qemu-xen-traditional-dir qemu-xen-traditional-dir-remote rm -rf qemu-xen-dir qemu-xen-dir-remote qemu-xen-build rm -rf ../config/Tools.mk config.h config.log config.status \ config.cache autom4te.cache ifneq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) IOEMU_CONFIGURE_CROSS ?= --cross-prefix=$(CROSS_COMPILE) \ --interp-prefix=$(CROSS_SYS_ROOT) endif ifeq ($(XEN_TOOLS_RPATH),y) QEMU_UPSTREAM_RPATH := -Wl,-rpath,$(LIBEXEC_LIB):$(libdir) IOEMU_EXTRA_LDFLAGS := --extra-ldflags="-Wl,-rpath,$(libdir)" else QEMU_UPSTREAM_RPATH := -Wl,-rpath,$(LIBEXEC_LIB) IOEMU_EXTRA_LDFLAGS := endif QEMU_ROOT := $(shell if [ -d "$(QEMU_TRADITIONAL_LOC)" ]; then echo "$(QEMU_TRADITIONAL_LOC)"; else echo .; fi) ifneq ($(QEMU_ROOT),.) export QEMU_ROOT endif # Targets for external trees: # ${target}-dir-find # See if the directory exists and check it out if not. # ${target}-dir-force-update # Pull to the most recent update (as if you had checked it out for the # first time) # subdir-all-${target}-dir # Do "make all" for ${target}, including all prerequisites (such as # configure) # subdir-install-${target}-dir # Do "make install" for $TARGET # subdir-clean-${target}-dir # Do "make clean" for $TARGET # # Directories for external trees: # ${target}-dir # Used for local builds. Usually a link to ${target}-dir-remote # ${target}-dir-remote # Where remote repositories are cloned # ${target} # Where a copy of the source files are put when building a source # tarball for release # # Expected variables: # ${TARGET}_URL # A url from which to clone a git repo # ${TARGET}_REVISION # The target revision to check out when doing "find" or "force-update" # ${TARGET}_INTREE # The directory where the subtree can be found (usually used when building # a source tarball) # ${TARGET}_LOC # The ultimate location of the source (either a local dir or remote URL) # External target: qemu-xen-traditional qemu-xen-traditional-dir-find: set -ex; \ if test -d $(QEMU_TRADITIONAL_LOC); then \ mkdir -p qemu-xen-traditional-dir; \ else \ export GIT=$(GIT); \ $(XEN_ROOT)/scripts/git-checkout.sh $(QEMU_TRADITIONAL_LOC) $(QEMU_TRADITIONAL_REVISION) qemu-xen-traditional-dir; \ fi .PHONY: qemu-xen-traditional-dir-force-update qemu-xen-traditional-dir-force-update: qemu-xen-traditional-dir-find set -ex; \ if [ "$(QEMU_TRADITIONAL_REVISION)" ]; then \ cd qemu-xen-traditional-dir-remote; \ $(GIT) fetch origin; \ $(GIT) reset --hard $(QEMU_TRADITIONAL_REVISION); \ fi qemu-traditional-recurse = \ set -e; \ $(buildmakevars2shellvars); \ export CONFIG_BLKTAP1=n; \ cd qemu-xen-traditional-dir; \ $(1) subdir-all-qemu-xen-traditional-dir: qemu-xen-traditional-dir-find $(call qemu-traditional-recurse,\ $(QEMU_ROOT)/xen-setup \ --extra-cflags="-D__XEN_TOOLS__ $(EXTRA_CFLAGS_QEMU_TRADITIONAL)" \ $(IOEMU_EXTRA_LDFLAGS) \ --cpu=$(IOEMU_CPU_ARCH) \ $(IOEMU_CONFIGURE_CROSS); \ $(MAKE) all \ ) subdir-install-qemu-xen-traditional-dir: subdir-all-qemu-xen-traditional-dir $(call qemu-traditional-recurse,$(MAKE) install) subdir-clean-qemu-xen-traditional-dir: set -e; if test -d qemu-xen-traditional-dir/.; then \ $(MAKE) -C qemu-xen-traditional-dir clean; \ fi # External target: qemu-xen qemu-xen-dir-find: if test -d $(QEMU_UPSTREAM_LOC) ; then \ mkdir -p qemu-xen-dir; \ else \ export GIT=$(GIT); \ $(XEN_ROOT)/scripts/git-checkout.sh $(QEMU_UPSTREAM_LOC) $(QEMU_UPSTREAM_REVISION) qemu-xen-dir ; \ fi .PHONY: qemu-xen-dir-force-update qemu-xen-dir-force-update: qemu-xen-dir-find set -ex; \ if [ "$(QEMU_UPSTREAM_REVISION)" ]; then \ cd qemu-xen-dir-remote; \ $(GIT) fetch origin; \ $(GIT) reset --hard $(QEMU_UPSTREAM_REVISION); \ fi ifeq ($(debug),y) QEMU_XEN_ENABLE_DEBUG := --enable-debug else QEMU_XEN_ENABLE_DEBUG := endif subdir-all-qemu-xen-dir: qemu-xen-dir-find if test -d $(QEMU_UPSTREAM_LOC) ; then \ source=$(QEMU_UPSTREAM_LOC); \ else \ source=$(XEN_ROOT)/tools/qemu-xen-dir; \ fi; \ mkdir -p qemu-xen-build; \ cd qemu-xen-build; \ if $$source/scripts/tracetool.py --check-backend --backend log ; then \ enable_trace_backend='--enable-trace-backend=log'; \ elif $$source/scripts/tracetool.py --check-backend --backend stderr ; then \ enable_trace_backend='--enable-trace-backend=stderr'; \ else \ enable_trace_backend='' ; \ fi ; \ PKG_CONFIG_PATH=$(XEN_ROOT)/tools/pkg-config \ $$source/configure --enable-xen --target-list=i386-softmmu \ $(QEMU_XEN_ENABLE_DEBUG) \ $$enable_trace_backend \ --prefix=$(LIBEXEC) \ --libdir=$(LIBEXEC_LIB) \ --includedir=$(LIBEXEC_INC) \ --source-path=$$source \ --extra-cflags="-DXC_WANT_COMPAT_EVTCHN_API=1 \ -DXC_WANT_COMPAT_GNTTAB_API=1 \ -DXC_WANT_COMPAT_MAP_FOREIGN_API=1 \ -DXC_WANT_COMPAT_DEVICEMODEL_API=1 \ -I$(XEN_ROOT)/tools/include \ -I$(XEN_ROOT)/tools/libs/toollog/include \ -I$(XEN_ROOT)/tools/libs/evtchn/include \ -I$(XEN_ROOT)/tools/libs/gnttab/include \ -I$(XEN_ROOT)/tools/libs/foreignmemory/include \ -I$(XEN_ROOT)/tools/libs/devicemodel/include \ -I$(XEN_ROOT)/tools/libxc/include \ -I$(XEN_ROOT)/tools/xenstore/include \ -I$(XEN_ROOT)/tools/xenstore/compat/include \ $(EXTRA_CFLAGS_QEMU_XEN)" \ --extra-ldflags="-L$(XEN_ROOT)/tools/libxc \ -L$(XEN_ROOT)/tools/xenstore \ -L$(XEN_ROOT)/tools/libs/evtchn \ -L$(XEN_ROOT)/tools/libs/gnttab \ -L$(XEN_ROOT)/tools/libs/foreignmemory \ -L$(XEN_ROOT)/tools/libs/devicemodel \ -Wl,-rpath-link=$(XEN_ROOT)/tools/libs/toollog \ -Wl,-rpath-link=$(XEN_ROOT)/tools/libs/evtchn \ -Wl,-rpath-link=$(XEN_ROOT)/tools/libs/gnttab \ -Wl,-rpath-link=$(XEN_ROOT)/tools/libs/call \ -Wl,-rpath-link=$(XEN_ROOT)/tools/libs/foreignmemory \ -Wl,-rpath-link=$(XEN_ROOT)/tools/libs/devicemodel \ $(QEMU_UPSTREAM_RPATH)" \ --bindir=$(LIBEXEC_BIN) \ --datadir=$(SHAREDIR)/qemu-xen \ --localstatedir=$(localstatedir) \ --disable-kvm \ --disable-docs \ --disable-guest-agent \ --python=$(PYTHON) \ $(CONFIG_QEMUU_EXTRA_ARGS) \ --cpu=$(IOEMU_CPU_ARCH) \ $(IOEMU_CONFIGURE_CROSS); \ $(MAKE) all subdir-install-qemu-xen-dir: subdir-all-qemu-xen-dir cd qemu-xen-build; \ $(MAKE) install subdir-clean-qemu-xen-dir: set -e; if test -d qemu-xen-build/.; then \ $(MAKE) -C qemu-xen-build clean; \ fi subdir-clean-debugger/gdbsx subdir-distclean-debugger/gdbsx: .phony $(MAKE) -C debugger/gdbsx clean subdir-install-debugger/gdbsx: .phony $(MAKE) -C debugger/gdbsx install subdir-all-debugger/gdbsx: .phony $(MAKE) -C debugger/gdbsx all subdir-clean-debugger/kdd subdir-distclean-debugger/kdd: .phony $(MAKE) -C debugger/kdd clean subdir-install-debugger/kdd: .phony $(MAKE) -C debugger/kdd install subdir-all-debugger/kdd: .phony $(MAKE) -C debugger/kdd all subdir-distclean-firmware: .phony $(MAKE) -C firmware distclean subtree-force-update: ifeq ($(CONFIG_QEMU_XEN),y) $(MAKE) qemu-xen-dir-force-update endif ifeq ($(CONFIG_QEMU_TRAD),y) $(MAKE) qemu-xen-traditional-dir-force-update endif ifeq ($(CONFIG_X86),y) $(MAKE) -C firmware subtree-force-update endif subtree-force-update-all: $(MAKE) qemu-xen-dir-force-update $(MAKE) qemu-xen-traditional-dir-force-update $(MAKE) -C firmware subtree-force-update-all xen-4.9.2/tools/helpers/0000775000175000017500000000000013256712137013257 5ustar smbsmbxen-4.9.2/tools/helpers/init-dom-json.h0000664000175000017500000000046313256712137016122 0ustar smbsmb#ifndef __INIT_DOM_JSON_H #define __INIT_DOM_JSON_H /* * Generate a stub JSON config for a domain with the given domid. */ int gen_stub_json_config(uint32_t domid); #endif /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/helpers/init-dom-json.c0000664000175000017500000000302313256712137016110 0ustar smbsmb#include #include #include #include #include #include #include int gen_stub_json_config(uint32_t domid) { int rc = 1; xentoollog_logger_stdiostream *logger; libxl_ctx *ctx; libxl_domain_config dom_config; char *json = NULL; logger = xtl_createlogger_stdiostream(stderr, XTL_ERROR, 0); if (!logger) return 1; if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger *)logger)) { fprintf(stderr, "cannot init libxl context\n"); goto outlog; } libxl_domain_config_init(&dom_config); /* Generate stub JSON config. */ dom_config.c_info.type = LIBXL_DOMAIN_TYPE_PV; libxl_domain_build_info_init_type(&dom_config.b_info, LIBXL_DOMAIN_TYPE_PV); json = libxl_domain_config_to_json(ctx, &dom_config); /* libxl-json format requires the string ends with '\0'. Code * snippet taken from libxl. */ rc = libxl_userdata_store(ctx, domid, "libxl-json", (const uint8_t *)json, strlen(json) + 1 /* include '\0' */); if (rc) fprintf(stderr, "cannot store stub json config for domain %u\n", domid); libxl_domain_config_dispose(&dom_config); free(json); libxl_ctx_free(ctx); outlog: xtl_logger_destroy((xentoollog_logger *)logger); return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/helpers/xen-init-dom0.c0000664000175000017500000000271713256712137016022 0ustar smbsmb#include #include #include #include #include #include "init-dom-json.h" #define DOMNAME_PATH "/local/domain/0/name" #define DOMID_PATH "/local/domain/0/domid" int main(int argc, char **argv) { int rc; struct xs_handle *xsh; char *domname_string = NULL, *domid_string = NULL; xsh = xs_open(0); if (!xsh) { fprintf(stderr, "cannot open xenstore connection\n"); exit(1); } /* Sanity check: this program can only be run once. */ domid_string = xs_read(xsh, XBT_NULL, DOMID_PATH, NULL); domname_string = xs_read(xsh, XBT_NULL, DOMNAME_PATH, NULL); if (domid_string && domname_string) { fprintf(stderr, "Dom0 is already set up\n"); rc = 0; goto out; } rc = gen_stub_json_config(0); if (rc) goto out; /* Write xenstore entries. */ if (!xs_write(xsh, XBT_NULL, DOMID_PATH, "0", strlen("0"))) { fprintf(stderr, "cannot set domid for Dom0\n"); rc = 1; goto out; } if (!xs_write(xsh, XBT_NULL, DOMNAME_PATH, "Domain-0", strlen("Domain-0"))) { fprintf(stderr, "cannot set domain name for Dom0\n"); rc = 1; goto out; } printf("Done setting up Dom0\n"); out: free(domid_string); free(domname_string); xs_close(xsh); return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/helpers/Makefile0000664000175000017500000000313713256712137014723 0ustar smbsmb# # tools/helpers/Makefile # XEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk PROGS += xen-init-dom0 ifeq ($(CONFIG_Linux),y) PROGS += init-xenstore-domain endif XEN_INIT_DOM0_OBJS = xen-init-dom0.o init-dom-json.o $(XEN_INIT_DOM0_OBJS): CFLAGS += $(CFLAGS_libxentoollog) $(XEN_INIT_DOM0_OBJS): CFLAGS += $(CFLAGS_libxenstore) $(XEN_INIT_DOM0_OBJS): CFLAGS += $(CFLAGS_libxenlight) INIT_XENSTORE_DOMAIN_OBJS = init-xenstore-domain.o init-dom-json.o $(INIT_XENSTORE_DOMAIN_OBJS): CFLAGS += $(CFLAGS_libxentoollog) $(INIT_XENSTORE_DOMAIN_OBJS): CFLAGS += $(CFLAGS_libxenguest) $(INIT_XENSTORE_DOMAIN_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(INIT_XENSTORE_DOMAIN_OBJS): CFLAGS += $(CFLAGS_libxenstore) $(INIT_XENSTORE_DOMAIN_OBJS): CFLAGS += $(CFLAGS_libxenlight) .PHONY: all all: $(PROGS) xen-init-dom0: $(XEN_INIT_DOM0_OBJS) $(CC) $(LDFLAGS) -o $@ $(XEN_INIT_DOM0_OBJS) $(LDLIBS_libxentoollog) $(LDLIBS_libxenstore) $(LDLIBS_libxenlight) $(APPEND_LDFLAGS) $(INIT_XENSTORE_DOMAIN_OBJS): _paths.h init-xenstore-domain: $(INIT_XENSTORE_DOMAIN_OBJS) $(CC) $(LDFLAGS) -o $@ $(INIT_XENSTORE_DOMAIN_OBJS) $(LDLIBS_libxentoollog) $(LDLIBS_libxenstore) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenlight) $(APPEND_LDFLAGS) .PHONY: install install: all $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN) $(INSTALL_PROG) xen-init-dom0 $(DESTDIR)$(LIBEXEC_BIN) ifeq ($(CONFIG_Linux),y) $(INSTALL_PROG) init-xenstore-domain $(DESTDIR)$(LIBEXEC_BIN) endif .PHONY: clean clean: $(RM) -f *.o $(PROGS) $(DEPS) _paths.h distclean: clean genpath-target = $(call buildmakevars2header,_paths.h) $(eval $(genpath-target)) xen-4.9.2/tools/helpers/init-xenstore-domain.c0000664000175000017500000002304113256712137017500 0ustar smbsmb#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "init-dom-json.h" #include "_paths.h" static uint32_t domid = ~0; static char *kernel; static char *ramdisk; static char *flask; static char *param; static char *name = "Xenstore"; static int memory; static int maxmem; static struct option options[] = { { "kernel", 1, NULL, 'k' }, { "memory", 1, NULL, 'm' }, { "flask", 1, NULL, 'f' }, { "ramdisk", 1, NULL, 'r' }, { "param", 1, NULL, 'p' }, { "name", 1, NULL, 'n' }, { "maxmem", 1, NULL, 'M' }, { NULL, 0, NULL, 0 } }; static void usage(void) { fprintf(stderr, "Usage:\n" "\n" "init-xenstore-domain \n" "\n" "where options may include:\n" "\n" " --kernel kernel file of the xenstore domain, mandatory\n" " --memory size of the domain in MB, mandatory\n" " --flask optional flask label of the domain\n" " --ramdisk optional ramdisk file for the domain\n" " --param optional additional parameters for the domain\n" " --name name of the domain (default: Xenstore)\n" " --maxmem maximum memory size in the format:\n" " |/|:/\n" " (an absolute value in MB, a fraction a/b of\n" " the host memory, or the maximum of both)\n"); } static int build(xc_interface *xch) { char cmdline[512]; uint32_t ssid; xen_domain_handle_t handle = { 0 }; int rv, xs_fd; struct xc_dom_image *dom = NULL; int limit_kb = (maxmem ? : (memory + 1)) * 1024; xs_fd = open("/dev/xen/xenbus_backend", O_RDWR); if ( xs_fd == -1 ) { fprintf(stderr, "Could not open /dev/xen/xenbus_backend\n"); return -1; } if ( flask ) { rv = xc_flask_context_to_sid(xch, flask, strlen(flask), &ssid); if ( rv ) { fprintf(stderr, "xc_flask_context_to_sid failed\n"); goto err; } } else { ssid = SECINITSID_DOMU; } rv = xc_domain_create(xch, ssid, handle, XEN_DOMCTL_CDF_xs_domain, &domid, NULL); if ( rv ) { fprintf(stderr, "xc_domain_create failed\n"); goto err; } rv = xc_domain_max_vcpus(xch, domid, 1); if ( rv ) { fprintf(stderr, "xc_domain_max_vcpus failed\n"); goto err; } rv = xc_domain_setmaxmem(xch, domid, limit_kb); if ( rv ) { fprintf(stderr, "xc_domain_setmaxmem failed\n"); goto err; } rv = xc_domain_set_memmap_limit(xch, domid, limit_kb); if ( rv ) { fprintf(stderr, "xc_domain_set_memmap_limit failed\n"); goto err; } rv = ioctl(xs_fd, IOCTL_XENBUS_BACKEND_SETUP, domid); if ( rv < 0 ) { fprintf(stderr, "Xenbus setup ioctl failed\n"); goto err; } if ( param ) snprintf(cmdline, 512, "--event %d --internal-db %s", rv, param); else snprintf(cmdline, 512, "--event %d --internal-db", rv); dom = xc_dom_allocate(xch, cmdline, NULL); rv = xc_dom_kernel_file(dom, kernel); if ( rv ) { fprintf(stderr, "xc_dom_kernel_file failed\n"); goto err; } if ( ramdisk ) { rv = xc_dom_ramdisk_file(dom, ramdisk); if ( rv ) { fprintf(stderr, "xc_dom_ramdisk_file failed\n"); goto err; } } rv = xc_dom_boot_xen_init(dom, xch, domid); if ( rv ) { fprintf(stderr, "xc_dom_boot_xen_init failed\n"); goto err; } rv = xc_dom_parse_image(dom); if ( rv ) { fprintf(stderr, "xc_dom_parse_image failed\n"); goto err; } rv = xc_dom_mem_init(dom, memory); if ( rv ) { fprintf(stderr, "xc_dom_mem_init failed\n"); goto err; } rv = xc_dom_boot_mem_init(dom); if ( rv ) { fprintf(stderr, "xc_dom_boot_mem_init failed\n"); goto err; } rv = xc_dom_build_image(dom); if ( rv ) { fprintf(stderr, "xc_dom_build_image failed\n"); goto err; } rv = xc_dom_boot_image(dom); if ( rv ) { fprintf(stderr, "xc_dom_boot_image failed\n"); goto err; } rv = xc_domain_set_virq_handler(xch, domid, VIRQ_DOM_EXC); if ( rv ) { fprintf(stderr, "xc_domain_set_virq_handler failed\n"); goto err; } rv = xc_domain_unpause(xch, domid); if ( rv ) { fprintf(stderr, "xc_domain_unpause failed\n"); goto err; } rv = 0; err: if ( dom ) xc_dom_release(dom); if ( xs_fd >= 0 ) close(xs_fd); /* if we failed then destroy the domain */ if ( rv && domid != ~0 ) xc_domain_destroy(xch, domid); return rv; } static int check_domain(xc_interface *xch) { xc_dominfo_t info; uint32_t dom; int ret; dom = 1; while ( (ret = xc_domain_getinfo(xch, dom, 1, &info)) == 1 ) { if ( info.xenstore ) return 1; dom = info.domid + 1; } if ( ret < 0 && errno != ESRCH ) { fprintf(stderr, "xc_domain_getinfo failed\n"); return ret; } return 0; } static int parse_maxmem(xc_interface *xch, char *str) { xc_physinfo_t info; int rv; unsigned long mb = 0, a = 0, b = 0; unsigned long val; unsigned long *res; char *p; char *s = str; rv = xc_physinfo(xch, &info); if ( rv ) { fprintf(stderr, "xc_physinfo failed\n"); return -1; } res = &mb; for (p = s; *p; s = p + 1) { val = strtoul(s, &p, 10); if ( val == 0 || val >= INT_MAX / 1024 ) goto err; if ( *p == '/' ) { if ( res != &mb || a != 0 ) goto err; a = val; res = &b; continue; } if ( *res != 0 ) goto err; *res = val; if ( *p != 0 && *p != ':' ) goto err; res = &mb; } if ( a && !b ) goto err; val = a ? info.total_pages * a / (b * 1024 * 1024 / XC_PAGE_SIZE) : 0; if ( val >= INT_MAX / 1024 ) goto err; maxmem = mb < val ? val : mb; if ( maxmem < memory ) maxmem = 0; return maxmem; err: fprintf(stderr, "illegal value for maxmem: %s\n", str); return -1; } static void do_xs_write(struct xs_handle *xsh, char *path, char *val) { if ( !xs_write(xsh, XBT_NULL, path, val, strlen(val)) ) fprintf(stderr, "writing %s to xenstore failed.\n", path); } static void do_xs_write_dom(struct xs_handle *xsh, char *path, char *val) { char full_path[64]; snprintf(full_path, 64, "/local/domain/%d/%s", domid, path); do_xs_write(xsh, full_path, val); } int main(int argc, char** argv) { int opt; xc_interface *xch; struct xs_handle *xsh; char buf[16]; int rv, fd; char *maxmem_str = NULL; while ( (opt = getopt_long(argc, argv, "", options, NULL)) != -1 ) { switch ( opt ) { case 'k': kernel = optarg; break; case 'm': memory = strtol(optarg, NULL, 10); break; case 'f': flask = optarg; break; case 'r': ramdisk = optarg; break; case 'p': param = optarg; break; case 'n': name = optarg; break; case 'M': maxmem_str = optarg; break; default: usage(); return 2; } } if ( optind != argc || !kernel || !memory ) { usage(); return 2; } xch = xc_interface_open(NULL, NULL, 0); if ( !xch ) { fprintf(stderr, "xc_interface_open() failed\n"); return 1; } if ( maxmem_str ) { maxmem = parse_maxmem(xch, maxmem_str); if ( maxmem < 0 ) { xc_interface_close(xch); return 1; } } rv = check_domain(xch); if ( !rv ) rv = build(xch); else if ( rv > 0 ) fprintf(stderr, "xenstore domain already present.\n"); xc_interface_close(xch); if ( rv ) return 1; rv = gen_stub_json_config(domid); if ( rv ) return 3; xsh = xs_open(0); if ( !xsh ) { fprintf(stderr, "xs_open() failed.\n"); return 3; } snprintf(buf, 16, "%d", domid); do_xs_write(xsh, "/tool/xenstored/domid", buf); do_xs_write_dom(xsh, "domid", buf); do_xs_write_dom(xsh, "name", name); snprintf(buf, 16, "%d", memory * 1024); do_xs_write_dom(xsh, "memory/target", buf); if (maxmem) snprintf(buf, 16, "%d", maxmem * 1024); do_xs_write_dom(xsh, "memory/static-max", buf); xs_close(xsh); fd = creat(XEN_RUN_DIR "/xenstored.pid", 0666); if ( fd < 0 ) { fprintf(stderr, "Creating " XEN_RUN_DIR "/xenstored.pid failed\n"); return 3; } rv = snprintf(buf, 16, "domid:%d\n", domid); rv = write(fd, buf, rv); close(fd); if ( rv < 0 ) { fprintf(stderr, "Writing domid to " XEN_RUN_DIR "/xenstored.pid failed\n"); return 3; } return 0; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xenpmd/0000775000175000017500000000000013256712137013110 5ustar smbsmbxen-4.9.2/tools/xenpmd/xenpmd.c0000664000175000017500000003566313256712137014564 0ustar smbsmb/* * xenpmd.c * * xen power management daemon - Facilitates power management * functionality within xen guests. * * Copyright (c) 2008 Kamala Narasimhan * Copyright (c) 2008 Citrix Systems, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ /* Xen extended power management support provides HVM guest power management * features beyond S3, S4, S5. For example, it helps expose system level * battery status and battery meter information and in future will be extended * to include more power management support. This extended power management * support is enabled by setting xen_extended_power_mgmt to 1 or 2 in the HVM * config file. When set to 2, non-pass through mode is enabled which heavily * relies on this power management daemon to glean battery information from * dom0 and store it xenstore which would then be queries and used by qemu and * passed to the guest when appropriate battery ports are read/written to. */ #include #include #include #include #include #include #include #include /* #define RUN_STANDALONE */ #define RUN_IN_SIMULATE_MODE enum BATTERY_INFO_TYPE { BIF, BST }; enum BATTERY_PRESENT { NO, YES }; enum BATTERY_TECHNOLOGY { NON_RECHARGEABLE, RECHARGEABLE }; struct battery_info { enum BATTERY_PRESENT present; unsigned long design_capacity; unsigned long last_full_capacity; enum BATTERY_TECHNOLOGY battery_technology; unsigned long design_voltage; unsigned long design_capacity_warning; unsigned long design_capacity_low; unsigned long capacity_granularity_1; unsigned long capacity_granularity_2; char model_number[32]; char serial_number[32]; char battery_type[32]; char oem_info[32]; }; struct battery_status { enum BATTERY_PRESENT present; unsigned long state; unsigned long present_rate; unsigned long remaining_capacity; unsigned long present_voltage; }; static struct xs_handle *xs; #ifdef RUN_IN_SIMULATE_MODE #define BATTERY_DIR_PATH "/tmp/battery" #define BATTERY_INFO_FILE_PATH "/tmp/battery/%s/info" #define BATTERY_STATE_FILE_PATH "/tmp/battery/%s/state" #else #define BATTERY_DIR_PATH "/proc/acpi/battery" #define BATTERY_INFO_FILE_PATH "/proc/acpi/battery/%s/info" #define BATTERY_STATE_FILE_PATH "/proc/acpi/battery/%s/state" #endif FILE *get_next_battery_file(DIR *battery_dir, enum BATTERY_INFO_TYPE battery_info_type) { FILE *file = 0; struct dirent *dir_entries; char file_name[284]; do { dir_entries = readdir(battery_dir); if ( !dir_entries ) return 0; if ( strlen(dir_entries->d_name) < 4 ) continue; if ( battery_info_type == BIF ) snprintf(file_name, sizeof(file_name), BATTERY_INFO_FILE_PATH, dir_entries->d_name); else snprintf(file_name, sizeof(file_name), BATTERY_STATE_FILE_PATH, dir_entries->d_name); file = fopen(file_name, "r"); } while ( !file ); return file; } void set_attribute_battery_info(char *attrib_name, char *attrib_value, struct battery_info *info) { if ( strstr(attrib_name, "present") ) { if ( strstr(attrib_value, "yes") ) info->present = YES; return; } if ( strstr(attrib_name, "design capacity warning") ) { info->design_capacity_warning = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "design capacity low") ) { info->design_capacity_low = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "design capacity") ) { info->design_capacity = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "last full capacity") ) { info->last_full_capacity = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "design voltage") ) { info->design_voltage = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "capacity granularity 1") ) { info->capacity_granularity_1 = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "capacity granularity 2") ) { info->capacity_granularity_2 = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "battery technology") ) { if ( strncmp(attrib_value, "rechargeable", strlen("rechargeable")) == 0 ) info->battery_technology = RECHARGEABLE; else info->battery_technology = NON_RECHARGEABLE; return; } if ( strstr(attrib_name, "model number") ) { strncpy(info->model_number, attrib_value, 32); return; } if ( strstr(attrib_name, "serial number") ) { strncpy(info->serial_number, attrib_value, 32); return; } if ( strstr(attrib_name, "battery type") ) { strncpy(info->battery_type, attrib_value, 32); return; } if ( strstr(attrib_name, "OEM info") ) { strncpy(info->oem_info, attrib_value, 32); return; } return; } void set_attribute_battery_status(char *attrib_name, char *attrib_value, struct battery_status *status) { if ( strstr(attrib_name, "charging state") ) { /* Check this, below is half baked */ if ( strstr(attrib_value, "charged") ) status->state = 0; else status->state = 1; return; } if ( strstr(attrib_name, "present rate") ) { status->present_rate = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "remaining capacity") ) { status->remaining_capacity = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "present voltage") ) { status->present_voltage = strtoull(attrib_value, NULL, 10); return; } if ( strstr(attrib_name, "present") ) { if ( strstr(attrib_value, "yes") ) status->present = YES; return; } } void parse_battery_info_or_status(char *line_info, enum BATTERY_INFO_TYPE type, void *info_or_status) { char attrib_name[128]; char attrib_value[64]; char *delimiter; unsigned long length; length = strlen(line_info); delimiter = (char *) strchr( line_info, ':'); if ( (!delimiter) || (delimiter == line_info) || (delimiter == line_info + length) ) return; strncpy(attrib_name, line_info, delimiter-line_info); while ( *(delimiter+1) == ' ' ) { delimiter++; if ( delimiter+1 == line_info + length) return; } strncpy(attrib_value, delimiter+1, (unsigned long)line_info + length -(unsigned long)delimiter); if ( type == BIF ) set_attribute_battery_info(attrib_name, attrib_value, (struct battery_info *)info_or_status); else set_attribute_battery_status(attrib_name, attrib_value, (struct battery_status *)info_or_status); return; } int get_next_battery_info_or_status(DIR *battery_dir, enum BATTERY_INFO_TYPE type, void *info_or_status) { FILE *file; char line_info[256]; if ( !info_or_status ) return 0; if (type == BIF) memset(info_or_status, 0, sizeof(struct battery_info)); else memset(info_or_status, 0, sizeof(struct battery_status)); file = get_next_battery_file(battery_dir, type); if ( !file ) return 0; while ( fgets(line_info, sizeof(line_info), file) != NULL ) parse_battery_info_or_status(line_info, type, info_or_status); fclose(file); return 1; } #ifdef RUN_STANDALONE void print_battery_info(struct battery_info *info) { printf("present: %d\n", info->present); printf("design capacity: %d\n", info->design_capacity); printf("last full capacity: %d\n", info->last_full_capacity); printf("battery technology: %d\n", info->battery_technology); printf("design voltage: %d\n", info->design_voltage); printf("design capacity warning:%d\n", info->design_capacity_warning); printf("design capacity low: %d\n", info->design_capacity_low); printf("capacity granularity 1: %d\n", info->capacity_granularity_1); printf("capacity granularity 2: %d\n", info->capacity_granularity_2); printf("model number: %s\n", info->model_number); printf("serial number: %s\n", info->serial_number); printf("battery type: %s\n", info->battery_type); printf("OEM info: %s\n", info->oem_info); } #endif /*RUN_STANDALONE*/ void write_ulong_lsb_first(char *temp_val, unsigned long val) { snprintf(temp_val, 9, "%02x%02x%02x%02x", (unsigned int)val & 0xff, (unsigned int)(val & 0xff00) >> 8, (unsigned int)(val & 0xff0000) >> 16, (unsigned int)(val & 0xff000000) >> 24); } void write_battery_info_to_xenstore(struct battery_info *info) { char val[1024], string_info[256]; xs_mkdir(xs, XBT_NULL, "/pm"); memset(val, 0, 1024); memset(string_info, 0, 256); /* write 9 dwords (so 9*4) + length of 4 strings + 4 null terminators */ snprintf(val, 3, "%02x", (unsigned int)(9*4 + strlen(info->model_number) + strlen(info->serial_number) + strlen(info->battery_type) + strlen(info->oem_info) + 4)); write_ulong_lsb_first(val+2, info->present); write_ulong_lsb_first(val+10, info->design_capacity); write_ulong_lsb_first(val+18, info->last_full_capacity); write_ulong_lsb_first(val+26, info->battery_technology); write_ulong_lsb_first(val+34, info->design_voltage); write_ulong_lsb_first(val+42, info->design_capacity_warning); write_ulong_lsb_first(val+50, info->design_capacity_low); write_ulong_lsb_first(val+58, info->capacity_granularity_1); write_ulong_lsb_first(val+66, info->capacity_granularity_2); snprintf(string_info, 256, "%02x%s%02x%s%02x%s%02x%s", (unsigned int)strlen(info->model_number), info->model_number, (unsigned int)strlen(info->serial_number), info->serial_number, (unsigned int)strlen(info->battery_type), info->battery_type, (unsigned int)strlen(info->oem_info), info->oem_info); strncat(val+73, string_info, 1024-73-1); xs_write(xs, XBT_NULL, "/pm/bif", val, 73+8+strlen(info->model_number)+strlen(info->serial_number)+ strlen(info->battery_type)+strlen(info->oem_info)+1); } int write_one_time_battery_info(void) { DIR *dir; int ret = 0; struct battery_info info; dir = opendir(BATTERY_DIR_PATH); if ( !dir ) return 0; while ( get_next_battery_info_or_status(dir, BIF, (void *)&info) ) { #ifdef RUN_STANDALONE print_battery_info(&info); #endif if ( info.present == YES ) { write_battery_info_to_xenstore(&info); ret = 1; break; /* rethink this... */ } } closedir(dir); return ret; } #ifdef RUN_STANDALONE void print_battery_status(struct battery_status *status) { printf("present: %d\n", status->present); printf("Battery state %d\n", status->state); printf("Battery present rate %d\n", status->present_rate); printf("Battery remining capacity %d\n", status->remaining_capacity); printf("Battery present voltage %d\n", status->present_voltage); } #endif /*RUN_STANDALONE*/ void write_battery_status_to_xenstore(struct battery_status *status) { char val[35]; xs_mkdir(xs, XBT_NULL, "/pm"); memset(val, 0, 35); snprintf(val, 3, "%02x", 16); write_ulong_lsb_first(val+2, status->state); write_ulong_lsb_first(val+10, status->present_rate); write_ulong_lsb_first(val+18, status->remaining_capacity); write_ulong_lsb_first(val+26, status->present_voltage); xs_write(xs, XBT_NULL, "/pm/bst", val, 35); } int wait_for_and_update_battery_status_request(void) { DIR *dir; int ret = 0; unsigned int count; struct battery_status status; while ( true ) { /* KN:@TODO - It is rather inefficient to not cache the file handle. * Switch to caching file handle. */ dir = opendir(BATTERY_DIR_PATH); if ( !dir ) return 0; while ( get_next_battery_info_or_status(dir, BST, (void *)&status) ) { #ifdef RUN_STANDALONE print_battery_status(&status); #endif if ( status.present == YES ) { write_battery_status_to_xenstore(&status); ret = 1; /* rethink this; though I have never seen, there might be * systems out there with more than one battery device * present */ break; } } closedir(dir); xs_watch(xs, "/pm/events", "refreshbatterystatus"); xs_read_watch(xs, &count); } return ret; } /* Borrowed daemonize from xenstored - Initially written by Stevens. */ static void daemonize(void) { pid_t pid; if ( (pid = fork()) < 0 ) exit(1); if ( pid != 0 ) exit(0); setsid(); if ( (pid = fork()) < 0 ) exit(1); if ( pid != 0 ) exit(0); if ( chdir("/") == -1 ) exit(1); umask(0); } int main(int argc, char *argv[]) { #ifndef RUN_STANDALONE daemonize(); #endif xs = (struct xs_handle *)xs_daemon_open(); if ( xs == NULL ) return -1; if ( write_one_time_battery_info() == 0 ) { xs_daemon_close(xs); return -1; } wait_for_and_update_battery_status_request(); xs_daemon_close(xs); return 0; } xen-4.9.2/tools/xenpmd/Makefile0000664000175000017500000000073113256712137014551 0ustar smbsmbXEN_ROOT=$(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk CFLAGS += -Werror CFLAGS += $(CFLAGS_libxenstore) LDLIBS += $(LDLIBS_libxenstore) .PHONY: all all: xenpmd .PHONY: install install: all $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_PROG) xenpmd $(DESTDIR)$(sbindir) .PHONY: clean clean: $(RM) -f xenpmd xenpmd.o $(DEPS) .PHONY: distclean distclean: clean xenpmd: xenpmd.o Makefile $(CC) $(LDFLAGS) $< -o $@ $(LDLIBS) $(APPEND_LDFLAGS) -include $(DEPS) xen-4.9.2/tools/libxl/0000775000175000017500000000000013256712137012727 5ustar smbsmbxen-4.9.2/tools/libxl/libxl_uuid.c0000664000175000017500000000722613256712137015242 0ustar smbsmb/* * Copyright (C) 2008,2010 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #if defined(__linux__) int libxl_uuid_is_nil(const libxl_uuid *uuid) { return uuid_is_null(uuid->uuid); } void libxl_uuid_generate(libxl_uuid *uuid) { uuid_generate(uuid->uuid); } int libxl_uuid_from_string(libxl_uuid *uuid, const char *in) { return uuid_parse(in, uuid->uuid); } void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst, const libxl_uuid *src) { uuid_copy(dst->uuid, src->uuid); } void libxl_uuid_clear(libxl_uuid *uuid) { uuid_clear(uuid->uuid); } int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2) { return uuid_compare(uuid1->uuid, uuid2->uuid); } const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid) { return uuid->uuid; } uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid) { return uuid->uuid; } #elif defined(__FreeBSD__) || defined(__NetBSD__) int libxl_uuid_is_nil(const libxl_uuid *uuid) { uint32_t status; uuid_t nat_uuid; uuid_dec_be(uuid->uuid, &nat_uuid); return uuid_is_nil(&nat_uuid, &status); } void libxl_uuid_generate(libxl_uuid *uuid) { uint32_t status; uuid_t nat_uuid; uuid_create(&nat_uuid, &status); assert(status == uuid_s_ok); uuid_enc_be(uuid->uuid, &nat_uuid); } #ifdef __FreeBSD__ int libxl_uuid_from_string(libxl_uuid *uuid, const char *in) { uint32_t status; uuid_t nat_uuid; uuid_from_string(in, &nat_uuid, &status); if (status != uuid_s_ok) return ERROR_FAIL; uuid_enc_be(uuid->uuid, &nat_uuid); return 0; } #else #define LIBXL__UUID_PTRS(uuid) &uuid[0], &uuid[1], &uuid[2], &uuid[3], \ &uuid[4], &uuid[5], &uuid[6], &uuid[7], \ &uuid[8], &uuid[9], &uuid[10],&uuid[11], \ &uuid[12],&uuid[13],&uuid[14],&uuid[15] int libxl_uuid_from_string(libxl_uuid *uuid, const char *in) { if ( sscanf(in, LIBXL_UUID_FMT, LIBXL__UUID_PTRS(uuid->uuid)) != sizeof(uuid->uuid) ) return -1; return 0; } #undef LIBXL__UUID_PTRS #endif void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst, const libxl_uuid *src) { memcpy(&dst->uuid, &src->uuid, sizeof(dst->uuid)); } void libxl_uuid_clear(libxl_uuid *uuid) { memset(&uuid->uuid, 0, sizeof(uuid->uuid)); } #ifdef __FreeBSD__ int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2) { uuid_t nat_uuid1, nat_uuid2; uuid_dec_be(uuid1->uuid, &nat_uuid1); uuid_dec_be(uuid2->uuid, &nat_uuid2); return uuid_compare(&nat_uuid1, &nat_uuid2, NULL); } #else int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2) { return memcmp(uuid1->uuid, uuid2->uuid, sizeof(uuid1->uuid)); } #endif const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid) { return uuid->uuid; } uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid) { return uuid->uuid; } #else #error "Please update libxl_uuid.c for your OS" #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/xenlight.pc.in0000664000175000017500000000053613256712137015506 0ustar smbsmbprefix=@@prefix@@ includedir=@@incdir@@ libdir=@@libdir@@ xenfirmwaredir=@@firmwaredir@@ libexec_bin=@@libexecbin@@ Name: Xenlight Description: The Xenlight library for Xen hypervisor Version: @@version@@ Cflags: -I${includedir} Libs: @@libsflag@@${libdir} -lxenlight Requires.private: xentoollog,xenevtchn,xencontrol,xenguest,xenstore,xenblktapctl xen-4.9.2/tools/libxl/libxl_stream_write.c0000664000175000017500000005553413256712137017006 0ustar smbsmb/* * Copyright (C) 2015 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" /* * Infrastructure for writing a domain to a libxl migration v2 stream. * * Entry points from outside: * - libxl__stream_write_start() * - Start writing a stream from the start. * - libxl__stream_write_start_checkpoint() * - Write the records which form a checkpoint into a stream. * * In normal operation, there are two tasks running at once; this * stream processing, and the libxl-save-helper. check_all_finished() * is used to join all the tasks in both success and error cases. * * Nomenclature for event callbacks: * - $FOO_done(): Completion callback for $FOO * - write_$FOO(): Set up the datacopier to write a $FOO * - $BAR_header(): A $BAR record header only * - $BAR_record(): A complete $BAR record with header and content * * The main loop for a plain VM writes: * - Stream header * - Libxc record * - (optional) Emulator xenstore record * - if (hvm) * - Emulator context record * - End record * * For checkpointed stream, there is a second loop which is triggered by a * save-helper checkpoint callback. It writes: * - (optional) Emulator xenstore record * - if (hvm) * - Emulator context record * - Checkpoint end record * * For back channel stream: * - libxl__stream_write_start() * - Set up the stream to running state * * - Use libxl__stream_write_checkpoint_state to write the record. When the * record is written out, call stream->checkpoint_callback() to return. */ /* Success/error/cleanup handling. */ static void stream_success(libxl__egc *egc, libxl__stream_write_state *stream); static void stream_complete(libxl__egc *egc, libxl__stream_write_state *stream, int rc); static void stream_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc); static void checkpoint_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc); static void check_all_finished(libxl__egc *egc, libxl__stream_write_state *stream, int rc); /* Event chain for a plain VM. */ static void stream_header_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); static void libxc_header_done(libxl__egc *egc, libxl__stream_write_state *stream); /* libxl__xc_domain_save_done() lives here, event-order wise. */ static void write_emulator_xenstore_record(libxl__egc *egc, libxl__stream_write_state *stream); static void emulator_xenstore_record_done(libxl__egc *egc, libxl__stream_write_state *stream); static void write_emulator_context_record(libxl__egc *egc, libxl__stream_write_state *stream); static void emulator_context_read_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); static void emulator_context_record_done(libxl__egc *egc, libxl__stream_write_state *stream); static void write_end_record(libxl__egc *egc, libxl__stream_write_state *stream); /* Event chain unique to checkpointed streams. */ static void write_checkpoint_end_record(libxl__egc *egc, libxl__stream_write_state *stream); static void checkpoint_end_record_done(libxl__egc *egc, libxl__stream_write_state *stream); /* checkpoint state */ static void write_checkpoint_state_done(libxl__egc *egc, libxl__stream_write_state *stream); static void checkpoint_state_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc); /*----- Helpers -----*/ static void write_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); /* Generic helper to set up writing some data to the stream. */ static void setup_generic_write(libxl__egc *egc, libxl__stream_write_state *stream, const char *what, libxl__sr_rec_hdr *hdr, libxl__sr_emulator_hdr *emu_hdr, void *body, sws_record_done_cb cb) { static const uint8_t zero_padding[1U << REC_ALIGN_ORDER] = { 0 }; libxl__datacopier_state *dc = &stream->dc; int rc; assert(stream->record_done_callback == NULL); dc->writewhat = what; dc->used = 0; dc->callback = write_done; rc = libxl__datacopier_start(dc); if (rc) { stream_complete(egc, stream, rc); return; } size_t padsz = ROUNDUP(hdr->length, REC_ALIGN_ORDER) - hdr->length; uint32_t length = hdr->length; /* Insert header */ libxl__datacopier_prefixdata(egc, dc, hdr, sizeof(*hdr)); /* Optional emulator sub-header */ if (emu_hdr) { assert(length >= sizeof(*emu_hdr)); libxl__datacopier_prefixdata(egc, dc, emu_hdr, sizeof(*emu_hdr)); length -= sizeof(*emu_hdr); } /* Optional body */ if (body) libxl__datacopier_prefixdata(egc, dc, body, length); /* Any required padding */ if (padsz > 0) libxl__datacopier_prefixdata(egc, dc, zero_padding, padsz); stream->record_done_callback = cb; } /* Helper to set up writing a regular record to the stream. */ static void setup_write(libxl__egc *egc, libxl__stream_write_state *stream, const char *what, libxl__sr_rec_hdr *hdr, void *body, sws_record_done_cb cb) { setup_generic_write(egc, stream, what, hdr, NULL, body, cb); } /* Helper to set up writing a record with an emulator prefix to the stream. */ static void setup_emulator_write(libxl__egc *egc, libxl__stream_write_state *stream, const char *what, libxl__sr_rec_hdr *hdr, libxl__sr_emulator_hdr *emu_hdr, void *body, sws_record_done_cb cb) { assert(stream->emu_sub_hdr.id != EMULATOR_UNKNOWN); assert(stream->device_model_version != LIBXL_DEVICE_MODEL_VERSION_NONE); setup_generic_write(egc, stream, what, hdr, emu_hdr, body, cb); } static void write_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc); STATE_AO_GC(stream->ao); sws_record_done_cb cb = stream->record_done_callback; stream->record_done_callback = NULL; if (onwrite || errnoval) stream_complete(egc, stream, rc ?: ERROR_FAIL); else cb(egc, stream); } /*----- Entrypoints -----*/ void libxl__stream_write_init(libxl__stream_write_state *stream) { assert(stream->ao); stream->shs.ao = stream->ao; libxl__save_helper_init(&stream->shs); stream->rc = 0; stream->running = false; stream->in_checkpoint = false; stream->sync_teardown = false; FILLZERO(stream->dc); stream->record_done_callback = NULL; FILLZERO(stream->emu_dc); stream->emu_carefd = NULL; FILLZERO(stream->emu_rec_hdr); FILLZERO(stream->emu_sub_hdr); stream->emu_body = NULL; stream->device_model_version = LIBXL_DEVICE_MODEL_VERSION_UNKNOWN; } void libxl__stream_write_start(libxl__egc *egc, libxl__stream_write_state *stream) { libxl__datacopier_state *dc = &stream->dc; libxl__domain_save_state *dss = stream->dss; STATE_AO_GC(stream->ao); struct libxl__sr_hdr hdr; int rc = 0; libxl__stream_write_init(stream); stream->running = true; dc->ao = ao; dc->readfd = -1; dc->writewhat = "stream header"; dc->copywhat = "save v2 stream"; dc->writefd = stream->fd; dc->maxsz = -1; dc->callback = stream_header_done; if (stream->back_channel) return; if (dss->type == LIBXL_DOMAIN_TYPE_HVM) { stream->device_model_version = libxl__device_model_version_running(gc, dss->domid); switch (stream->device_model_version) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: stream->emu_sub_hdr.id = EMULATOR_QEMU_TRADITIONAL; break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: stream->emu_sub_hdr.id = EMULATOR_QEMU_UPSTREAM; break; case LIBXL_DEVICE_MODEL_VERSION_NONE: stream->emu_sub_hdr.id = EMULATOR_UNKNOWN; break; default: rc = ERROR_FAIL; LOGD(ERROR, dss->domid, "Unknown emulator for HVM domain"); goto err; } stream->emu_sub_hdr.index = 0; } rc = libxl__datacopier_start(dc); if (rc) goto err; FILLZERO(hdr); hdr.ident = htobe64(RESTORE_STREAM_IDENT); hdr.version = htobe32(RESTORE_STREAM_VERSION); hdr.options = htobe32(0); libxl__datacopier_prefixdata(egc, dc, &hdr, sizeof(hdr)); return; err: assert(rc); stream_complete(egc, stream, rc); } void libxl__stream_write_start_checkpoint(libxl__egc *egc, libxl__stream_write_state *stream) { assert(stream->running); assert(!stream->in_checkpoint); assert(!stream->back_channel); stream->in_checkpoint = true; write_emulator_xenstore_record(egc, stream); } void libxl__stream_write_abort(libxl__egc *egc, libxl__stream_write_state *stream, int rc) { assert(rc); if (stream->running) stream_complete(egc, stream, rc); } /*----- Event logic -----*/ static void stream_header_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, dc); STATE_AO_GC(stream->ao); struct libxl__sr_rec_hdr rec; if (rc || errnoval) { stream_complete(egc, stream, rc ?: ERROR_FAIL); return; } FILLZERO(rec); rec.type = REC_TYPE_LIBXC_CONTEXT; setup_write(egc, stream, "libxc header", &rec, NULL, libxc_header_done); } static void libxc_header_done(libxl__egc *egc, libxl__stream_write_state *stream) { libxl__xc_domain_save(egc, stream->dss, &stream->shs); } void libxl__xc_domain_save_done(libxl__egc *egc, void *dss_void, int rc, int retval, int errnoval) { libxl__domain_save_state *dss = dss_void; libxl__stream_write_state *stream = &dss->sws; STATE_AO_GC(dss->ao); if (rc) goto err; if (retval) { LOGEVD(ERROR, errnoval, dss->domid, "saving domain: %s", dss->dsps.guest_responded ? "domain responded to suspend request" : "domain did not respond to suspend request"); if (!dss->dsps.guest_responded) rc = ERROR_GUEST_TIMEDOUT; else if (dss->rc) rc = dss->rc; else rc = ERROR_FAIL; goto err; } err: check_all_finished(egc, stream, rc); /* * This function is the callback associated with the save helper * task, not the stream task. We do not know whether the stream is * alive, and check_all_finished() may have torn it down around us. * If the stream is not still alive, we must not continue any work. */ if (libxl__stream_write_inuse(stream)) { if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE) /* * For remus, if libxl__xc_domain_save_done() completes, * there was an error sending data to the secondary. * Resume the primary ASAP. The caller doesn't care of the * return value (Please refer to libxl__remus_teardown()) */ stream_complete(egc, stream, 0); else write_emulator_xenstore_record(egc, stream); } } static void write_emulator_xenstore_record(libxl__egc *egc, libxl__stream_write_state *stream) { libxl__domain_save_state *dss = stream->dss; STATE_AO_GC(stream->ao); struct libxl__sr_rec_hdr rec; int rc; char *buf = NULL; uint32_t len = 0; if (stream->device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) { emulator_xenstore_record_done(egc, stream); return; } rc = libxl__save_emulator_xenstore_data(dss, &buf, &len); if (rc) goto err; /* No record? - All done. */ if (len == 0) { emulator_xenstore_record_done(egc, stream); return; } FILLZERO(rec); rec.type = REC_TYPE_EMULATOR_XENSTORE_DATA; rec.length = len + sizeof(stream->emu_sub_hdr); setup_emulator_write(egc, stream, "emulator xenstore record", &rec, &stream->emu_sub_hdr, buf, emulator_xenstore_record_done); return; err: assert(rc); stream_complete(egc, stream, rc); } static void emulator_xenstore_record_done(libxl__egc *egc, libxl__stream_write_state *stream) { libxl__domain_save_state *dss = stream->dss; if (dss->type == LIBXL_DOMAIN_TYPE_HVM) write_emulator_context_record(egc, stream); else { if (stream->in_checkpoint) write_checkpoint_end_record(egc, stream); else write_end_record(egc, stream); } } static void write_emulator_context_record(libxl__egc *egc, libxl__stream_write_state *stream) { libxl__domain_save_state *dss = stream->dss; libxl__datacopier_state *dc = &stream->emu_dc; STATE_AO_GC(stream->ao); struct libxl__sr_rec_hdr *rec = &stream->emu_rec_hdr; struct stat st; int rc; assert(dss->type == LIBXL_DOMAIN_TYPE_HVM); if (stream->device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) { emulator_context_record_done(egc, stream); return; } /* Convenience aliases */ const char *const filename = dss->dsps.dm_savefile; libxl__carefd_begin(); int readfd = open(filename, O_RDONLY); stream->emu_carefd = libxl__carefd_opened(CTX, readfd); if (readfd == -1) { rc = ERROR_FAIL; LOGED(ERROR, dss->domid, "unable to open %s", filename); goto err; } if (fstat(readfd, &st)) { rc = ERROR_FAIL; LOGED(ERROR, dss->domid, "unable to fstat %s", filename); goto err; } if (!S_ISREG(st.st_mode)) { rc = ERROR_FAIL; LOGD(ERROR, dss->domid, "%s is not a plain file!", filename); goto err; } rec->type = REC_TYPE_EMULATOR_CONTEXT; rec->length = st.st_size + sizeof(stream->emu_sub_hdr); stream->emu_body = libxl__malloc(NOGC, st.st_size); FILLZERO(*dc); dc->ao = stream->ao; dc->readwhat = "qemu save file"; dc->copywhat = "save v2 stream"; dc->readfd = readfd; dc->writefd = -1; dc->maxsz = -1; dc->readbuf = stream->emu_body; dc->bytes_to_read = st.st_size; dc->callback = emulator_context_read_done; rc = libxl__datacopier_start(dc); if (rc) goto err; return; err: assert(rc); stream_complete(egc, stream, rc); } static void emulator_context_read_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__stream_write_state *stream = CONTAINER_OF(dc, *stream, emu_dc); STATE_AO_GC(stream->ao); if (rc || onwrite || errnoval) { stream_complete(egc, stream, rc ?: ERROR_FAIL); return; } libxl__carefd_close(stream->emu_carefd); stream->emu_carefd = NULL; setup_emulator_write(egc, stream, "emulator record", &stream->emu_rec_hdr, &stream->emu_sub_hdr, stream->emu_body, emulator_context_record_done); } static void emulator_context_record_done(libxl__egc *egc, libxl__stream_write_state *stream) { free(stream->emu_body); stream->emu_body = NULL; if (stream->in_checkpoint) write_checkpoint_end_record(egc, stream); else write_end_record(egc, stream); } static void write_end_record(libxl__egc *egc, libxl__stream_write_state *stream) { struct libxl__sr_rec_hdr rec; FILLZERO(rec); rec.type = REC_TYPE_END; setup_write(egc, stream, "end record", &rec, NULL, stream_success); } static void write_checkpoint_end_record(libxl__egc *egc, libxl__stream_write_state *stream) { struct libxl__sr_rec_hdr rec; FILLZERO(rec); rec.type = REC_TYPE_CHECKPOINT_END; setup_write(egc, stream, "checkpoint end record", &rec, NULL, checkpoint_end_record_done); } static void checkpoint_end_record_done(libxl__egc *egc, libxl__stream_write_state *stream) { checkpoint_done(egc, stream, 0); } /*----- Success/error/cleanup handling. -----*/ static void stream_success(libxl__egc *egc, libxl__stream_write_state *stream) { stream_complete(egc, stream, 0); } static void stream_complete(libxl__egc *egc, libxl__stream_write_state *stream, int rc) { assert(stream->running); if (stream->in_checkpoint) { assert(rc); /* * If an error is encountered while in a checkpoint, pass it * back to libxc. The failure will come back around to us via * libxl__xc_domain_save_done() */ checkpoint_done(egc, stream, rc); return; } if (stream->in_checkpoint_state) { assert(rc); /* * If an error is encountered while in a checkpoint, pass it * back to libxc. The failure will come back around to us via * 1. normal stream * libxl__xc_domain_save_done() * 2. back_channel stream * libxl__stream_write_abort() */ checkpoint_state_done(egc, stream, rc); return; } stream_done(egc, stream, rc); } static void stream_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc) { assert(stream->running); assert(!stream->in_checkpoint_state); stream->running = false; if (stream->emu_carefd) libxl__carefd_close(stream->emu_carefd); free(stream->emu_body); if (!stream->back_channel) { /* * 1. In stream_done(), stream->running is set to false, so * the stream itself is not in use. * 2. Write stream is a back channel stream, this means it * is only used by secondary(restore side) to send records * back, so it doesn't have save helper. * So we don't need invoke check_all_finished here */ check_all_finished(egc, stream, rc); } } static void checkpoint_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc) { assert(stream->in_checkpoint); stream->in_checkpoint = false; stream->checkpoint_callback(egc, stream, rc); } static void check_all_finished(libxl__egc *egc, libxl__stream_write_state *stream, int rc) { STATE_AO_GC(stream->ao); /* * In the case of a failure, the _abort()'s below might cancel * synchronously on top of us, or asynchronously at a later point. * * We must avoid the situation where all _abort() cancel * synchronously and the completion_callback() gets called twice; * once by the first error and once by the final stacked abort(), * both of whom will find that all of the tasks have stopped. * * To avoid this problem, any stacked re-entry into this function is * ineligible to fire the completion callback. The outermost * instance will take care of completing, once the stack has * unwound. */ if (stream->sync_teardown) return; if (!stream->rc && rc) { /* First reported failure. Tear everything down. */ stream->rc = rc; stream->sync_teardown = true; libxl__stream_write_abort(egc, stream, rc); libxl__save_helper_abort(egc, &stream->shs); stream->sync_teardown = false; } /* Don't fire the callback until all our parallel tasks have stopped. */ if (libxl__stream_write_inuse(stream) || libxl__save_helper_inuse(&stream->shs)) return; if (stream->completion_callback) /* back channel stream doesn't have completion_callback() */ stream->completion_callback(egc, stream, stream->rc); } /*----- checkpoint state -----*/ void libxl__stream_write_checkpoint_state(libxl__egc *egc, libxl__stream_write_state *stream, libxl_sr_checkpoint_state *srcs) { struct libxl__sr_rec_hdr rec; assert(stream->running); assert(!stream->in_checkpoint); assert(!stream->in_checkpoint_state); stream->in_checkpoint_state = true; FILLZERO(rec); rec.type = REC_TYPE_CHECKPOINT_STATE; rec.length = sizeof(*srcs); setup_write(egc, stream, "checkpoint state", &rec, srcs, write_checkpoint_state_done); } static void write_checkpoint_state_done(libxl__egc *egc, libxl__stream_write_state *stream) { checkpoint_state_done(egc, stream, 0); } static void checkpoint_state_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc) { assert(stream->in_checkpoint_state); stream->in_checkpoint_state = false; stream->checkpoint_callback(egc, stream, rc); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_internal.h0000664000175000017500000000344213256712137016276 0ustar smbsmb/* * Copyright (C) 2010 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef LIBXLU_INTERNAL_H #define LIBXLU_INTERNAL_H #include #include #include #include #include #include #include "libxlutil.h" struct XLU_ConfigList { int avalues; /* available slots */ int nvalues; /* actual occupied slots */ XLU_ConfigValue **values; }; typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; } YYLTYPE; #define YYLTYPE_IS_DECLARED struct XLU_ConfigValue { enum XLU_ConfigValueType type; union { char *string; XLU_ConfigList list; } u; YYLTYPE loc; }; typedef struct XLU_ConfigSetting { /* transparent */ struct XLU_ConfigSetting *next; char *name; XLU_ConfigValue *value; int lineno; } XLU_ConfigSetting; struct XLU_Config { XLU_ConfigSetting *settings; FILE *report; char *config_source; }; typedef struct { XLU_Config *cfg; int err, lexerrlineno, likely_python; void *scanner; } CfgParseContext; #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #endif /*LIBXLU_INTERNAL_H*/ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_console.c0000664000175000017500000006530613256712137015741 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" static int libxl__console_tty_path(libxl__gc *gc, uint32_t domid, int cons_num, libxl_console_type type, char **tty_path) { int rc; char *dom_path; dom_path = libxl__xs_get_dompath(gc, domid); if (!dom_path) { rc = ERROR_FAIL; goto out; } switch (type) { case LIBXL_CONSOLE_TYPE_SERIAL: *tty_path = GCSPRINTF("%s/serial/%d/tty", dom_path, cons_num); rc = 0; break; case LIBXL_CONSOLE_TYPE_PV: if (cons_num == 0) *tty_path = GCSPRINTF("%s/console/tty", dom_path); else *tty_path = GCSPRINTF("%s/device/console/%d/tty", dom_path, cons_num); rc = 0; break; default: rc = ERROR_INVAL; goto out; } out: return rc; } int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num, libxl_console_type type, int notify_fd) { GC_INIT(ctx); char *p = GCSPRINTF("%s/xenconsole", libxl__private_bindir_path()); char *domid_s = GCSPRINTF("%d", domid); char *cons_num_s = GCSPRINTF("%d", cons_num); char *notify_fd_s; char *cons_type_s; switch (type) { case LIBXL_CONSOLE_TYPE_PV: cons_type_s = "pv"; break; case LIBXL_CONSOLE_TYPE_SERIAL: cons_type_s = "serial"; break; default: goto out; } if (notify_fd != -1) { notify_fd_s = GCSPRINTF("%d", notify_fd); execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s, "--start-notify-fd", notify_fd_s, (void *)NULL); } else { execl(p, p, domid_s, "--num", cons_num_s, "--type", cons_type_s, (void *)NULL); } out: GC_FREE; return ERROR_FAIL; } int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num, libxl_console_type type, char **path) { GC_INIT(ctx); char *tty_path; char *tty; int rc; rc = libxl__console_tty_path(gc, domid, cons_num, type, &tty_path); if (rc) { LOGD(ERROR, domid, "Failed to get tty path\n"); goto out; } tty = libxl__xs_read(gc, XBT_NULL, tty_path); if (!tty || tty[0] == '\0') { LOGED(ERROR, domid, "Unable to read console tty path `%s'", tty_path); rc = ERROR_FAIL; goto out; } *path = libxl__strdup(NOGC, tty); rc = 0; out: GC_FREE; return rc; } static int libxl__primary_console_find(libxl_ctx *ctx, uint32_t domid_vm, uint32_t *domid, int *cons_num, libxl_console_type *type) { GC_INIT(ctx); uint32_t stubdomid = libxl_get_stubdom_id(ctx, domid_vm); int rc; if (stubdomid) { *domid = stubdomid; *cons_num = STUBDOM_CONSOLE_SERIAL; *type = LIBXL_CONSOLE_TYPE_PV; } else { switch (libxl__domain_type(gc, domid_vm)) { case LIBXL_DOMAIN_TYPE_HVM: *domid = domid_vm; *cons_num = 0; *type = LIBXL_CONSOLE_TYPE_SERIAL; break; case LIBXL_DOMAIN_TYPE_PV: *domid = domid_vm; *cons_num = 0; *type = LIBXL_CONSOLE_TYPE_PV; break; case LIBXL_DOMAIN_TYPE_INVALID: rc = ERROR_INVAL; goto out; default: abort(); } } rc = 0; out: GC_FREE; return rc; } int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm, int notify_fd) { uint32_t domid; int cons_num; libxl_console_type type; int rc; rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type); if ( rc ) return rc; return libxl_console_exec(ctx, domid, cons_num, type, notify_fd); } int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, char **path) { uint32_t domid; int cons_num; libxl_console_type type; int rc; rc = libxl__primary_console_find(ctx, domid_vm, &domid, &cons_num, &type); if ( rc ) return rc; return libxl_console_get_tty(ctx, domid, cons_num, type, path); } int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass) { GC_INIT(ctx); const char *vnc_port; const char *vnc_listen = NULL, *vnc_pass = NULL; int port = 0, autopass_fd = -1; char *vnc_bin, *args[] = { "vncviewer", NULL, /* hostname:display */ NULL, /* -autopass */ NULL, }; vnc_port = libxl__xs_read(gc, XBT_NULL, GCSPRINTF( "/local/domain/%d/console/vnc-port", domid)); if (!vnc_port) { LOGD(ERROR, domid, "Cannot get vnc-port"); goto x_fail; } port = atoi(vnc_port) - 5900; vnc_listen = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("/local/domain/%d/console/vnc-listen", domid)); if ( autopass ) vnc_pass = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("/local/domain/%d/console/vnc-pass", domid)); if ( NULL == vnc_listen ) vnc_listen = "localhost"; if ( (vnc_bin = getenv("VNCVIEWER")) ) args[0] = vnc_bin; args[1] = GCSPRINTF("%s:%d", vnc_listen, port); if ( vnc_pass ) { char tmpname[] = "/tmp/vncautopass.XXXXXX"; autopass_fd = mkstemp(tmpname); if ( autopass_fd < 0 ) { LOGED(ERROR, domid, "mkstemp %s failed", tmpname); goto x_fail; } if ( unlink(tmpname) ) { /* should never happen */ LOGED(ERROR, domid, "unlink %s failed", tmpname); goto x_fail; } if ( libxl_write_exactly(ctx, autopass_fd, vnc_pass, strlen(vnc_pass), tmpname, "vnc password") ) goto x_fail; if ( lseek(autopass_fd, SEEK_SET, 0) ) { LOGED(ERROR, domid, "rewind %s (autopass) failed", tmpname); goto x_fail; } args[2] = "-autopass"; } libxl__exec(gc, autopass_fd, -1, -1, args[0], args, NULL); x_fail: GC_FREE; return ERROR_FAIL; } int libxl__device_console_add(libxl__gc *gc, uint32_t domid, libxl__device_console *console, libxl__domain_build_state *state, libxl__device *device) { flexarray_t *front, *ro_front; flexarray_t *back; int rc; if (console->devid && state) { rc = ERROR_INVAL; goto out; } if (!console->devid && (console->name || console->path)) { LOGD(ERROR, domid, "Primary console has invalid configuration"); rc = ERROR_INVAL; goto out; } front = flexarray_make(gc, 16, 1); ro_front = flexarray_make(gc, 16, 1); back = flexarray_make(gc, 16, 1); device->backend_devid = console->devid; device->backend_domid = console->backend_domid; device->backend_kind = LIBXL__DEVICE_KIND_CONSOLE; device->devid = console->devid; device->domid = domid; device->kind = LIBXL__DEVICE_KIND_CONSOLE; flexarray_append(back, "frontend-id"); flexarray_append(back, GCSPRINTF("%d", domid)); flexarray_append(back, "online"); flexarray_append(back, "1"); flexarray_append(back, "state"); flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append(back, "protocol"); flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL); if (console->name) { flexarray_append(ro_front, "name"); flexarray_append(ro_front, console->name); flexarray_append(back, "name"); flexarray_append(back, console->name); } if (console->connection) { flexarray_append(back, "connection"); flexarray_append(back, console->connection); } if (console->path) { flexarray_append(back, "path"); flexarray_append(back, console->path); } flexarray_append(front, "backend-id"); flexarray_append(front, GCSPRINTF("%d", console->backend_domid)); flexarray_append(ro_front, "limit"); flexarray_append(ro_front, GCSPRINTF("%d", LIBXL_XENCONSOLE_LIMIT)); flexarray_append(ro_front, "type"); if (console->consback == LIBXL__CONSOLE_BACKEND_XENCONSOLED) flexarray_append(ro_front, "xenconsoled"); else flexarray_append(ro_front, "ioemu"); flexarray_append(ro_front, "output"); flexarray_append(ro_front, console->output); flexarray_append(ro_front, "tty"); if (state && state->console_tty) flexarray_append(ro_front, state->console_tty); else flexarray_append(ro_front, ""); if (state) { flexarray_append(ro_front, "port"); flexarray_append(ro_front, GCSPRINTF("%"PRIu32, state->console_port)); flexarray_append(ro_front, "ring-ref"); flexarray_append(ro_front, GCSPRINTF("%lu", state->console_mfn)); } else { flexarray_append(front, "state"); flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append(front, "protocol"); flexarray_append(front, LIBXL_XENCONSOLE_PROTOCOL); } libxl__device_generic_add(gc, XBT_NULL, device, libxl__xs_kvs_of_flexarray(gc, back), libxl__xs_kvs_of_flexarray(gc, front), libxl__xs_kvs_of_flexarray(gc, ro_front)); rc = 0; out: return rc; } int libxl__init_console_from_channel(libxl__gc *gc, libxl__device_console *console, int dev_num, libxl_device_channel *channel) { int rc; libxl__device_console_init(console); /* Perform validation first, allocate second. */ if (!channel->name) { LOG(ERROR, "channel %d has no name", channel->devid); return ERROR_INVAL; } if (channel->backend_domname) { rc = libxl_domain_qualifier_to_domid(CTX, channel->backend_domname, &channel->backend_domid); if (rc < 0) return rc; } /* The xenstore 'output' node tells the backend what to connect the console to. If the channel has "connection = pty" then the "output" node will be set to "pty". If the channel has "connection = socket" then the "output" node will be set to "chardev:libxl-channel%d". This tells the qemu backend to proxy data between the console ring and the character device with id "libxl-channel%d". These character devices are currently defined on the qemu command-line via "-chardev" options in libxl_dm.c */ switch (channel->connection) { case LIBXL_CHANNEL_CONNECTION_UNKNOWN: LOG(ERROR, "channel %d has no defined connection; " "to where should it be connected?", channel->devid); return ERROR_INVAL; case LIBXL_CHANNEL_CONNECTION_PTY: console->connection = libxl__strdup(NOGC, "pty"); console->output = libxl__sprintf(NOGC, "pty"); break; case LIBXL_CHANNEL_CONNECTION_SOCKET: if (!channel->u.socket.path) { LOG(ERROR, "channel %d has no path", channel->devid); return ERROR_INVAL; } console->connection = libxl__strdup(NOGC, "socket"); console->path = libxl__strdup(NOGC, channel->u.socket.path); console->output = libxl__sprintf(NOGC, "chardev:libxl-channel%d", channel->devid); break; default: /* We've forgotten to add the clause */ LOG(ERROR, "%s: missing implementation for channel connection %d", __func__, channel->connection); abort(); } console->devid = dev_num; console->consback = LIBXL__CONSOLE_BACKEND_IOEMU; console->backend_domid = channel->backend_domid; console->name = libxl__strdup(NOGC, channel->name); return 0; } static int libxl__device_channel_from_xenstore(libxl__gc *gc, const char *libxl_path, libxl_device_channel *channel) { const char *tmp; int rc; libxl_device_channel_init(channel); rc = libxl__xs_read_checked(NOGC, XBT_NULL, GCSPRINTF("%s/name", libxl_path), (const char **)(&channel->name)); if (rc) goto out; rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/connection", libxl_path), &tmp); if (rc) goto out; if (!strcmp(tmp, "pty")) { channel->connection = LIBXL_CHANNEL_CONNECTION_PTY; } else if (!strcmp(tmp, "socket")) { channel->connection = LIBXL_CHANNEL_CONNECTION_SOCKET; rc = libxl__xs_read_checked(NOGC, XBT_NULL, GCSPRINTF("%s/path", libxl_path), (const char **)(&channel->u.socket.path)); if (rc) goto out; } else { rc = ERROR_INVAL; goto out; } rc = 0; out: return rc; } static int libxl__append_channel_list(libxl__gc *gc, uint32_t domid, libxl_device_channel **channels, int *nchannels) { char *libxl_dir_path = NULL; char **dir = NULL; unsigned int n = 0, devid = 0; libxl_device_channel *next = NULL; int rc = 0, i; libxl_dir_path = GCSPRINTF("%s/device/console", libxl__xs_libxl_path(gc, domid)); dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n); if (!dir || !n) goto out; for (i = 0; i < n; i++) { const char *libxl_path, *name; libxl_device_channel *tmp; libxl_path = GCSPRINTF("%s/%s", libxl_dir_path, dir[i]); name = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/name", libxl_path)); /* 'channels' are consoles with names, so ignore all consoles without names */ if (!name) continue; tmp = realloc(*channels, sizeof(libxl_device_channel) * (*nchannels + devid + 1)); if (!tmp) { rc = ERROR_NOMEM; goto out; } *channels = tmp; next = *channels + *nchannels + devid; rc = libxl__device_channel_from_xenstore(gc, libxl_path, next); if (rc) goto out; next->devid = devid; devid++; } *nchannels += devid; return 0; out: return rc; } libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx, uint32_t domid, int *num) { GC_INIT(ctx); libxl_device_channel *channels = NULL; int rc; *num = 0; rc = libxl__append_channel_list(gc, domid, &channels, num); if (rc) goto out_err; GC_FREE; return channels; out_err: LOGD(ERROR, domid, "Unable to list channels"); while (*num) { (*num)--; libxl_device_channel_dispose(&channels[*num]); } free(channels); return NULL; } int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_channel *channel, libxl_channelinfo *channelinfo) { GC_INIT(ctx); char *dompath, *fe_path, *libxl_path; char *val; int rc; dompath = libxl__xs_get_dompath(gc, domid); channelinfo->devid = channel->devid; fe_path = GCSPRINTF("%s/device/console/%d", dompath, channelinfo->devid + 1); libxl_path = GCSPRINTF("%s/device/console/%d", libxl__xs_libxl_path(gc, domid), channelinfo->devid + 1); channelinfo->backend = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/backend", libxl_path), NULL); if (!channelinfo->backend) { GC_FREE; return ERROR_FAIL; } rc = libxl__backendpath_parse_domid(gc, channelinfo->backend, &channelinfo->backend_id); if (rc) goto out; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path)); channelinfo->state = val ? strtoul(val, NULL, 10) : -1; channelinfo->frontend = libxl__strdup(NOGC, fe_path); channelinfo->frontend_id = domid; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path)); channelinfo->rref = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/port", fe_path)); channelinfo->evtch = val ? strtoul(val, NULL, 10) : -1; channelinfo->connection = channel->connection; switch (channel->connection) { case LIBXL_CHANNEL_CONNECTION_PTY: val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tty", fe_path)); /* * It is obviously very wrong for this value to be in the * frontend. But in XSA-175 we don't want to re-engineer * this because other xenconsole code elsewhere (some * even out of tree, perhaps) expects this node to be * here. * * FE/pty is readonly for the guest. It always exists if * FE does because libxl__device_console_add * unconditionally creates it and nothing deletes it. * * The guest can delete the whole FE (which it has write * privilege on) but the containing directories * /local/GUEST[/device[/console]] are also RO for the * guest. So if the guest deletes FE it cannot recreate * it. * * Therefore the guest cannot cause FE/pty to contain bad * data, although it can cause it to not exist. */ if (!val) val = "/NO-SUCH-PATH"; channelinfo->u.pty.path = strdup(val); break; default: break; } rc = 0; out: GC_FREE; return rc; } int libxl__device_vkb_setdefault(libxl__gc *gc, libxl_device_vkb *vkb) { int rc; rc = libxl__resolve_domid(gc, vkb->backend_domname, &vkb->backend_domid); return rc; } static int libxl__device_from_vkb(libxl__gc *gc, uint32_t domid, libxl_device_vkb *vkb, libxl__device *device) { device->backend_devid = vkb->devid; device->backend_domid = vkb->backend_domid; device->backend_kind = LIBXL__DEVICE_KIND_VKBD; device->devid = vkb->devid; device->domid = domid; device->kind = LIBXL__DEVICE_KIND_VKBD; return 0; } int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int rc; rc = libxl__device_vkb_add(gc, domid, vkb); if (rc) { LOGD(ERROR, domid, "Unable to add vkb device"); goto out; } out: libxl__ao_complete(egc, ao, rc); return AO_INPROGRESS; } int libxl__device_vkb_add(libxl__gc *gc, uint32_t domid, libxl_device_vkb *vkb) { flexarray_t *front; flexarray_t *back; libxl__device device; int rc; rc = libxl__device_vkb_setdefault(gc, vkb); if (rc) goto out; front = flexarray_make(gc, 16, 1); back = flexarray_make(gc, 16, 1); if (vkb->devid == -1) { if ((vkb->devid = libxl__device_nextid(gc, domid, "vkb")) < 0) { rc = ERROR_FAIL; goto out; } } rc = libxl__device_from_vkb(gc, domid, vkb, &device); if (rc != 0) goto out; flexarray_append(back, "frontend-id"); flexarray_append(back, GCSPRINTF("%d", domid)); flexarray_append(back, "online"); flexarray_append(back, "1"); flexarray_append(back, "state"); flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append(front, "backend-id"); flexarray_append(front, GCSPRINTF("%d", vkb->backend_domid)); flexarray_append(front, "state"); flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising)); libxl__device_generic_add(gc, XBT_NULL, &device, libxl__xs_kvs_of_flexarray(gc, back), libxl__xs_kvs_of_flexarray(gc, front), NULL); rc = 0; out: return rc; } int libxl__device_vfb_setdefault(libxl__gc *gc, libxl_device_vfb *vfb) { int rc; libxl_defbool_setdefault(&vfb->vnc.enable, true); if (libxl_defbool_val(vfb->vnc.enable)) { if (!vfb->vnc.listen) { vfb->vnc.listen = strdup("127.0.0.1"); if (!vfb->vnc.listen) return ERROR_NOMEM; } libxl_defbool_setdefault(&vfb->vnc.findunused, true); } else { libxl_defbool_setdefault(&vfb->vnc.findunused, false); } libxl_defbool_setdefault(&vfb->sdl.enable, false); libxl_defbool_setdefault(&vfb->sdl.opengl, false); rc = libxl__resolve_domid(gc, vfb->backend_domname, &vfb->backend_domid); return rc; } static int libxl__device_from_vfb(libxl__gc *gc, uint32_t domid, libxl_device_vfb *vfb, libxl__device *device) { device->backend_devid = vfb->devid; device->backend_domid = vfb->backend_domid; device->backend_kind = LIBXL__DEVICE_KIND_VFB; device->devid = vfb->devid; device->domid = domid; device->kind = LIBXL__DEVICE_KIND_VFB; return 0; } int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int rc; rc = libxl__device_vfb_add(gc, domid, vfb); if (rc) { LOGD(ERROR, domid, "Unable to add vfb device"); goto out; } out: libxl__ao_complete(egc, ao, rc); return AO_INPROGRESS; } int libxl__device_vfb_add(libxl__gc *gc, uint32_t domid, libxl_device_vfb *vfb) { flexarray_t *front; flexarray_t *back; libxl__device device; int rc; rc = libxl__device_vfb_setdefault(gc, vfb); if (rc) goto out; front = flexarray_make(gc, 16, 1); back = flexarray_make(gc, 16, 1); if (vfb->devid == -1) { if ((vfb->devid = libxl__device_nextid(gc, domid, "vfb")) < 0) { rc = ERROR_FAIL; goto out; } } rc = libxl__device_from_vfb(gc, domid, vfb, &device); if (rc != 0) goto out; flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid)); flexarray_append_pair(back, "online", "1"); flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append_pair(back, "vnc", libxl_defbool_val(vfb->vnc.enable) ? "1" : "0"); flexarray_append_pair(back, "vnclisten", vfb->vnc.listen); flexarray_append_pair(back, "vncpasswd", vfb->vnc.passwd); flexarray_append_pair(back, "vncdisplay", GCSPRINTF("%d", vfb->vnc.display)); flexarray_append_pair(back, "vncunused", libxl_defbool_val(vfb->vnc.findunused) ? "1" : "0"); flexarray_append_pair(back, "sdl", libxl_defbool_val(vfb->sdl.enable) ? "1" : "0"); flexarray_append_pair(back, "opengl", libxl_defbool_val(vfb->sdl.opengl) ? "1" : "0"); if (vfb->sdl.xauthority) { flexarray_append_pair(back, "xauthority", vfb->sdl.xauthority); } if (vfb->sdl.display) { flexarray_append_pair(back, "display", vfb->sdl.display); } flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", vfb->backend_domid)); flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising)); libxl__device_generic_add(gc, XBT_NULL, &device, libxl__xs_kvs_of_flexarray(gc, back), libxl__xs_kvs_of_flexarray(gc, front), NULL); rc = 0; out: return rc; } /* The following functions are defined: * libxl_device_vkb_remove * libxl_device_vkb_destroy * libxl_device_vfb_remove * libxl_device_vfb_destroy */ /* channel/console hotunplug is not implemented. There are 2 possibilities: * 1. add support for secondary consoles to xenconsoled * 2. dynamically add/remove qemu chardevs via qmp messages. */ /* vkb */ LIBXL_DEFINE_DEVICE_REMOVE(vkb) /* vfb */ LIBXL_DEFINE_DEVICE_REMOVE(vfb) libxl_xen_console_reader * libxl_xen_console_read_start(libxl_ctx *ctx, int clear) { GC_INIT(ctx); libxl_xen_console_reader *cr; unsigned int size = 16384; cr = libxl__zalloc(NOGC, sizeof(libxl_xen_console_reader)); cr->buffer = libxl__zalloc(NOGC, size); cr->size = size; cr->count = size; cr->clear = clear; cr->incremental = 1; GC_FREE; return cr; } /* return values: *line_r * 1 success, whole line obtained from buffer non-0 * 0 no more lines available right now 0 * negative error code ERROR_* 0 * On success *line_r is updated to point to a nul-terminated * string which is valid until the next call on the same console * reader. The libxl caller may overwrite parts of the string * if it wishes. */ int libxl_xen_console_read_line(libxl_ctx *ctx, libxl_xen_console_reader *cr, char **line_r) { int ret; GC_INIT(ctx); memset(cr->buffer, 0, cr->size); ret = xc_readconsolering(ctx->xch, cr->buffer, &cr->count, cr->clear, cr->incremental, &cr->index); if (ret < 0) { LOGE(ERROR, "reading console ring buffer"); GC_FREE; return ERROR_FAIL; } if (!ret) { if (cr->count) { *line_r = cr->buffer; ret = 1; } else { *line_r = NULL; ret = 0; } } GC_FREE; return ret; } void libxl_xen_console_read_finish(libxl_ctx *ctx, libxl_xen_console_reader *cr) { free(cr->buffer); free(cr); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_disk_l.l0000664000175000017500000002411213256712137015730 0ustar smbsmb/* -*- fundamental -*- */ /* * libxlu_disk_l.l - parser for disk specification strings * * Copyright (C) 2011 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * Parsing the old xm/xend/xl-4.1 disk specs is a tricky problem, * because the target string might in theory contain "," which is the * delimiter we use for stripping off things on the RHS, and ":", * which is the delimiter we use for stripping off things on the LHS. * * In this parser we do not support such target strings in the old * syntax; if the target string has to contain "," or ":" the new * syntax's "target=" should be used. */ %top{ #include "libxl_osdeps.h" /* must come before any other headers */ } %{ #include "libxlu_disk_i.h" #define YY_NO_INPUT /* Some versions of flex have a bug (Fedora bugzilla 612465) which causes * it to fail to declare these functions, which it defines. So declare * them ourselves. Hopefully we won't have to simultaneously support * a flex version which declares these differently somehow. */ int xlu__disk_yyget_column(yyscan_t yyscanner); void xlu__disk_yyset_column(int column_no, yyscan_t yyscanner); /*----- useful macros and functions used in actions ----- * we use macros in the actual rules to keep the actions short * and particularly to avoid repeating boilerplate values such as * DPC->disk, yytext, etc. */ /* Sets an enum, checking it hasn't already been set to a different value */ #define DSET(dpc,member,enumname,str,valname) do{ \ if (dpc->disk->member != LIBXL_DISK_##enumname##_UNKNOWN && \ dpc->disk->member != LIBXL_DISK_##enumname##_##valname) { \ xlu__disk_err(dpc, str, TOSTRING(member) " respecified"); \ } else { \ dpc->disk->member = LIBXL_DISK_##enumname##_##valname; \ } \ }while(0) /* For actions whose patterns contain '=', finds the start of the value */ #define FROMEQUALS (strchr(yytext,'=')+1) /* Chops the delimiter off, modifying yytext and yyleng. */ #define STRIP(delim) do{ \ if (yyleng>0 && yytext[yyleng-1]==(delim)) \ yytext[--yyleng] = 0; \ }while(0) /* Sets a string value, checking it hasn't been set already. */ #define SAVESTRING(what,loc,val) do{ \ savestring(DPC, what " respecified", &DPC->disk->loc, (val)); \ }while(0) static void savestring(DiskParseContext *dpc, const char *what_respecified, char **update, const char *value) { if (*update) { if (**update) { xlu__disk_err(dpc,value,what_respecified); return; } free(*update); /* do not complain about overwriting empty strings */ } *update = strdup(value); } #define DPC dpc /* our convention in lexer helper functions */ /* Sets ->readwrite from the string. This ought to be an enum, perhaps. */ static void setaccess(DiskParseContext *dpc, const char *str) { if (!strcmp(str, "r") || !strcmp(str, "ro")) { dpc->disk->readwrite = 0; } else if (!strcmp(str, "rw") || !strcmp(str, "w") || !strcmp(str,"")) { dpc->disk->readwrite = 1; } else { xlu__disk_err(dpc,str,"unknown value for access"); } } /* Sets ->format from the string. IDL should provide something for this. */ static void setformat(DiskParseContext *dpc, const char *str) { if (!strcmp(str,"")) DSET(dpc,format,FORMAT,str,RAW); else if (!strcmp(str,"raw")) DSET(dpc,format,FORMAT,str,RAW); else if (!strcmp(str,"qcow")) DSET(dpc,format,FORMAT,str,QCOW); else if (!strcmp(str,"qcow2")) DSET(dpc,format,FORMAT,str,QCOW2); else if (!strcmp(str,"vhd")) DSET(dpc,format,FORMAT,str,VHD); else if (!strcmp(str,"empty")) DSET(dpc,format,FORMAT,str,EMPTY); else if (!strcmp(str,"qed")) DSET(dpc,format,FORMAT,str,QED); else xlu__disk_err(dpc,str,"unknown value for format"); } /* Sets ->backend from the string. IDL should provide something for this. */ static void setbackendtype(DiskParseContext *dpc, const char *str) { if ( !strcmp(str,"phy")) DSET(dpc,backend,BACKEND,str,PHY); else if (!strcmp(str,"tap")) DSET(dpc,backend,BACKEND,str,TAP); else if (!strcmp(str,"qdisk")) DSET(dpc,backend,BACKEND,str,QDISK); else xlu__disk_err(dpc,str,"unknown value for backendtype"); } /* Sets ->colo-port from the string. COLO need this. */ static void setcoloport(DiskParseContext *dpc, const char *str) { int port = atoi(str); if (port) { dpc->disk->colo_port = port; } else { xlu__disk_err(dpc,str,"unknown value for colo_port"); } } #define DEPRECATE(usewhatinstead) /* not currently reported */ /* Handles a vdev positional parameter which includes a devtype. */ static int vdev_and_devtype(DiskParseContext *dpc, char *str) { /* returns 1 if it was :, 0 (doing nothing) otherwise */ char *colon = strrchr(str, ':'); if (!colon) return 0; DEPRECATE("use `devtype=...'"); *colon++ = 0; SAVESTRING("vdev", vdev, str); if (!strcmp(colon,"cdrom")) { DPC->disk->is_cdrom = 1; } else if (!strcmp(colon,"disk")) { DPC->disk->is_cdrom = 0; } else { xlu__disk_err(DPC,colon,"unknown deprecated type"); } return 1; } #undef DPC /* needs to be defined differently the actual lexer */ #define DPC ((DiskParseContext*)yyextra) %} %option warn %option nodefault %option batch %option 8bit %option noyywrap %option reentrant %option prefix="xlu__disk_yy" %option nounput %x LEXERR %% /*----- the scanner rules which do the parsing -----*/ [ \t\n]+/([^ \t\n].*)? { /* ignore whitespace before parameters */ } /* ordinary parameters setting enums or strings */ format=[^,]*,? { STRIP(','); setformat(DPC, FROMEQUALS); } cdrom,? { DPC->disk->is_cdrom = 1; } devtype=cdrom,? { DPC->disk->is_cdrom = 1; } devtype=disk,? { DPC->disk->is_cdrom = 0; } devtype=[^,]*,? { xlu__disk_err(DPC,yytext,"unknown value for type"); } access=[^,]*,? { STRIP(','); setaccess(DPC, FROMEQUALS); } backend=[^,]*,? { STRIP(','); SAVESTRING("backend", backend_domname, FROMEQUALS); } backendtype=[^,]*,? { STRIP(','); setbackendtype(DPC,FROMEQUALS); } vdev=[^,]*,? { STRIP(','); SAVESTRING("vdev", vdev, FROMEQUALS); } script=[^,]*,? { STRIP(','); SAVESTRING("script", script, FROMEQUALS); } direct-io-safe,? { DPC->disk->direct_io_safe = 1; } discard,? { libxl_defbool_set(&DPC->disk->discard_enable, true); } no-discard,? { libxl_defbool_set(&DPC->disk->discard_enable, false); } /* Note that the COLO configuration settings should be considered unstable. * They may change incompatibly in future versions of Xen. */ colo,? { libxl_defbool_set(&DPC->disk->colo_enable, true); } no-colo,? { libxl_defbool_set(&DPC->disk->colo_enable, false); } colo-host=[^,]*,? { STRIP(','); SAVESTRING("colo-host", colo_host, FROMEQUALS); } colo-port=[^,]*,? { STRIP(','); setcoloport(DPC, FROMEQUALS); } colo-export=[^,]*,? { STRIP(','); SAVESTRING("colo-export", colo_export, FROMEQUALS); } active-disk=[^,]*,? { STRIP(','); SAVESTRING("active-disk", active_disk, FROMEQUALS); } hidden-disk=[^,]*,? { STRIP(','); SAVESTRING("hidden-disk", hidden_disk, FROMEQUALS); } /* the target magic parameter, eats the rest of the string */ target=.* { STRIP(','); SAVESTRING("target", pdev_path, FROMEQUALS); } /* unknown parameters */ [a-z][-a-z0-9]*=[^,],? { xlu__disk_err(DPC,yytext,"unknown parameter"); } /* deprecated prefixes */ /* the "/.*" in these patterns ensures that they count as if they * matched the whole string, so these patterns take precedence */ (raw|qcow2?|vhd):/.* { STRIP(':'); DPC->had_depr_prefix=1; DEPRECATE("use `[format=]...,'"); setformat(DPC, yytext); } (iscsi|e?nbd|drbd):/.* { char *newscript; STRIP(':'); DPC->had_depr_prefix=1; DEPRECATE("use `script=...'"); if (asprintf(&newscript, "block-%s", yytext) < 0) { xlu__disk_err(DPC,yytext,"unable to format script"); return 0; } savestring(DPC, "script respecified", &DPC->disk->script, newscript); free(newscript); } tapdisk:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); } tap2?:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); } aio:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); } ioemu:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); } file:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); } phy:/.* { DPC->had_depr_prefix=1; DEPRECATE(0); } [a-z][a-z0-9]*:/([^a-z0-9].*)? { xlu__disk_err(DPC,yytext,"unknown deprecated disk prefix"); return 0; } /* positional parameters */ [^=,]*,|[^=,]+,? { STRIP(','); if (DPC->err) { /* previous errors may just lead to subsequent ones */ } else if (!DPC->disk->pdev_path) { SAVESTRING("target", pdev_path, yytext); } else if (!DPC->had_depr_prefix && DPC->disk->format == LIBXL_DISK_FORMAT_UNKNOWN) { if (!*DPC->disk->pdev_path && vdev_and_devtype(DPC,yytext)) { DPC->disk->format = LIBXL_DISK_FORMAT_EMPTY; } else { setformat(DPC,yytext); } } else if (!DPC->disk->vdev) { if (!vdev_and_devtype(DPC,yytext)) SAVESTRING("vdev", vdev, yytext); } else if (!DPC->access_set) { DPC->access_set = 1; setaccess(DPC,yytext); } else { xlu__disk_err(DPC,yytext,"too many positional parameters"); return 0; /* don't print any more errors */ } } . { BEGIN(LEXERR); yymore(); } .* { xlu__disk_err(DPC,yytext,"bad disk syntax"); return 0; } xen-4.9.2/tools/libxl/libxl_aoutils.c0000664000175000017500000004663513256712137015763 0ustar smbsmb/* * Copyright (C) 2010 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" /*----- xswait -----*/ static libxl__ev_xswatch_callback xswait_xswatch_callback; static libxl__ev_time_callback xswait_timeout_callback; static void xswait_report_error(libxl__egc*, libxl__xswait_state*, int rc); void libxl__xswait_init(libxl__xswait_state *xswa) { libxl__ev_time_init(&xswa->time_ev); libxl__ev_xswatch_init(&xswa->watch_ev); } void libxl__xswait_stop(libxl__gc *gc, libxl__xswait_state *xswa) { libxl__ev_time_deregister(gc, &xswa->time_ev); libxl__ev_xswatch_deregister(gc, &xswa->watch_ev); } bool libxl__xswait_inuse(const libxl__xswait_state *xswa) { bool time_inuse = libxl__ev_time_isregistered(&xswa->time_ev); bool watch_inuse = libxl__ev_xswatch_isregistered(&xswa->watch_ev); assert(time_inuse == watch_inuse); return time_inuse; } int libxl__xswait_start(libxl__gc *gc, libxl__xswait_state *xswa) { int rc; rc = libxl__ev_time_register_rel(xswa->ao, &xswa->time_ev, xswait_timeout_callback, xswa->timeout_ms); if (rc) goto err; rc = libxl__ev_xswatch_register(gc, &xswa->watch_ev, xswait_xswatch_callback, xswa->path); if (rc) goto err; return 0; err: libxl__xswait_stop(gc, xswa); return rc; } void xswait_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path) { EGC_GC; libxl__xswait_state *xswa = CONTAINER_OF(xsw, *xswa, watch_ev); int rc; const char *data; if (xswa->path[0] == '@') { data = 0; } else { rc = libxl__xs_read_checked(gc, XBT_NULL, xswa->path, &data); if (rc) { xswait_report_error(egc, xswa, rc); return; } } xswa->callback(egc, xswa, 0, data); } void xswait_timeout_callback(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc) { EGC_GC; libxl__xswait_state *xswa = CONTAINER_OF(ev, *xswa, time_ev); LOG(DEBUG, "%s: xswait timeout (path=%s)", xswa->what, xswa->path); xswait_report_error(egc, xswa, rc); } static void xswait_report_error(libxl__egc *egc, libxl__xswait_state *xswa, int rc) { EGC_GC; libxl__xswait_stop(gc, xswa); xswa->callback(egc, xswa, rc, 0); } /*----- data copier -----*/ void libxl__datacopier_init(libxl__datacopier_state *dc) { assert(dc->ao); libxl__ao_abortable_init(&dc->abrt); libxl__ev_fd_init(&dc->toread); libxl__ev_fd_init(&dc->towrite); LIBXL_TAILQ_INIT(&dc->bufs); } void libxl__datacopier_kill(libxl__datacopier_state *dc) { STATE_AO_GC(dc->ao); libxl__datacopier_buf *buf, *tbuf; libxl__ao_abortable_deregister(&dc->abrt); libxl__ev_fd_deregister(gc, &dc->toread); libxl__ev_fd_deregister(gc, &dc->towrite); LIBXL_TAILQ_FOREACH_SAFE(buf, &dc->bufs, entry, tbuf) free(buf); LIBXL_TAILQ_INIT(&dc->bufs); } static void datacopier_callback(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__datacopier_kill(dc); dc->callback(egc, dc, rc, onwrite, errnoval); } static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents); static void datacopier_check_state(libxl__egc *egc, libxl__datacopier_state *dc) { STATE_AO_GC(dc->ao); int rc; if (dc->used && !dc->readbuf) { if (!libxl__ev_fd_isregistered(&dc->towrite)) { rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable, dc->writefd, POLLOUT); if (rc) { LOG(ERROR, "unable to establish write event on %s" " during copy of %s", dc->writewhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO); return; } } } else if (!libxl__ev_fd_isregistered(&dc->toread) || dc->bytes_to_read == 0) { /* we have had eof */ datacopier_callback(egc, dc, 0, 0, 0); return; } else { /* nothing buffered, but still reading */ libxl__ev_fd_deregister(gc, &dc->towrite); } } void libxl__datacopier_prefixdata(libxl__egc *egc, libxl__datacopier_state *dc, const void *data, size_t len) { EGC_GC; libxl__datacopier_buf *buf; const uint8_t *ptr; /* * It is safe for this to be called immediately after _start, as * is documented in the public comment. _start's caller must have * the ctx locked, so other threads don't get to mess with the * contents, and the fd events cannot happen reentrantly. So we * are guaranteed to beat the first data from the read fd. */ assert(len < dc->maxsz - dc->used); for (ptr = data; len; len -= buf->used, ptr += buf->used) { buf = libxl__malloc(NOGC, sizeof(*buf)); buf->used = min(len, sizeof(buf->buf)); memcpy(buf->buf, ptr, buf->used); dc->used += buf->used; LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry); } } static int datacopier_pollhup_handled(libxl__egc *egc, libxl__datacopier_state *dc, int fd, short revents, int onwrite) { STATE_AO_GC(dc->ao); if (dc->callback_pollhup && (revents & POLLHUP)) { LOG(DEBUG, "received POLLHUP on fd %d: %s during copy of %s", fd, onwrite ? dc->writewhat : dc->readwhat, dc->copywhat); libxl__datacopier_kill(dc); dc->callback_pollhup(egc, dc, ERROR_FAIL, onwrite, -1); return 1; } return 0; } static void datacopier_abort(libxl__egc *egc, libxl__ao_abortable *abrt, int rc) { libxl__datacopier_state *dc = CONTAINER_OF(abrt, *dc, abrt); STATE_AO_GC(dc->ao); datacopier_callback(egc, dc, rc, -1, 0); } static void datacopier_readable(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, toread); STATE_AO_GC(dc->ao); if (datacopier_pollhup_handled(egc, dc, fd, revents, 0)) return; if (revents & ~(POLLIN|POLLHUP)) { LOG(ERROR, "unexpected poll event 0x%x on fd %d (expected POLLIN " "and/or POLLHUP) reading %s during copy of %s", revents, fd, dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO); return; } assert(revents & (POLLIN|POLLHUP)); for (;;) { libxl__datacopier_buf *buf = NULL; int r; if (dc->readbuf) { r = read(ev->fd, dc->readbuf + dc->used, dc->bytes_to_read); } else { while (dc->used >= dc->maxsz) { libxl__datacopier_buf *rm = LIBXL_TAILQ_FIRST(&dc->bufs); dc->used -= rm->used; assert(dc->used >= 0); LIBXL_TAILQ_REMOVE(&dc->bufs, rm, entry); free(rm); } buf = LIBXL_TAILQ_LAST(&dc->bufs, libxl__datacopier_bufs); if (!buf || buf->used >= sizeof(buf->buf)) { buf = libxl__malloc(NOGC, sizeof(*buf)); buf->used = 0; LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry); } r = read(ev->fd, buf->buf + buf->used, min_t(size_t, sizeof(buf->buf) - buf->used, (dc->bytes_to_read == -1) ? SIZE_MAX : dc->bytes_to_read)); } if (r < 0) { if (errno == EINTR) continue; assert(errno); if (errno == EWOULDBLOCK) { if (revents & POLLHUP) { LOG(ERROR, "poll reported HUP but fd read gave EWOULDBLOCK" " on %s during copy of %s", dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, -1, 0); return; } break; } LOGE(ERROR, "error reading %s during copy of %s", dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, 0, errno); return; } if (r == 0) { if (dc->callback_pollhup) { /* It might be that this "eof" is actually a HUP. If * the caller cares about the difference, * double-check using poll(2). */ struct pollfd hupchk; hupchk.fd = ev->fd; hupchk.events = POLLIN; hupchk.revents = 0; r = poll(&hupchk, 1, 0); if (r < 0) LIBXL__EVENT_DISASTER(egc, "unexpected failure polling fd for datacopier eof hup check", errno, 0); if (datacopier_pollhup_handled(egc, dc, fd, hupchk.revents, 0)) return; } libxl__ev_fd_deregister(gc, &dc->toread); break; } if (dc->log) { int wrote = fwrite(buf->buf + buf->used, 1, r, dc->log); if (wrote != r) { assert(ferror(dc->log)); assert(errno); LOGE(ERROR, "error logging %s", dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, 0, errno); return; } } if (!dc->readbuf) { buf->used += r; assert(buf->used <= sizeof(buf->buf)); } dc->used += r; if (dc->bytes_to_read > 0) dc->bytes_to_read -= r; if (dc->bytes_to_read == 0) break; } datacopier_check_state(egc, dc); } static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, towrite); STATE_AO_GC(dc->ao); if (datacopier_pollhup_handled(egc, dc, fd, revents, 1)) return; if (revents & ~POLLOUT) { LOG(ERROR, "unexpected poll event 0x%x on fd %d (should be POLLOUT)" " writing %s during copy of %s", revents, fd, dc->writewhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO); return; } assert(revents & POLLOUT); for (;;) { libxl__datacopier_buf *buf = LIBXL_TAILQ_FIRST(&dc->bufs); if (!buf) break; if (!buf->used) { LIBXL_TAILQ_REMOVE(&dc->bufs, buf, entry); free(buf); continue; } int r = write(ev->fd, buf->buf, buf->used); if (r < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) break; assert(errno); LOGE(ERROR, "error writing to %s during copy of %s", dc->writewhat, dc->copywhat); datacopier_callback(egc, dc, ERROR_FAIL, 1, errno); return; } assert(r > 0); assert(r <= buf->used); buf->used -= r; dc->used -= r; assert(dc->used >= 0); memmove(buf->buf, buf->buf+r, buf->used); } datacopier_check_state(egc, dc); } int libxl__datacopier_start(libxl__datacopier_state *dc) { int rc; STATE_AO_GC(dc->ao); libxl__datacopier_init(dc); assert(dc->readfd >= 0 || dc->writefd >= 0); assert(!(dc->readbuf && dc->bytes_to_read == -1)); dc->abrt.ao = ao; dc->abrt.callback = datacopier_abort; rc = libxl__ao_abortable_register(&dc->abrt); if (rc) goto out; if (dc->readfd >= 0) { rc = libxl__ev_fd_register(gc, &dc->toread, datacopier_readable, dc->readfd, POLLIN); if (rc) goto out; } if (dc->writefd >= 0) { rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable, dc->writefd, POLLOUT); if (rc) goto out; } return 0; out: libxl__datacopier_kill(dc); return rc; } /*----- openpty -----*/ /* implementation */ static void openpty_cleanup(libxl__openpty_state *op) { int i; for (i=0; icount; i++) { libxl__openpty_result *res = &op->results[i]; libxl__carefd_close(res->master); res->master = 0; libxl__carefd_close(res->slave); res->slave = 0; } } static void openpty_exited(libxl__egc *egc, libxl__ev_child *child, pid_t pid, int status) { libxl__openpty_state *op = CONTAINER_OF(child, *op, child); STATE_AO_GC(op->ao); if (status) { /* Perhaps the child gave us the fds and then exited nonzero. * Well that would be odd but we don't really care. */ libxl_report_child_exitstatus(CTX, op->rc ? LIBXL__LOG_ERROR : LIBXL__LOG_WARNING, "openpty child", pid, status); } if (op->rc) openpty_cleanup(op); op->callback(egc, op); } int libxl__openptys(libxl__openpty_state *op, struct termios *termp, struct winsize *winp) { /* * This is completely crazy. openpty calls grantpt which the spec * says may fork, and may not be called with a SIGCHLD handler. * Now our application may have a SIGCHLD handler so that's bad. * We could perhaps block it but we'd need to block it on all * threads. This is just Too Hard. * * So instead, we run openpty in a child process. That child * process then of course has only our own thread and our own * signal handlers. We pass the fds back. * * Since our only current caller actually wants two ptys, we * support calling openpty multiple times for a single fork. */ STATE_AO_GC(op->ao); int count = op->count; int r, i, rc, sockets[2], ptyfds[count][2]; libxl__carefd *for_child = 0; pid_t pid = -1; for (i=0; iresults[i]; assert(!res->master); assert(!res->slave); } sockets[0] = sockets[1] = -1; /* 0 is for us, 1 for our child */ libxl__carefd_begin(); r = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); if (r) { sockets[0] = sockets[1] = -1; } for_child = libxl__carefd_opened(CTX, sockets[1]); if (r) { LOGE(ERROR,"socketpair failed"); rc = ERROR_FAIL; goto out; } pid = libxl__ev_child_fork(gc, &op->child, openpty_exited); if (pid == -1) { rc = ERROR_FAIL; goto out; } if (!pid) { /* child */ close(sockets[0]); signal(SIGCHLD, SIG_DFL); for (i=0; iresults[i]; res->master = libxl__carefd_record(CTX, ptyfds[i][0]); res->slave = libxl__carefd_record(CTX, ptyfds[i][1]); } } /* now the pty fds are in the carefds, if they were ever open */ libxl__carefd_unlock(); if (rc) goto out; rc = 0; out: if (sockets[0] >= 0) close(sockets[0]); libxl__carefd_close(for_child); if (libxl__ev_child_inuse(&op->child)) { op->rc = rc; /* we will get a callback when the child dies */ return 0; } assert(rc); openpty_cleanup(op); return rc; } /*----- async exec -----*/ static void async_exec_timeout(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc) { libxl__async_exec_state *aes = CONTAINER_OF(ev, *aes, time); STATE_AO_GC(aes->ao); if (!aes->rc) aes->rc = rc; libxl__ev_time_deregister(gc, &aes->time); assert(libxl__ev_child_inuse(&aes->child)); LOG(ERROR, "killing execution of %s because of timeout", aes->what); if (kill(aes->child.pid, SIGKILL)) { LOGEV(ERROR, errno, "unable to kill %s [%ld]", aes->what, (unsigned long)aes->child.pid); } return; } static void async_exec_done(libxl__egc *egc, libxl__ev_child *child, pid_t pid, int status) { libxl__async_exec_state *aes = CONTAINER_OF(child, *aes, child); STATE_AO_GC(aes->ao); libxl__ev_time_deregister(gc, &aes->time); if (status) { if (!aes->rc) libxl_report_child_exitstatus(CTX, LIBXL__LOG_ERROR, aes->what, pid, status); } aes->callback(egc, aes, aes->rc, status); } void libxl__async_exec_init(libxl__async_exec_state *aes) { libxl__ev_time_init(&aes->time); libxl__ev_child_init(&aes->child); } int libxl__async_exec_start(libxl__async_exec_state *aes) { pid_t pid; /* Convenience aliases */ libxl__ao *ao = aes->ao; AO_GC; libxl__ev_child *const child = &aes->child; char ** const args = aes->args; aes->rc = 0; /* Set execution timeout */ if (libxl__ev_time_register_rel(ao, &aes->time, async_exec_timeout, aes->timeout_ms)) { LOG(ERROR, "unable to register timeout for executing: %s", aes->what); goto out; } LOG(DEBUG, "forking to execute: %s ", aes->what); /* Fork and exec */ pid = libxl__ev_child_fork(gc, child, async_exec_done); if (pid == -1) { LOG(ERROR, "unable to fork"); goto out; } if (!pid) { /* child */ libxl__exec(gc, aes->stdfds[0], aes->stdfds[1], aes->stdfds[2], args[0], args, aes->env); } return 0; out: return ERROR_FAIL; } bool libxl__async_exec_inuse(const libxl__async_exec_state *aes) { bool time_inuse = libxl__ev_time_isregistered(&aes->time); bool child_inuse = libxl__ev_child_inuse(&aes->child); assert(time_inuse == child_inuse); return child_inuse; } void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what) { int r = kill(pid, sig); if (r) LOGE(WARN, "failed to kill() %s [%lu] (signal %d)", what, (unsigned long)pid, sig); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_sr_stream_format.h0000664000175000017500000000324113256712137017641 0ustar smbsmb#ifndef LIBXL__SR_STREAM_FORMAT_H #define LIBXL__SR_STREAM_FORMAT_H /* * C structures for the Migration v2 stream format. * See docs/specs/libxl-migration-stream.pandoc */ #include typedef struct libxl__sr_hdr { uint64_t ident; uint32_t version; uint32_t options; } libxl__sr_hdr; #define RESTORE_STREAM_IDENT 0x4c6962786c466d74UL #define RESTORE_STREAM_VERSION 0x00000002U #define RESTORE_OPT_BIG_ENDIAN (1u << 0) #define RESTORE_OPT_LEGACY (1u << 1) typedef struct libxl__sr_rec_hdr { uint32_t type; uint32_t length; } libxl__sr_rec_hdr; /* All records must be aligned up to an 8 octet boundary */ #define REC_ALIGN_ORDER 3U #define REC_TYPE_END 0x00000000U #define REC_TYPE_LIBXC_CONTEXT 0x00000001U #define REC_TYPE_EMULATOR_XENSTORE_DATA 0x00000002U #define REC_TYPE_EMULATOR_CONTEXT 0x00000003U #define REC_TYPE_CHECKPOINT_END 0x00000004U #define REC_TYPE_CHECKPOINT_STATE 0x00000005U typedef struct libxl__sr_emulator_hdr { uint32_t id; uint32_t index; } libxl__sr_emulator_hdr; #define EMULATOR_UNKNOWN 0x00000000U #define EMULATOR_QEMU_TRADITIONAL 0x00000001U #define EMULATOR_QEMU_UPSTREAM 0x00000002U typedef struct libxl_sr_checkpoint_state { uint32_t id; } libxl_sr_checkpoint_state; #define CHECKPOINT_NEW 0x00000000U #define CHECKPOINT_SVM_SUSPENDED 0x00000001U #define CHECKPOINT_SVM_READY 0x00000002U #define CHECKPOINT_SVM_RESUMED 0x00000003U #endif /* LIBXL__SR_STREAM_FORMAT_H */ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_cfg_y.y0000664000175000017500000000410613256712137015570 0ustar smbsmb/* -*- fundamental -*- */ /* * libxlu_cfg_l.y - xl configuration file parsing: parser * * Copyright (C) 2010 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ %{ #define ctx_scanner ctx->scanner #include "libxlu_cfg_i.h" #include "libxlu_cfg_l.h" %} %union { char *string; XLU_ConfigValue *value; } %locations %pure-parser %defines %error-verbose %name-prefix "xlu__cfg_yy" %parse-param { CfgParseContext *ctx } %lex-param { ctx_scanner } %token IDENT STRING NUMBER NEWLINE %type atom %destructor { free($$); } atom IDENT STRING NUMBER %type value valuelist values %destructor { xlu__cfg_value_free($$); } value valuelist values %% file: stmts | stmts assignment stmts: /* empty */ | stmts stmt stmt: assignment endstmt | endstmt | error NEWLINE assignment: IDENT '=' value { xlu__cfg_set_store(ctx,$1,$3,@3.first_line); } endstmt: NEWLINE | ';' value: atom { $$= xlu__cfg_string_mk(ctx,$1,&@1); } | '[' nlok valuelist ']' { $$= $3; } atom: STRING { $$= $1; } | NUMBER { $$= $1; } valuelist: /* empty */ { $$= xlu__cfg_list_mk(ctx,NULL,&yylloc); } | values { $$= $1; } | values ',' nlok { $$= $1; } values: value nlok { $$= xlu__cfg_list_mk(ctx,$1,&@1); } | values ',' nlok value nlok { xlu__cfg_list_append(ctx,$1,$4); $$= $1; } nlok: /* nothing */ | nlok NEWLINE xen-4.9.2/tools/libxl/libxl_colo_qdisk.c0000664000175000017500000001507513256712137016424 0ustar smbsmb/* * Copyright (C) 2016 FUJITSU LIMITED * Author: Wen Congyang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" /* ========== init() and cleanup() ========== */ int init_subkind_qdisk(libxl__checkpoint_devices_state *cds) { /* * We don't know if we use qemu block replication, so * we cannot start block replication here. */ return 0; } void cleanup_subkind_qdisk(libxl__checkpoint_devices_state *cds) { } /* ========== setup() and teardown() ========== */ static void colo_qdisk_setup(libxl__egc *egc, libxl__checkpoint_device *dev, bool primary) { const libxl_device_disk *disk = dev->backend_dev; int ret, rc = 0; libxl__colo_qdisk *colo_qdisk = NULL; char port[32]; /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = dev->cds; const char *host = disk->colo_host; const char *export_name = disk->colo_export; const int domid = cds->domid; STATE_AO_GC(dev->cds->ao); if (disk->backend != LIBXL_DISK_BACKEND_QDISK || !libxl_defbool_val(disk->colo_enable) || !host || !export_name || (disk->colo_port <= 0) || !disk->active_disk || !disk->hidden_disk) { rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH; goto out; } dev->matched = true; GCNEW(colo_qdisk); dev->concrete_data = colo_qdisk; if (primary) { libxl__colo_save_state *css = cds->concrete_data; css->qdisk_used = true; /* NBD server is not ready, so we cannot start block replication now */ goto out; } else { libxl__colo_restore_state *crs = cds->concrete_data; sprintf(port, "%d", disk->colo_port); if (!crs->qdisk_used) { /* start nbd server */ ret = libxl__qmp_nbd_server_start(gc, domid, host, port); if (ret) { rc = ERROR_FAIL; goto out; } crs->host = host; crs->port = port; } else { if (strcmp(crs->host, host) || strcmp(crs->port, port)) { LOGD(ERROR, domid, "The host and port of all disks must be the same"); rc = ERROR_FAIL; goto out; } } crs->qdisk_used = true; ret = libxl__qmp_nbd_server_add(gc, domid, export_name); if (ret) rc = ERROR_FAIL; colo_qdisk->setuped = true; } out: dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } static void colo_qdisk_teardown(libxl__egc *egc, libxl__checkpoint_device *dev, bool primary) { int ret, rc = 0; const libxl__colo_qdisk *colo_qdisk = dev->concrete_data; const libxl_device_disk *disk = dev->backend_dev; /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = dev->cds; const int domid = cds->domid; const char *export_name = disk->colo_export; EGC_GC; if (primary) { if (!colo_qdisk->setuped) goto out; /* * There is no way to get the child name, but we know it is children.1 */ ret = libxl__qmp_x_blockdev_change(gc, domid, export_name, "children.1", NULL); if (ret) rc = ERROR_FAIL; } else { libxl__colo_restore_state *crs = cds->concrete_data; if (crs->qdisk_used) { ret = libxl__qmp_nbd_server_stop(gc, domid); if (ret) rc = ERROR_FAIL; } } out: dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } /* ========== checkpointing APIs ========== */ static void colo_qdisk_save_preresume(libxl__egc *egc, libxl__checkpoint_device *dev) { libxl__colo_qdisk *colo_qdisk = dev->concrete_data; const libxl_device_disk *disk = dev->backend_dev; int ret, rc = 0; char *node = NULL; char *cmd = NULL; /* Convenience aliases */ const int domid = dev->cds->domid; const char *host = disk->colo_host; int port = disk->colo_port; const char *export_name = disk->colo_export; EGC_GC; if (colo_qdisk->setuped) goto out; /* qmp command doesn't support the driver "nbd" */ node = GCSPRINTF("colo_node%d", libxl__device_disk_dev_number(disk->vdev, NULL, NULL)); cmd = GCSPRINTF("drive_add -n buddy driver=replication,mode=primary," "file.driver=nbd,file.host=%s,file.port=%d," "file.export=%s,node-name=%s", host, port, export_name, node); ret = libxl__qmp_hmp(gc, domid, cmd, NULL); if (ret) rc = ERROR_FAIL; ret = libxl__qmp_x_blockdev_change(gc, domid, export_name, NULL, node); if (ret) rc = ERROR_FAIL; colo_qdisk->setuped = true; out: dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } /* ======== primary ======== */ static void colo_qdisk_save_setup(libxl__egc *egc, libxl__checkpoint_device *dev) { colo_qdisk_setup(egc, dev, true); } static void colo_qdisk_save_teardown(libxl__egc *egc, libxl__checkpoint_device *dev) { colo_qdisk_teardown(egc, dev, true); } const libxl__checkpoint_device_instance_ops colo_save_device_qdisk = { .kind = LIBXL__DEVICE_KIND_VBD, .setup = colo_qdisk_save_setup, .teardown = colo_qdisk_save_teardown, .preresume = colo_qdisk_save_preresume, }; /* ======== secondary ======== */ static void colo_qdisk_restore_setup(libxl__egc *egc, libxl__checkpoint_device *dev) { colo_qdisk_setup(egc, dev, false); } static void colo_qdisk_restore_teardown(libxl__egc *egc, libxl__checkpoint_device *dev) { colo_qdisk_teardown(egc, dev, false); } const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk = { .kind = LIBXL__DEVICE_KIND_VBD, .setup = colo_qdisk_restore_setup, .teardown = colo_qdisk_restore_teardown, }; xen-4.9.2/tools/libxl/libxl_test_fdevent.c0000664000175000017500000000352713256712137016766 0ustar smbsmb/* * fdevent test helpr for the libxl event system */ #include "libxl_internal.h" #include "libxl_test_fdevent.h" typedef struct { libxl__ao *ao; libxl__ev_fd fd; libxl__ao_abortable abrt; } libxl__test_fdevent; static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe, int rc); static void tfe_init(libxl__test_fdevent *tfe, libxl__ao *ao) { tfe->ao = ao; libxl__ev_fd_init(&tfe->fd); libxl__ao_abortable_init(&tfe->abrt); } static void tfe_cleanup(libxl__gc *gc, libxl__test_fdevent *tfe) { libxl__ev_fd_deregister(gc, &tfe->fd); libxl__ao_abortable_deregister(&tfe->abrt); } static void tfe_fd_cb(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { libxl__test_fdevent *tfe = CONTAINER_OF(ev,*tfe,fd); STATE_AO_GC(tfe->ao); fdevent_complete(egc, tfe, 0); } static void tfe_abrt_cb(libxl__egc *egc, libxl__ao_abortable *abrt, int rc) { libxl__test_fdevent *tfe = CONTAINER_OF(abrt,*tfe,abrt); STATE_AO_GC(tfe->ao); fdevent_complete(egc, tfe, rc); } static void fdevent_complete(libxl__egc *egc, libxl__test_fdevent *tfe, int rc) { STATE_AO_GC(tfe->ao); tfe_cleanup(gc, tfe); libxl__ao_complete(egc, ao, rc); } int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events, libxl_asyncop_how *ao_how) { int rc; libxl__test_fdevent *tfe; AO_CREATE(ctx, 0, ao_how); GCNEW(tfe); tfe_init(tfe, ao); rc = libxl__ev_fd_register(gc, &tfe->fd, tfe_fd_cb, fd, events); if (rc) goto out; tfe->abrt.ao = ao; tfe->abrt.callback = tfe_abrt_cb; rc = libxl__ao_abortable_register(&tfe->abrt); if (rc) goto out; return AO_INPROGRESS; out: tfe_cleanup(gc, tfe); return AO_CREATE_FAIL(rc); } xen-4.9.2/tools/libxl/libxl_types.idl0000664000175000017500000010416413256712137015765 0ustar smbsmb# -*- python -*- # # Builtin libxl types # namespace("libxl_") libxl_defbool = Builtin("defbool", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, copy_fn=None, check_default_fn="libxl__defbool_is_default") libxl_domid = Builtin("domid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__uint32_parse_json", json_parse_type = "JSON_INTEGER", autogenerate_json = False, copy_fn=None) libxl_devid = Builtin("devid", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__int_parse_json", json_parse_type = "JSON_INTEGER", autogenerate_json = False, signed = True, init_val="-1", copy_fn=None) libxl_uuid = Builtin("uuid", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl_uuid_is_nil", copy_fn="libxl_uuid_copy") libxl_mac = Builtin("mac", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, check_default_fn="libxl__mac_is_default", copy_fn="libxl_mac_copy") libxl_bitmap = Builtin("bitmap", json_parse_type="JSON_ARRAY", dispose_fn="libxl_bitmap_dispose", passby=PASS_BY_REFERENCE, check_default_fn="libxl_bitmap_is_empty", copy_fn="libxl_bitmap_copy_alloc") libxl_cpuid_policy_list = Builtin("cpuid_policy_list", dispose_fn="libxl_cpuid_dispose", passby=PASS_BY_REFERENCE, json_parse_type="JSON_ARRAY", check_default_fn="libxl__cpuid_policy_is_empty", copy_fn="libxl_cpuid_policy_list_copy") libxl_string_list = Builtin("string_list", dispose_fn="libxl_string_list_dispose", passby=PASS_BY_REFERENCE, json_parse_type="JSON_ARRAY", check_default_fn="libxl__string_list_is_empty", copy_fn="libxl_string_list_copy") libxl_key_value_list = Builtin("key_value_list", dispose_fn="libxl_key_value_list_dispose", passby=PASS_BY_REFERENCE, json_parse_type="JSON_MAP", check_default_fn="libxl__key_value_list_is_empty", copy_fn="libxl_key_value_list_copy") libxl_hwcap = Builtin("hwcap", passby=PASS_BY_REFERENCE, json_parse_type="JSON_ARRAY", check_default_fn="libxl__hwcap_is_default", copy_fn="libxl_hwcap_copy") libxl_ms_vm_genid = Builtin("ms_vm_genid", passby=PASS_BY_REFERENCE, check_default_fn="libxl_ms_vm_genid_is_zero", copy_fn="libxl_ms_vm_genid_copy") # # Specific integer types # MemKB = UInt(64, init_val = "LIBXL_MEMKB_DEFAULT", json_gen_fn = "libxl__uint64_gen_json") # # Constants / Enumerations # libxl_error = Enumeration("error", [ (-1, "NONSPECIFIC"), (-2, "VERSION"), (-3, "FAIL"), (-4, "NI"), (-5, "NOMEM"), (-6, "INVAL"), (-7, "BADFAIL"), (-8, "GUEST_TIMEDOUT"), (-9, "TIMEDOUT"), (-10, "NOPARAVIRT"), (-11, "NOT_READY"), (-12, "OSEVENT_REG_FAIL"), (-13, "BUFFERFULL"), (-14, "UNKNOWN_CHILD"), (-15, "LOCK_FAIL"), (-16, "JSON_CONFIG_EMPTY"), (-17, "DEVICE_EXISTS"), (-18, "CHECKPOINT_DEVOPS_DOES_NOT_MATCH"), (-19, "CHECKPOINT_DEVICE_NOT_SUPPORTED"), (-20, "VNUMA_CONFIG_INVALID"), (-21, "DOMAIN_NOTFOUND"), (-22, "ABORTED"), (-23, "NOTFOUND"), (-24, "DOMAIN_DESTROYED"), # Target domain ceased to exist during op (-25, "FEATURE_REMOVED"), # For functionality that has been removed ], value_namespace = "") libxl_domain_type = Enumeration("domain_type", [ (-1, "INVALID"), (1, "HVM"), (2, "PV"), ], init_val = "LIBXL_DOMAIN_TYPE_INVALID") libxl_rdm_reserve_strategy = Enumeration("rdm_reserve_strategy", [ (0, "ignore"), (1, "host"), ]) libxl_rdm_reserve_policy = Enumeration("rdm_reserve_policy", [ (-1, "invalid"), (0, "strict"), (1, "relaxed"), ], init_val = "LIBXL_RDM_RESERVE_POLICY_INVALID") libxl_channel_connection = Enumeration("channel_connection", [ (0, "UNKNOWN"), (1, "PTY"), (2, "SOCKET"), # a listening Unix domain socket ]) libxl_device_model_version = Enumeration("device_model_version", [ (0, "UNKNOWN"), (1, "QEMU_XEN_TRADITIONAL"), # Historical qemu-xen device model (qemu-dm) (2, "QEMU_XEN"), # Upstream based qemu-xen device model (3, "NONE"), # No device model ]) libxl_console_type = Enumeration("console_type", [ (0, "UNKNOWN"), (1, "SERIAL"), (2, "PV"), ]) libxl_disk_format = Enumeration("disk_format", [ (0, "UNKNOWN"), (1, "QCOW"), (2, "QCOW2"), (3, "VHD"), (4, "RAW"), (5, "EMPTY"), (6, "QED"), ]) libxl_disk_backend = Enumeration("disk_backend", [ (0, "UNKNOWN"), (1, "PHY"), (2, "TAP"), (3, "QDISK"), ]) libxl_nic_type = Enumeration("nic_type", [ (0, "UNKNOWN"), (1, "VIF_IOEMU"), (2, "VIF"), ]) libxl_action_on_shutdown = Enumeration("action_on_shutdown", [ (1, "DESTROY"), (2, "RESTART"), (3, "RESTART_RENAME"), (4, "PRESERVE"), (5, "COREDUMP_DESTROY"), (6, "COREDUMP_RESTART"), (7, "SOFT_RESET"), ], init_val = "LIBXL_ACTION_ON_SHUTDOWN_DESTROY") libxl_trigger = Enumeration("trigger", [ (0, "UNKNOWN"), (1, "POWER"), (2, "SLEEP"), (3, "NMI"), (4, "INIT"), (5, "RESET"), (6, "S3RESUME"), ]) libxl_tsc_mode = Enumeration("tsc_mode", [ (0, "default"), (1, "always_emulate"), (2, "native"), (3, "native_paravirt"), ]) libxl_gfx_passthru_kind = Enumeration("gfx_passthru_kind", [ (0, "default"), (1, "igd"), ]) # Consistent with the values defined for HVM_PARAM_TIMER_MODE. libxl_timer_mode = Enumeration("timer_mode", [ (-1, "unknown"), (0, "delay_for_missed_ticks"), (1, "no_delay_for_missed_ticks"), (2, "no_missed_ticks_pending"), (3, "one_missed_tick_pending"), ], init_val = "LIBXL_TIMER_MODE_DEFAULT") libxl_bios_type = Enumeration("bios_type", [ (0, "unknown"), (1, "rombios"), (2, "seabios"), (3, "ovmf"), ]) # Consistent with values defined in domctl.h # Except unknown which we have made up libxl_scheduler = Enumeration("scheduler", [ (0, "unknown"), (4, "sedf"), (5, "credit"), (6, "credit2"), (7, "arinc653"), (8, "rtds"), (9, "null"), ]) # Consistent with SHUTDOWN_* in sched.h (apart from UNKNOWN) libxl_shutdown_reason = Enumeration("shutdown_reason", [ (-1, "unknown"), (0, "poweroff"), (1, "reboot"), (2, "suspend"), (3, "crash"), (4, "watchdog"), (5, "soft_reset"), ], init_val = "LIBXL_SHUTDOWN_REASON_UNKNOWN") libxl_vga_interface_type = Enumeration("vga_interface_type", [ (0, "UNKNOWN"), (1, "CIRRUS"), (2, "STD"), (3, "NONE"), (4, "QXL"), ], init_val = "LIBXL_VGA_INTERFACE_TYPE_UNKNOWN") libxl_vendor_device = Enumeration("vendor_device", [ (0, "NONE"), (1, "XENSERVER"), ]) libxl_viridian_enlightenment = Enumeration("viridian_enlightenment", [ (0, "base"), (1, "freq"), (2, "time_ref_count"), (3, "reference_tsc"), (4, "hcall_remote_tlb_flush"), (5, "apic_assist"), (6, "crash_ctl"), ]) libxl_hdtype = Enumeration("hdtype", [ (1, "IDE"), (2, "AHCI"), ], init_val = "LIBXL_HDTYPE_IDE") # Consistent with the values defined for migration_stream. libxl_checkpointed_stream = Enumeration("checkpointed_stream", [ (0, "NONE"), (1, "REMUS"), (2, "COLO"), ]) # # Complex libxl types # libxl_ioport_range = Struct("ioport_range", [ ("first", uint32), ("number", uint32), ]) libxl_iomem_range = Struct("iomem_range", [ # start host frame number to be mapped to the guest ("start", uint64), # number of frames to be mapped ("number", uint64), # guest frame number used as a start for the mapping ("gfn", uint64, {'init_val': "LIBXL_INVALID_GFN"}), ]) libxl_vga_interface_info = Struct("vga_interface_info", [ ("kind", libxl_vga_interface_type), ]) libxl_vnc_info = Struct("vnc_info", [ ("enable", libxl_defbool), # "address:port" that should be listened on ("listen", string), ("passwd", string), ("display", integer), # If set then try to find an unused port ("findunused", libxl_defbool), ]) libxl_spice_info = Struct("spice_info", [ ("enable", libxl_defbool), # At least one of spice port or spicetls_post must be given ("port", integer), ("tls_port", integer), # Interface to bind to ("host", string), # enable client connection with no password ("disable_ticketing", libxl_defbool), ("passwd", string), ("agent_mouse", libxl_defbool), ("vdagent", libxl_defbool), ("clipboard_sharing", libxl_defbool), ("usbredirection", integer), ("image_compression", string), ("streaming_video", string), ]) libxl_sdl_info = Struct("sdl_info", [ ("enable", libxl_defbool), ("opengl", libxl_defbool), ("display", string), ("xauthority", string), ]) libxl_dominfo = Struct("dominfo",[ ("uuid", libxl_uuid), ("domid", libxl_domid), ("ssidref", uint32), ("ssid_label", string), ("running", bool), ("blocked", bool), ("paused", bool), ("shutdown", bool), ("dying", bool), ("never_stop", bool), # Valid iff ->shutdown is true. # # Otherwise set to a value guaranteed not to clash with any valid # LIBXL_SHUTDOWN_REASON_* constant. ("shutdown_reason", libxl_shutdown_reason), ("outstanding_memkb", MemKB), ("current_memkb", MemKB), ("shared_memkb", MemKB), ("paged_memkb", MemKB), ("max_memkb", MemKB), ("cpu_time", uint64), ("vcpu_max_id", uint32), ("vcpu_online", uint32), ("cpupool", uint32), ("domain_type", libxl_domain_type), ], dir=DIR_OUT) libxl_cpupoolinfo = Struct("cpupoolinfo", [ ("poolid", uint32), ("pool_name", string), ("sched", libxl_scheduler), ("n_dom", uint32), ("cpumap", libxl_bitmap) ], dir=DIR_OUT) libxl_channelinfo = Struct("channelinfo", [ ("backend", string), ("backend_id", uint32), ("frontend", string), ("frontend_id", uint32), ("devid", libxl_devid), ("state", integer), ("evtch", integer), ("rref", integer), ("u", KeyedUnion(None, libxl_channel_connection, "connection", [("unknown", None), ("pty", Struct(None, [("path", string),])), ("socket", None), ])), ], dir=DIR_OUT) libxl_vminfo = Struct("vminfo", [ ("uuid", libxl_uuid), ("domid", libxl_domid), ], dir=DIR_OUT) libxl_version_info = Struct("version_info", [ ("xen_version_major", integer), ("xen_version_minor", integer), ("xen_version_extra", string), ("compiler", string), ("compile_by", string), ("compile_domain", string), ("compile_date", string), ("capabilities", string), ("changeset", string), ("virt_start", uint64), ("pagesize", integer), ("commandline", string), ("build_id", string), ], dir=DIR_OUT) libxl_domain_create_info = Struct("domain_create_info",[ ("type", libxl_domain_type), ("hap", libxl_defbool), ("oos", libxl_defbool), ("ssidref", uint32), ("ssid_label", string), ("name", string), ("uuid", libxl_uuid), ("xsdata", libxl_key_value_list), ("platformdata", libxl_key_value_list), ("poolid", uint32), ("pool_name", string), ("run_hotplug_scripts",libxl_defbool), ("driver_domain",libxl_defbool), ], dir=DIR_IN) libxl_domain_restore_params = Struct("domain_restore_params", [ ("checkpointed_stream", integer), ("stream_version", uint32, {'init_val': '1'}), ("colo_proxy_script", string), ("userspace_colo_proxy", libxl_defbool), ]) libxl_sched_params = Struct("sched_params",[ ("vcpuid", integer, {'init_val': 'LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT'}), ("weight", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}), ("cap", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}), ("period", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}), ("extratime", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}), ("budget", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}), ]) libxl_vcpu_sched_params = Struct("vcpu_sched_params",[ ("sched", libxl_scheduler), ("vcpus", Array(libxl_sched_params, "num_vcpus")), ]) libxl_domain_sched_params = Struct("domain_sched_params",[ ("sched", libxl_scheduler), ("weight", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT'}), ("cap", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT'}), ("period", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT'}), ("budget", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT'}), # The following three parameters ('slice', 'latency' and 'extratime') are deprecated, # and will have no effect if used, since the SEDF scheduler has been removed. # Note that 'period' was an SDF parameter too, but it is still effective as it is # now used (together with 'budget') by the RTDS scheduler. ("slice", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT'}), ("latency", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT'}), ("extratime", integer, {'init_val': 'LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT'}), ]) libxl_vnode_info = Struct("vnode_info", [ ("memkb", MemKB), ("distances", Array(uint32, "num_distances")), # distances from this node to other nodes ("pnode", uint32), # physical node of this node ("vcpus", libxl_bitmap), # vcpus in this node ]) libxl_gic_version = Enumeration("gic_version", [ (0, "DEFAULT"), (0x20, "v2"), (0x30, "v3") ], init_val = "LIBXL_GIC_VERSION_DEFAULT") libxl_rdm_reserve = Struct("rdm_reserve", [ ("strategy", libxl_rdm_reserve_strategy), ("policy", libxl_rdm_reserve_policy), ]) # Consistent with the values defined for HVM_PARAM_ALTP2M libxl_altp2m_mode = Enumeration("altp2m_mode", [ (0, "disabled"), (1, "mixed"), (2, "external"), (3, "limited"), ], init_val = "LIBXL_ALTP2M_MODE_DISABLED") libxl_domain_build_info = Struct("domain_build_info",[ ("max_vcpus", integer), ("avail_vcpus", libxl_bitmap), ("cpumap", libxl_bitmap), ("nodemap", libxl_bitmap), ("vcpu_hard_affinity", Array(libxl_bitmap, "num_vcpu_hard_affinity")), ("vcpu_soft_affinity", Array(libxl_bitmap, "num_vcpu_soft_affinity")), ("numa_placement", libxl_defbool), ("tsc_mode", libxl_tsc_mode), ("max_memkb", MemKB), ("target_memkb", MemKB), ("video_memkb", MemKB), ("shadow_memkb", MemKB), ("rtc_timeoffset", uint32), ("exec_ssidref", uint32), ("exec_ssid_label", string), ("localtime", libxl_defbool), ("disable_migrate", libxl_defbool), ("cpuid", libxl_cpuid_policy_list), ("blkdev_start", string), ("vnuma_nodes", Array(libxl_vnode_info, "num_vnuma_nodes")), ("device_model_version", libxl_device_model_version), ("device_model_stubdomain", libxl_defbool), # if you set device_model you must set device_model_version too ("device_model", string), ("device_model_ssidref", uint32), ("device_model_ssid_label", string), # device_model_user is not ready for use yet ("device_model_user", string), # extra parameters pass directly to qemu, NULL terminated ("extra", libxl_string_list), # extra parameters pass directly to qemu for PV guest, NULL terminated ("extra_pv", libxl_string_list), # extra parameters pass directly to qemu for HVM guest, NULL terminated ("extra_hvm", libxl_string_list), # parameters for all type of scheduler ("sched_params", libxl_domain_sched_params), ("ioports", Array(libxl_ioport_range, "num_ioports")), ("irqs", Array(uint32, "num_irqs")), ("iomem", Array(libxl_iomem_range, "num_iomem")), ("claim_mode", libxl_defbool), ("event_channels", uint32), ("kernel", string), ("cmdline", string), ("ramdisk", string), # Given the complexity of verifying the validity of a device tree, # libxl doesn't do any security check on it. It's the responsibility # of the caller to provide only trusted device tree. # Note that the partial device tree should avoid to use the phandle # 65000 which is reserved by the toolstack. ("device_tree", string), ("acpi", libxl_defbool), ("u", KeyedUnion(None, libxl_domain_type, "type", [("hvm", Struct(None, [("firmware", string), ("bios", libxl_bios_type), ("pae", libxl_defbool), ("apic", libxl_defbool), # The following acpi field is deprecated. # Please use the unified acpi field above # which works for both x86 and ARM. ("acpi", libxl_defbool), ("acpi_s3", libxl_defbool), ("acpi_s4", libxl_defbool), ("acpi_laptop_slate",libxl_defbool), ("nx", libxl_defbool), ("viridian", libxl_defbool), ("viridian_enable", libxl_bitmap), ("viridian_disable", libxl_bitmap), ("timeoffset", string), ("hpet", libxl_defbool), ("vpt_align", libxl_defbool), ("mmio_hole_memkb", MemKB), ("timer_mode", libxl_timer_mode), ("nested_hvm", libxl_defbool), # The u.hvm.altp2m field is used solely # for x86 HVM guests and is maintained # for legacy purposes. ("altp2m", libxl_defbool), ("system_firmware", string), ("smbios_firmware", string), ("acpi_firmware", string), ("hdtype", libxl_hdtype), ("nographic", libxl_defbool), ("vga", libxl_vga_interface_info), ("vnc", libxl_vnc_info), # keyboard layout, default is en-us keyboard ("keymap", string), ("sdl", libxl_sdl_info), ("spice", libxl_spice_info), ("gfx_passthru", libxl_defbool), ("gfx_passthru_kind", libxl_gfx_passthru_kind), ("serial", string), ("boot", string), ("usb", libxl_defbool), ("usbversion", integer), # usbdevice: # - "tablet" for absolute mouse, # - "mouse" for PS/2 protocol relative mouse ("usbdevice", string), ("soundhw", string), ("xen_platform_pci", libxl_defbool), ("usbdevice_list", libxl_string_list), ("vendor_device", libxl_vendor_device), # See libxl_ms_vm_genid_generate() ("ms_vm_genid", libxl_ms_vm_genid), ("serial_list", libxl_string_list), ("rdm", libxl_rdm_reserve), ("rdm_mem_boundary_memkb", MemKB), ])), ("pv", Struct(None, [("kernel", string), ("slack_memkb", MemKB), ("bootloader", string), ("bootloader_args", libxl_string_list), ("cmdline", string), ("ramdisk", string), ("features", string, {'const': True}), # Use host's E820 for PCI passthrough. ("e820_host", libxl_defbool), ])), ("invalid", None), ], keyvar_init_val = "LIBXL_DOMAIN_TYPE_INVALID")), ("arch_arm", Struct(None, [("gic_version", libxl_gic_version), ])), # Alternate p2m is not bound to any architecture or guest type, as it is # supported by x86 HVM and ARM support is planned. ("altp2m", libxl_altp2m_mode), ], dir=DIR_IN ) libxl_device_vfb = Struct("device_vfb", [ ("backend_domid", libxl_domid), ("backend_domname",string), ("devid", libxl_devid), ("vnc", libxl_vnc_info), ("sdl", libxl_sdl_info), # set keyboard layout, default is en-us keyboard ("keymap", string), ]) libxl_device_vkb = Struct("device_vkb", [ ("backend_domid", libxl_domid), ("backend_domname", string), ("devid", libxl_devid), ]) libxl_device_disk = Struct("device_disk", [ ("backend_domid", libxl_domid), ("backend_domname", string), ("pdev_path", string), ("vdev", string), ("backend", libxl_disk_backend), ("format", libxl_disk_format), ("script", string), ("removable", integer), ("readwrite", integer), ("is_cdrom", integer), ("direct_io_safe", bool), ("discard_enable", libxl_defbool), # Note that the COLO configuration settings should be considered unstable. # They may change incompatibly in future versions of Xen. ("colo_enable", libxl_defbool), ("colo_restore_enable", libxl_defbool), ("colo_host", string), ("colo_port", integer), ("colo_export", string), ("active_disk", string), ("hidden_disk", string) ]) libxl_device_nic = Struct("device_nic", [ ("backend_domid", libxl_domid), ("backend_domname", string), ("devid", libxl_devid), ("mtu", integer), ("model", string), ("mac", libxl_mac), ("ip", string), ("bridge", string), ("ifname", string), ("script", string), ("nictype", libxl_nic_type), ("rate_bytes_per_interval", uint64), ("rate_interval_usecs", uint32), ("gatewaydev", string), # Note that the COLO configuration settings should be considered unstable. # They may change incompatibly in future versions of Xen. ("coloft_forwarddev", string), ("colo_sock_mirror_id", string), ("colo_sock_mirror_ip", string), ("colo_sock_mirror_port", string), ("colo_sock_compare_pri_in_id", string), ("colo_sock_compare_pri_in_ip", string), ("colo_sock_compare_pri_in_port", string), ("colo_sock_compare_sec_in_id", string), ("colo_sock_compare_sec_in_ip", string), ("colo_sock_compare_sec_in_port", string), ("colo_sock_compare_notify_id", string), ("colo_sock_compare_notify_ip", string), ("colo_sock_compare_notify_port", string), ("colo_sock_redirector0_id", string), ("colo_sock_redirector0_ip", string), ("colo_sock_redirector0_port", string), ("colo_sock_redirector1_id", string), ("colo_sock_redirector1_ip", string), ("colo_sock_redirector1_port", string), ("colo_sock_redirector2_id", string), ("colo_sock_redirector2_ip", string), ("colo_sock_redirector2_port", string), ("colo_filter_mirror_queue", string), ("colo_filter_mirror_outdev", string), ("colo_filter_redirector0_queue", string), ("colo_filter_redirector0_indev", string), ("colo_filter_redirector0_outdev", string), ("colo_filter_redirector1_queue", string), ("colo_filter_redirector1_indev", string), ("colo_filter_redirector1_outdev", string), ("colo_compare_pri_in", string), ("colo_compare_sec_in", string), ("colo_compare_out", string), ("colo_compare_notify_dev", string), ("colo_sock_sec_redirector0_id", string), ("colo_sock_sec_redirector0_ip", string), ("colo_sock_sec_redirector0_port", string), ("colo_sock_sec_redirector1_id", string), ("colo_sock_sec_redirector1_ip", string), ("colo_sock_sec_redirector1_port", string), ("colo_filter_sec_redirector0_queue", string), ("colo_filter_sec_redirector0_indev", string), ("colo_filter_sec_redirector0_outdev", string), ("colo_filter_sec_redirector1_queue", string), ("colo_filter_sec_redirector1_indev", string), ("colo_filter_sec_redirector1_outdev", string), ("colo_filter_sec_rewriter0_queue", string), ("colo_checkpoint_host", string), ("colo_checkpoint_port", string) ]) libxl_device_pci = Struct("device_pci", [ ("func", uint8), ("dev", uint8), ("bus", uint8), ("domain", integer), ("vdevfn", uint32), ("vfunc_mask", uint32), ("msitranslate", bool), ("power_mgmt", bool), ("permissive", bool), ("seize", bool), ("rdm_policy", libxl_rdm_reserve_policy), ]) libxl_device_rdm = Struct("device_rdm", [ ("start", uint64), ("size", uint64), ("policy", libxl_rdm_reserve_policy), ]) libxl_usbctrl_type = Enumeration("usbctrl_type", [ (0, "AUTO"), (1, "PV"), (2, "DEVICEMODEL"), (3, "QUSB"), ]) libxl_usbdev_type = Enumeration("usbdev_type", [ (1, "hostdev"), ]) libxl_device_usbctrl = Struct("device_usbctrl", [ ("type", libxl_usbctrl_type), ("devid", libxl_devid), ("version", integer), ("ports", integer), ("backend_domid", libxl_domid), ("backend_domname", string), ]) libxl_device_usbdev = Struct("device_usbdev", [ ("ctrl", libxl_devid), ("port", integer), ("u", KeyedUnion(None, libxl_usbdev_type, "type", [("hostdev", Struct(None, [ ("hostbus", uint8), ("hostaddr", uint8)])), ])), ]) libxl_device_dtdev = Struct("device_dtdev", [ ("path", string), ]) libxl_device_vtpm = Struct("device_vtpm", [ ("backend_domid", libxl_domid), ("backend_domname", string), ("devid", libxl_devid), ("uuid", libxl_uuid), ]) libxl_device_p9 = Struct("device_p9", [ ("backend_domid", libxl_domid), ("backend_domname", string), ("tag", string), ("path", string), ("security_model", string), ("devid", libxl_devid), ]) libxl_device_channel = Struct("device_channel", [ ("backend_domid", libxl_domid), ("backend_domname", string), ("devid", libxl_devid), ("name", string), ("u", KeyedUnion(None, libxl_channel_connection, "connection", [("unknown", None), ("pty", None), ("socket", Struct(None, [("path", string)])), ])), ]) libxl_domain_config = Struct("domain_config", [ ("c_info", libxl_domain_create_info), ("b_info", libxl_domain_build_info), ("disks", Array(libxl_device_disk, "num_disks")), ("nics", Array(libxl_device_nic, "num_nics")), ("pcidevs", Array(libxl_device_pci, "num_pcidevs")), ("rdms", Array(libxl_device_rdm, "num_rdms")), ("dtdevs", Array(libxl_device_dtdev, "num_dtdevs")), ("vfbs", Array(libxl_device_vfb, "num_vfbs")), ("vkbs", Array(libxl_device_vkb, "num_vkbs")), ("vtpms", Array(libxl_device_vtpm, "num_vtpms")), ("p9", Array(libxl_device_p9, "num_p9s")), # a channel manifests as a console with a name, # see docs/misc/channels.txt ("channels", Array(libxl_device_channel, "num_channels")), ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")), ("usbdevs", Array(libxl_device_usbdev, "num_usbdevs")), ("on_poweroff", libxl_action_on_shutdown), ("on_reboot", libxl_action_on_shutdown), ("on_watchdog", libxl_action_on_shutdown), ("on_crash", libxl_action_on_shutdown), ("on_soft_reset", libxl_action_on_shutdown), ], dir=DIR_IN) libxl_diskinfo = Struct("diskinfo", [ ("backend", string), ("backend_id", uint32), ("frontend", string), ("frontend_id", uint32), ("devid", libxl_devid), ("state", integer), ("evtch", integer), ("rref", integer), ], dir=DIR_OUT) libxl_nicinfo = Struct("nicinfo", [ ("backend", string), ("backend_id", uint32), ("frontend", string), ("frontend_id", uint32), ("devid", libxl_devid), ("state", integer), ("evtch", integer), ("rref_tx", integer), ("rref_rx", integer), ], dir=DIR_OUT) libxl_vtpminfo = Struct("vtpminfo", [ ("backend", string), ("backend_id", uint32), ("frontend", string), ("frontend_id", uint32), ("devid", libxl_devid), ("state", integer), ("evtch", integer), ("rref", integer), ("uuid", libxl_uuid), ], dir=DIR_OUT) libxl_usbctrlinfo = Struct("usbctrlinfo", [ ("type", libxl_usbctrl_type), ("devid", libxl_devid), ("version", integer), ("ports", integer), ("backend", string), ("backend_id", uint32), ("frontend", string), ("frontend_id", uint32), ("state", integer), ("evtch", integer), ("ref_urb", integer), ("ref_conn", integer), ], dir=DIR_OUT) libxl_vcpuinfo = Struct("vcpuinfo", [ ("vcpuid", uint32), ("cpu", uint32), ("online", bool), ("blocked", bool), ("running", bool), ("vcpu_time", uint64), # total vcpu time ran (ns) ("cpumap", libxl_bitmap), # current hard cpu affinity ("cpumap_soft", libxl_bitmap), # current soft cpu affinity ], dir=DIR_OUT) libxl_physinfo = Struct("physinfo", [ ("threads_per_core", uint32), ("cores_per_socket", uint32), ("max_cpu_id", uint32), ("nr_cpus", uint32), ("cpu_khz", uint32), ("total_pages", uint64), ("free_pages", uint64), ("scrub_pages", uint64), ("outstanding_pages", uint64), ("sharing_freed_pages", uint64), ("sharing_used_frames", uint64), ("nr_nodes", uint32), ("hw_cap", libxl_hwcap), ("cap_hvm", bool), ("cap_hvm_directio", bool), ], dir=DIR_OUT) # NUMA node characteristics: size and free are how much memory it has, and how # much of it is free, respectively. dists is an array of distances from this # node to each other node. libxl_numainfo = Struct("numainfo", [ ("size", uint64), ("free", uint64), ("dists", Array(uint32, "num_dists")), ], dir=DIR_OUT) libxl_cputopology = Struct("cputopology", [ ("core", uint32), ("socket", uint32), ("node", uint32), ], dir=DIR_OUT) libxl_pcitopology = Struct("pcitopology", [ ("seg", uint16), ("bus", uint8), ("devfn", uint8), ("node", uint32), ], dir=DIR_OUT) libxl_sched_credit_params = Struct("sched_credit_params", [ ("tslice_ms", integer), ("ratelimit_us", integer), ], dispose_fn=None) libxl_sched_credit2_params = Struct("sched_credit2_params", [ ("ratelimit_us", integer), ], dispose_fn=None) libxl_domain_remus_info = Struct("domain_remus_info",[ ("interval", integer), ("allow_unsafe", libxl_defbool), ("blackhole", libxl_defbool), ("compression", libxl_defbool), ("netbuf", libxl_defbool), ("netbufscript", string), ("diskbuf", libxl_defbool), ("colo", libxl_defbool), ("userspace_colo_proxy", libxl_defbool) ]) libxl_event_type = Enumeration("event_type", [ (1, "DOMAIN_SHUTDOWN"), (2, "DOMAIN_DEATH"), (3, "DISK_EJECT"), (4, "OPERATION_COMPLETE"), (5, "DOMAIN_CREATE_CONSOLE_AVAILABLE"), ]) libxl_ev_user = UInt(64) libxl_ev_link = Builtin("ev_link", json_parse_type="JSON_STRING", passby=PASS_BY_REFERENCE, private=True) libxl_event = Struct("event",[ ("link", libxl_ev_link), # for use by libxl; caller may use this once the event has been # returned by libxl_event_{check,wait} ("domid", libxl_domid), ("domuuid", libxl_uuid), ("for_user", libxl_ev_user), ("u", KeyedUnion(None, libxl_event_type, "type", [("domain_shutdown", Struct(None, [ ("shutdown_reason", uint8), ])), ("domain_death", None), ("disk_eject", Struct(None, [ ("vdev", string), ("disk", libxl_device_disk), ])), ("operation_complete", Struct(None, [ ("rc", integer), ])), ("domain_create_console_available", None), ]))]) libxl_psr_cmt_type = Enumeration("psr_cmt_type", [ (1, "CACHE_OCCUPANCY"), (2, "TOTAL_MEM_COUNT"), (3, "LOCAL_MEM_COUNT"), ]) libxl_psr_cbm_type = Enumeration("psr_cbm_type", [ (0, "UNKNOWN"), (1, "L3_CBM"), (2, "L3_CBM_CODE"), (3, "L3_CBM_DATA"), ]) libxl_psr_cat_info = Struct("psr_cat_info", [ ("id", uint32), ("cos_max", uint32), ("cbm_len", uint32), ("cdp_enabled", bool), ]) xen-4.9.2/tools/libxl/libxl_internal.h0000664000175000017500000054030413256712137016114 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef LIBXL_INTERNAL_H #define LIBXL_INTERNAL_H #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_sr_stream_format.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define XC_WANT_COMPAT_MAP_FOREIGN_API #include #include #include #include "xentoollog.h" #include #ifdef LIBXL_H # error libxl.h should be included via libxl_internal.h, not separately #endif #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) # define LIBXL_EXTERNAL_CALLERS_ONLY \ __attribute__((warning("may not be called from within libxl"))) #endif #include "libxl.h" #include "_paths.h" #include "_libxl_save_msgs_callout.h" #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) #define _hidden __attribute__((visibility("hidden"))) #define _protected __attribute__((visibility("protected"))) #else #define _hidden #define _protected #endif #include "flexarray.h" #include "libxl_utils.h" #include "libxl_json.h" #include "_libxl_types_internal.h" #include "_libxl_types_internal_json.h" #define LIBXL_INIT_TIMEOUT 10 #define LIBXL_DESTROY_TIMEOUT 10 #define LIBXL_HOTPLUG_TIMEOUT 40 /* QEMU may be slow to load and start due to a bug in Linux where the I/O * subsystem sometime produce high latency under load. */ #define LIBXL_DEVICE_MODEL_START_TIMEOUT 60 #define LIBXL_DEVICE_MODEL_SAVE_FILE XEN_LIB_DIR "/qemu-save" /* .$domid */ #define LIBXL_DEVICE_MODEL_RESTORE_FILE XEN_LIB_DIR "/qemu-resume" /* .$domid */ #define LIBXL_STUBDOM_START_TIMEOUT 30 #define LIBXL_QEMU_BODGE_TIMEOUT 2 #define LIBXL_XENCONSOLE_LIMIT 1048576 #define LIBXL_XENCONSOLE_PROTOCOL "vt100" #define LIBXL_MAXMEM_CONSTANT 1024 #define LIBXL_PV_EXTRA_MEMORY 1024 #define LIBXL_HVM_EXTRA_MEMORY 2048 #define LIBXL_MIN_DOM0_MEM (128*1024) #define LIBXL_INVALID_GFN (~(uint64_t)0) #define LIBXL_VGA_HOLE_SIZE 0x20 /* use 0 as the domid of the toolstack domain for now */ #define LIBXL_TOOLSTACK_DOMID 0 #define QEMU_SIGNATURE "DeviceModelRecord0002" #define STUBDOM_CONSOLE_LOGGING 0 #define STUBDOM_CONSOLE_SAVE 1 #define STUBDOM_CONSOLE_RESTORE 2 #define STUBDOM_CONSOLE_SERIAL 3 #define STUBDOM_SPECIAL_CONSOLES 3 #define TAP_DEVICE_SUFFIX "-emu" #define DOMID_XS_PATH "domid" #define INVALID_DOMID ~0 /* Size macros. */ #define __AC(X,Y) (X##Y) #define _AC(X,Y) __AC(X,Y) #define MB(_mb) (_AC(_mb, ULL) << 20) #define GB(_gb) (_AC(_gb, ULL) << 30) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define ROUNDUP(_val, _order) \ (((unsigned long)(_val)+(1UL<<(_order))-1) & ~((1UL<<(_order))-1)) #define DIV_ROUNDUP(n, d) (((n) + (d) - 1) / (d)) #define MASK_EXTR(v, m) (((v) & (m)) / ((m) & -(m))) #define MASK_INSR(v, m) (((v) * ((m) & -(m))) & (m)) #define min(X, Y) ({ \ const typeof (X) _x = (X); \ const typeof (Y) _y = (Y); \ (void) (&_x == &_y); \ (_x < _y) ? _x : _y; }) #define max(X, Y) ({ \ const typeof (X) _x = (X); \ const typeof (Y) _y = (Y); \ (void) (&_x == &_y); \ (_x > _y) ? _x : _y; }) #define min_t(type, x, y) \ ({ const type _x = (x); const type _y = (y); _x < _y ? _x: _y; }) #define max_t(type, x, y) \ ({ const type _x = (x); const type _y = (y); _x > _y ? _x: _y; }) #define LIBXL__LOGGING_ENABLED #ifdef LIBXL__LOGGING_ENABLED #define LIBXL__LOG(ctx, loglevel, _f, _a...) libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a) #define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...) libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a) #define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...) libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, INVALID_DOMID, _f, ##_a) /* Same log functions as above, but with _d being a domain id. */ #define LIBXL__LOGD(ctx, loglevel, _d, _f, _a...) libxl__log(ctx, loglevel, -1, __FILE__, __LINE__, __func__, _d, _f, ##_a) #define LIBXL__LOGD_ERRNO(ctx, loglevel, _d, _f, _a...) libxl__log(ctx, loglevel, errno, __FILE__, __LINE__, __func__, _d, _f, ##_a) #define LIBXL__LOGD_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...) libxl__log(ctx, loglevel, errnoval, __FILE__, __LINE__, __func__, _d, _f, ##_a) #else #define LIBXL__LOG(ctx, loglevel, _f, _a...) #define LIBXL__LOG_ERRNO(ctx, loglevel, _f, _a...) #define LIBXL__LOG_ERRNOVAL(ctx, loglevel, errnoval, _f, _a...) #define LIBXLD__LOG(ctx, loglevel, _d, _f, _a...) #define LIBXLD__LOG_ERRNO(ctx, loglevel, _d, _f, _a...) #define LIBXLD__LOG_ERRNOVAL(ctx, loglevel, errnoval, _d, _f, _a...) #endif /* all of these macros preserve errno (saving and restoring) */ /* Convert pfn to physical address space. */ #define pfn_to_paddr(x) ((uint64_t)(x) << XC_PAGE_SHIFT) /* logging */ _hidden void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval, const char *file /* may be 0 */, int line /* ignored if !file */, const char *func /* may be 0 */, uint32_t domid /* may be INVALID_DOMID */, const char *fmt, va_list al) __attribute__((format(printf,8,0))); _hidden void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval, const char *file /* may be 0 */, int line /* ignored if !file */, const char *func /* may be 0 */, uint32_t domid /* may be INVALID_DOMID */, const char *fmt, ...) __attribute__((format(printf,8,9))); /* these functions preserve errno (saving and restoring) */ typedef struct libxl__gc libxl__gc; typedef struct libxl__egc libxl__egc; typedef struct libxl__ao libxl__ao; typedef struct libxl__aop_occurred libxl__aop_occurred; typedef struct libxl__osevent_hook_nexus libxl__osevent_hook_nexus; typedef struct libxl__osevent_hook_nexi libxl__osevent_hook_nexi; typedef struct libxl__domain_create_state libxl__domain_create_state; typedef void libxl__domain_create_cb(struct libxl__egc *egc, libxl__domain_create_state *dcs, int rc, uint32_t domid); typedef struct libxl__colo_device_nic libxl__colo_device_nic; typedef struct libxl__colo_qdisk libxl__colo_qdisk; typedef struct libxl__colo_proxy_state libxl__colo_proxy_state; typedef struct libxl__colo_save_state libxl__colo_save_state; typedef struct libxl__colo_restore_state libxl__colo_restore_state; _hidden void libxl__alloc_failed(libxl_ctx *, const char *func, size_t nmemb, size_t size) __attribute__((noreturn)); /* func, size and nmemb are used only in the log message. * You may pass size==0 if size and nmemb are not meaningful * and should not be printed. */ typedef struct libxl__ev_fd libxl__ev_fd; typedef void libxl__ev_fd_callback(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents); /* Note that revents may contain POLLERR or POLLHUP regardless of * events; otherwise revents contains only bits in events. Contrary * to the documentation for poll(2), POLLERR and POLLHUP can occur * even if only POLLIN was set in events. (POLLNVAL is a fatal * error and will cause libxl event machinery to fail an assertion.) * * It is not permitted to listen for the same or overlapping events * on the same fd using multiple different libxl__ev_fd's. * * (Spurious wakeups, and spurious bits set in revents, are * suppressed by the libxl event core.) */ struct libxl__ev_fd { /* caller should include this in their own struct */ /* read-only for caller, who may read only when registered: */ int fd; short events; libxl__ev_fd_callback *func; /* remainder is private for libxl__ev_fd... */ LIBXL_LIST_ENTRY(libxl__ev_fd) entry; libxl__osevent_hook_nexus *nexus; }; typedef struct libxl__ao_abortable libxl__ao_abortable; typedef void libxl__ao_abortable_callback(libxl__egc *egc, libxl__ao_abortable *ao_abortable, int rc /* ABORTED */); struct libxl__ao_abortable { /* caller must fill this in and it must remain valid */ libxl__ao *ao; libxl__ao_abortable_callback *callback; /* remainder is private for abort machinery */ bool registered; LIBXL_LIST_ENTRY(libxl__ao_abortable) entry; /* * For nested aos: * Semantically, abort affects the whole tree of aos, * not just the parent. * libxl__ao_abortable.ao refers to the child, so * that the child callback sees the right ao. (After all, * it was code dealing with the child that set .ao.) * But, the abortable is recorded on the "abortables" list * for the ultimate root ao, so that every possible child * abort occurs as a result of the abort of the parent. * We set ao->aborting only in the root. */ }; _hidden int libxl__ao_abortable_register(libxl__ao_abortable*); _hidden void libxl__ao_abortable_deregister(libxl__ao_abortable*); static inline void libxl__ao_abortable_init (libxl__ao_abortable *c) { c->registered = 0; } static inline bool libxl__ao_abortable_isregistered (const libxl__ao_abortable *c) { return c->registered; } int libxl__ao_aborting(libxl__ao *ao); /* -> 0 or ERROR_ABORTED */ typedef struct libxl__ev_time libxl__ev_time; typedef void libxl__ev_time_callback(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc); /* TIMEDOUT or ABORTED */ struct libxl__ev_time { /* caller should include this in their own struct */ /* read-only for caller, who may read only when registered: */ libxl__ev_time_callback *func; /* remainder is private for libxl__ev_time... */ int infinite; /* not registered in list or with app if infinite */ LIBXL_TAILQ_ENTRY(libxl__ev_time) entry; struct timeval abs; libxl__osevent_hook_nexus *nexus; libxl__ao_abortable abrt; }; typedef struct libxl__ev_xswatch libxl__ev_xswatch; typedef void libxl__ev_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch*, const char *watch_path, const char *event_path); struct libxl__ev_xswatch { /* caller should include this in their own struct */ /* read-only for caller, who may read only when registered: */ char *path; libxl__ev_xswatch_callback *callback; /* remainder is private for libxl__ev_xswatch... */ int slotnum; /* registered iff slotnum >= 0 */ uint32_t counterval; }; typedef struct libxl__ev_evtchn libxl__ev_evtchn; typedef void libxl__ev_evtchn_callback(libxl__egc *egc, libxl__ev_evtchn*); struct libxl__ev_evtchn { /* caller must fill these in, and they must all remain valid */ libxl__ev_evtchn_callback *callback; int port; /* remainder is private for libxl__ev_evtchn_... */ bool waiting; LIBXL_LIST_ENTRY(libxl__ev_evtchn) entry; }; /* * An entry in the watch_slots table is either: * 1. an entry in the free list, ie NULL or pointer to next free list entry * 2. an pointer to a libxl__ev_xswatch * * But we don't want to use unions or type-punning because the * compiler might "prove" that our code is wrong and misoptimise it. * * The rules say that all struct pointers have identical * representation and alignment requirements (C99+TC1+TC2 6.2.5p26) so * what we do is simply declare our array as containing only the free * list pointers, and explicitly convert from and to our actual * xswatch pointers when we store and retrieve them. */ typedef struct libxl__ev_watch_slot { LIBXL_SLIST_ENTRY(struct libxl__ev_watch_slot) empty; } libxl__ev_watch_slot; _hidden libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum); typedef struct libxl__ev_child libxl__ev_child; typedef void libxl__ev_child_callback(libxl__egc *egc, libxl__ev_child*, pid_t pid, int status); struct libxl__ev_child { /* caller should include this in their own struct */ /* read-only for caller: */ pid_t pid; /* -1 means unused ("unregistered", ie Idle) */ libxl__ev_child_callback *callback; /* remainder is private for libxl__ev_... */ LIBXL_LIST_ENTRY(struct libxl__ev_child) entry; }; /* * evgen structures, which are the state we use for generating * events for the caller. * * In general in each case there's an internal and an external * version of the _evdisable_FOO function; the internal one is * used during cleanup. */ struct libxl__evgen_domain_death { uint32_t domid; unsigned shutdown_reported:1, death_reported:1; LIBXL_TAILQ_ENTRY(libxl_evgen_domain_death) entry; /* on list .death_reported ? CTX->death_list : CTX->death_reported */ libxl_ev_user user; }; _hidden void libxl__evdisable_domain_death(libxl__gc*, libxl_evgen_domain_death*); struct libxl__evgen_disk_eject { libxl__ev_xswatch watch; uint32_t domid; LIBXL_LIST_ENTRY(libxl_evgen_disk_eject) entry; libxl_ev_user user; char *vdev, *be_ptr_path; }; _hidden void libxl__evdisable_disk_eject(libxl__gc*, libxl_evgen_disk_eject*); typedef struct libxl__poller libxl__poller; struct libxl__poller { /* * These are used to allow other threads to wake up a thread which * may be stuck in poll, because whatever it was waiting for * hadn't happened yet. Threads which generate events will write * a byte to each pipe. A thread which is waiting will empty its * own pipe, and put its poller on the pollers_event list, before * releasing the ctx lock and going into poll; when it comes out * of poll it will take the poller off the pollers_event list. * * A thread which is waiting for completion of a synchronous ao * will allocate a poller and record it in the ao, so that other * threads can wake it up. * * When a thread is done with a poller it should put it onto * pollers_idle, where it can be reused later. * * The "poller_app" is never idle, but is sometimes on * pollers_event. */ LIBXL_LIST_ENTRY(libxl__poller) entry; struct pollfd *fd_polls; int fd_polls_allocd; int fd_rindices_allocd; int (*fd_rindices)[3]; /* see libxl_event.c:beforepoll_internal */ int wakeup_pipe[2]; /* 0 means no fd allocated */ /* * We also use the poller to record whether any fds have been * deregistered since we entered poll. Each poller which is not * idle is on the list pollers_fds_changed. fds_changed is * cleared by beforepoll, and tested by afterpoll. Whenever an fd * event is deregistered, we set the fds_changed of all non-idle * pollers. So afterpoll can tell whether any POLLNVAL is * plausibly due to an fd being closed and reopened. */ LIBXL_LIST_ENTRY(libxl__poller) fds_changed_entry; bool fds_changed; }; struct libxl__gc { /* mini-GC */ int alloc_maxsize; /* -1 means this is the dummy non-gc gc */ void **alloc_ptrs; libxl_ctx *owner; }; struct libxl__ctx { xentoollog_logger *lg; xc_interface *xch; struct xs_handle *xsh; libxl__gc nogc_gc; const libxl_event_hooks *event_hooks; void *event_hooks_user; pthread_mutex_t lock; /* protects data structures hanging off the ctx */ /* Always use libxl__ctx_lock and _unlock (or the convenience * macors CTX_LOCK and CTX_UNLOCK) to manipulate this. * * You may acquire this mutex recursively if it is convenient to * do so. You may not acquire this lock at the same time as any * other lock. If you need to call application code outside * libxl (ie, a callback) with this lock held then it is * necessaray to impose restrictions on the caller to maintain a * proper lock hierarchy, and these restrictions must then be * documented in the libxl public interface. */ LIBXL_TAILQ_HEAD(libxl__event_list, libxl_event) occurred; int osevent_in_hook; const libxl_osevent_hooks *osevent_hooks; void *osevent_user; /* See the comment for OSEVENT_HOOK_INTERN in libxl_event.c * for restrictions on the use of the osevent fields. */ libxl__poller *poller_app; /* libxl_osevent_beforepoll and _afterpoll */ LIBXL_LIST_HEAD(, libxl__poller) pollers_event, pollers_idle; LIBXL_LIST_HEAD(, libxl__poller) pollers_fds_changed; LIBXL_SLIST_HEAD(libxl__osevent_hook_nexi, libxl__osevent_hook_nexus) hook_fd_nexi_idle, hook_timeout_nexi_idle; LIBXL_LIST_HEAD(, libxl__ev_fd) efds; LIBXL_TAILQ_HEAD(, libxl__ev_time) etimes; libxl__ev_watch_slot *watch_slots; int watch_nslots, nwatches; LIBXL_SLIST_HEAD(, libxl__ev_watch_slot) watch_freeslots; uint32_t watch_counter; /* helps disambiguate slot reuse */ libxl__ev_fd watch_efd; xenevtchn_handle *xce; /* waiting must be done only with libxl__ev_evtchn* */ LIBXL_LIST_HEAD(, libxl__ev_evtchn) evtchns_waiting; libxl__ev_fd evtchn_efd; LIBXL_LIST_HEAD(, libxl__ao) aos_inprogress; LIBXL_TAILQ_HEAD(libxl__evgen_domain_death_list, libxl_evgen_domain_death) death_list /* sorted by domid */, death_reported; libxl__ev_xswatch death_watch; LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens; const libxl_childproc_hooks *childproc_hooks; void *childproc_user; int sigchld_selfpipe[2]; /* [0]==-1 means handler not installed */ libxl__ev_fd sigchld_selfpipe_efd; LIBXL_LIST_HEAD(, libxl__ev_child) children; bool sigchld_user_registered; LIBXL_LIST_ENTRY(libxl_ctx) sigchld_users_entry; libxl_version_info version_info; }; /* * libxl__device is a transparent structure that doesn't contain private fields * or external memory references, and as such can be copied by assignment. */ typedef struct { uint32_t backend_devid; uint32_t backend_domid; uint32_t devid; uint32_t domid; libxl__device_kind backend_kind; libxl__device_kind kind; } libxl__device; /* Used to know if backend of given device is QEMU */ #define QEMU_BACKEND(dev) (\ (dev)->backend_kind == LIBXL__DEVICE_KIND_QDISK || \ (dev)->backend_kind == LIBXL__DEVICE_KIND_VFB || \ (dev)->backend_kind == LIBXL__DEVICE_KIND_QUSB || \ (dev)->backend_kind == LIBXL__DEVICE_KIND_VKBD) #define XC_PCI_BDF "0x%x, 0x%x, 0x%x, 0x%x" #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) #define PCI_FUNC(devfn) ((devfn) & 0x07) #define AUTO_PHP_SLOT 0x100 #define PROC_PCI_NUM_RESOURCES 7 #define PCI_BAR_IO 0x01 #define PRINTF_ATTRIBUTE(x, y) __attribute__((format(printf, x, y))) struct libxl__egc { /* For event-generating functions only. * The egc and its gc may be accessed only on the creating thread. */ struct libxl__gc gc; struct libxl__event_list occurred_for_callback; LIBXL_TAILQ_HEAD(, libxl__ao) aos_for_callback; LIBXL_TAILQ_HEAD(, libxl__aop_occurred) aops_for_callback; }; struct libxl__aop_occurred { /* * An aop belongs to, and may be accessed only on, the thread * which created it. It normally lives in that thread's egc. * * While an aop exists, it corresponds to one refcount in * ao->progress_reports_outstanding, preventing ao destruction. */ LIBXL_TAILQ_ENTRY(libxl__aop_occurred) entry; libxl__ao *ao; libxl_event *ev; const libxl_asyncprogress_how *how; }; #define LIBXL__AO_MAGIC 0xA0FACE00ul #define LIBXL__AO_MAGIC_DESTROYED 0xA0DEAD00ul struct libxl__ao { /* * An ao and its gc may be accessed only with the ctx lock held. * * Special exception: If an ao has been added to * egc->aos_for_callback, the thread owning the egc may remove the * ao from that list and make the callback without holding the * lock. * * Corresponding restriction: An ao may be added only to one * egc->aos_for_callback, once; rc and how must already have been * set and may not be subsequently modified. (This restriction is * easily and obviously met since the ao is queued for callback * only in libxl__ao_complete.) */ uint32_t magic; unsigned constructing:1, in_initiator:1, complete:1, notified:1, aborting:1; int manip_refcnt; libxl__ao *nested_root; int nested_progeny; int progress_reports_outstanding; int rc; LIBXL_LIST_HEAD(, libxl__ao_abortable) abortables; LIBXL_LIST_ENTRY(libxl__ao) inprogress_entry; libxl__gc gc; libxl_asyncop_how how; libxl__poller *poller; uint32_t domid; LIBXL_TAILQ_ENTRY(libxl__ao) entry_for_callback; }; #define LIBXL_INIT_GC(gc,ctx) do{ \ (gc).alloc_maxsize = 0; \ (gc).alloc_ptrs = 0; \ (gc).owner = (ctx); \ } while(0) /* NB, also, a gc struct ctx->nogc_gc is initialised in libxl_ctx_alloc */ static inline libxl_ctx *libxl__gc_owner(libxl__gc *gc) { return gc->owner; } static inline int libxl__gc_is_real(const libxl__gc *gc) { return gc->alloc_maxsize >= 0; } /* * Memory allocation tracking/helpers * * See comment "libxl memory management" in libxl.h for a description * of the framework which these calls belong to. * * These functions deal with memory allocations of type (a) and (d) in * that description. * * All pointers returned by these functions are registered for garbage * collection on exit from the outermost libxl callframe. * * However, where the argument is stated to be "gc_opt", &ctx->nogc_gc * may be passed instead, in which case no garbage collection will * occur; the pointer must later be freed with free(). (Passing NULL * for gc_opt is not permitted.) This is for memory allocations of * types (b) and (c). The convenience macro NOGC should be used where * possible. * * NOGC (and ctx->nogc_gc) may ONLY be used with functions which * explicitly declare that it's OK. Use with nonconsenting functions * may result in leaks of those functions' internal allocations on the * psuedo-gc. */ /* register ptr in gc for free on exit from outermost libxl callframe. */ #define NN1 __attribute__((nonnull(1))) /* It used to be legal to pass NULL for gc_opt. Get the compiler to * warn about this if any slip through. */ _hidden void libxl__ptr_add(libxl__gc *gc_opt, void *ptr /* may be NULL */) NN1; /* if this is the outermost libxl callframe then free all pointers in @gc */ _hidden void libxl__free_all(libxl__gc *gc); /* allocate @size bytes. (a gc'd malloc(3)) */ _hidden void *libxl__malloc(libxl__gc *gc_opt, size_t size) NN1; /* allocate and zero @size. (similar to a gc'd malloc(3)+memzero()) */ _hidden void *libxl__zalloc(libxl__gc *gc_opt, size_t size) NN1; /* allocate and zero memory for an array of @nmemb members of @size each. * (similar to a gc'd calloc(3)). */ _hidden void *libxl__calloc(libxl__gc *gc_opt, size_t nmemb, size_t size) NN1; /* change the size of the memory block pointed to by @ptr to @new_size bytes. * unlike other allocation functions here any additional space between the * oldsize and @new_size is not initialised (similar to a gc'd realloc(3)). * if @ptr is non-NULL and @gc_opt is not nogc_gc then @ptr must have been * registered with @gc_opt previously. */ _hidden void *libxl__realloc(libxl__gc *gc_opt, void *ptr, size_t new_size) NN1; /* print @fmt into an allocated string large enoughto contain the result. * (similar to gc'd asprintf(3)). */ _hidden char *libxl__sprintf(libxl__gc *gc_opt, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3) NN1; _hidden char *libxl__vsprintf(libxl__gc *gc, const char *format, va_list ap) PRINTF_ATTRIBUTE(2, 0); /* duplicate the string @c (similar to a gc'd strdup(3)). */ _hidden char *libxl__strdup(libxl__gc *gc_opt, const char *c /* may be NULL */) NN1; /* duplicate at most @n bytes of string @c (similar to a gc'd strndup(3)). */ _hidden char *libxl__strndup(libxl__gc *gc_opt, const char *c /* may be NULL */, size_t n) NN1; /* strip the last path component from @s and return as a newly allocated * string. (similar to a gc'd dirname(3)). */ _hidden char *libxl__dirname(libxl__gc *gc_opt, const char *s) NN1; /* Make a pipe and set both ends nonblocking. On error, nothing * is left open and both fds[]==-1, and a message is logged. * Useful for self-pipes. */ _hidden int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2]); /* Closes the pipe fd(s). Either or both of fds[] may be -1 meaning * `not open'. Ignores any errors. Sets fds[] to -1. */ _hidden void libxl__pipe_close(int fds[2]); /* Change the flags for the file description associated with fd to * (flags & mask) | val. * If r_oldflags != NULL then sets *r_oldflags to the original set of * flags. */ _hidden int libxl__fd_flags_modify_save(libxl__gc *gc, int fd, int mask, int val, int *r_oldflags); /* Restores the flags for the file description associated with fd to * to the previous value (returned by libxl__fd_flags_modify_save) */ _hidden int libxl__fd_flags_restore(libxl__gc *gc, int fd, int old_flags); /* Each of these logs errors and returns a libxl error code. * They do not mind if path is already removed. * For _file, path must not be a directory; for _directory it must be. */ _hidden int libxl__remove_file(libxl__gc *gc, const char *path); _hidden int libxl__remove_directory(libxl__gc *gc, const char *path); _hidden int libxl__remove_file_or_directory(libxl__gc *gc, const char *path); _hidden char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array); /* treats kvs as pairs of keys and values and writes each to dir. */ _hidden int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t, const char *dir, char **kvs); /* as writev but also sets the permissions on each path */ _hidden int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t, const char *dir, char *kvs[], struct xs_permissions *perms, unsigned int num_perms); /* _atonce creates a transaction and writes all keys at once */ _hidden int libxl__xs_writev_atonce(libxl__gc *gc, const char *dir, char **kvs); /* Each fn returns 0 on success. * On error: returns -1, sets errno (no logging) */ _hidden char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid); /* On error: logs, returns NULL, sets errno. */ _hidden char *libxl__xs_read(libxl__gc *gc, xs_transaction_t t, const char *path); _hidden char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t, const char *path, unsigned int *nb); /* On error: returns NULL, sets errno (no logging) */ _hidden char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid); _hidden int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path, libxl_domid *domid_out); /*----- "checked" xenstore access functions -----*/ /* Each of these functions will check that it succeeded; if it * fails it logs and returns ERROR_FAIL. */ int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t, const char *path, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(4, 0); int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t, const char *path, const char *fmt, ...) PRINTF_ATTRIBUTE(4, 5); /* On success, path will exist and will have an empty value */ int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t, const char *path, struct xs_permissions *perms, unsigned int num_perms); /* On success, *result_out came from the gc. * On error, *result_out is undefined. * ENOENT is regarded as error. */ int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t, const char *path, const char **result_out); /* On success, *result_out came from the gc. * On error, *result_out is undefined. * ENOENT counts as success but sets *result_out=0 */ int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t, const char *path, const char **result_out); /* Does not include a trailing null. * May usefully be combined with GCSPRINTF if the format string * behaviour of libxl__xs_printf is desirable. */ int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t, const char *path, const char *string); /* ENOENT is not an error (even if the parent directories don't exist) */ int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path); /* Transaction functions, best used together. * The caller should initialise *t to 0 (XBT_NULL) before calling start. * Each function leaves *t!=0 iff the transaction needs cleaning up. * * libxl__xs_transaction_commit returns: * <0 failure - a libxl error code * +1 commit conflict; transaction has been destroyed and caller * must go round again (call _start again and retry) * 0 committed successfully * * The intended usage pattern looks like this: * int some_function() * { * int rc; * xs_transaction_t t = 0; * // other initialisations * * // do whatever you need to do before the xenstore stuff * // errors? set rc and goto out. * * for (;;) { * rc = libxl__xs_transaction_start(gc, &t); * if (rc) goto out; * * // do your work here, including all xenstore reads and writes * // libxl__xs_*_checked are useful; pass them t. * // errors? set rc and goto out. * * rc = libxl__xs_transaction_commit(gc, &t); * if (!rc) break; * if (rc<0) goto out; * } * * // now the xenstore transaction succeeded * // do whatever else you need to do * // errors? set rc and goto out. * * return something; * * out: * // other cleanups * libxl__xs_transaction_abort(gc, &t); * // other cleanups * return rc; * } * * Formally the states of *t are: * * name value of *t description * Idle 0 no transaction exists * Ready non-0 ready for work, nothing done yet * Busy non-0 writes have been made but we are not finished * Uncommitted non-0 writes have been made and should be committed * * libxl__xs_transaction_start: Idle -> Ready (on error: Idle) * * The transaction goes from Ready to Busy, and from Busy to * Uncommitted, by the use of xenstore read and write operations * (libxl__xs_..., xs_...) made by libxl__xs_transaction's caller. * * libxl__xs_transaction_commit: Ready/Uncommitted -> Idle * on success (returns 0): xenstore has been updated * on error (<0) or conflict (+1): updates discarded * * libxl__xs_transaction_abort: Any -> Idle (any updates discarded) */ int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t); int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t); void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t); /* * This is a recursive delete, from top to bottom. What this function does * is remove empty folders that contained the deleted entry. * * It mimics xenstore-rm -t behaviour. */ _hidden int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t, const char *user_path); /* * Event generation functions provided by the libxl event core to the * rest of libxl. Implemented in terms of _beforepoll/_afterpoll * and/or the fd registration machinery, as provided by the * application. * * Semantics are similar to those of the fd and timeout registration * functions provided to libxl_osevent_register_hooks. * * Non-0 returns from libxl__ev_{modify,deregister} have already been * logged by the core and should be returned unmodified to libxl's * caller; NB that they may be valid libxl error codes but they may * also be positive numbers supplied by the caller. * * In each case, there is a libxl__ev_FOO structure which can be in * one of three states: * * Undefined - Might contain anything. All-bits-zero is * an undefined state. * * Idle - Struct contents are defined enough to pass to any * libxl__ev_FOO function but not registered and * callback will not be called. The struct does not * contain references to any allocated resources so * can be thrown away. * * Active - Request for events has been registered and events * may be generated. _deregister must be called to * reclaim resources. * * These functions are provided for each kind of event KIND: * * int libxl__ev_KIND_register(libxl__gc *gc, libxl__ev_KIND *GEN, * libxl__ev_KIND_callback *FUNC, * DETAILS); * On entry *GEN must be in state Undefined or Idle. * Returns a libxl error code; on error return *GEN is Idle. * On successful return *GEN is Active and FUNC wil be * called by the event machinery in future. FUNC will * not be called from within the call to _register. * FUNC will be called with the context locked (with CTX_LOCK). * * void libxl__ev_KIND_deregister(libxl__gc *gc, libxl__ev_KIND *GEN_upd); * On entry *GEN must be in state Active or Idle. * On return it is Idle. (Idempotent.) * * void libxl__ev_KIND_init(libxl__ev_KIND *GEN); * Provided for initialising an Undefined KIND. * On entry *GEN must be in state Idle or Undefined. * On return it is Idle. (Idempotent.) * * int libxl__ev_KIND_isregistered(const libxl__ev_KIND *GEN); * On entry *GEN must be Idle or Active. * Returns nonzero if it is Active, zero otherwise. * Cannot fail. * * int libxl__ev_KIND_modify(libxl__gc*, libxl__ev_KIND *GEN, * DETAILS); * Only provided for some kinds of generator. * On entry *GEN must be Active and on return, whether successful * or not, it will be Active. * Returns a libxl error code; on error the modification * is not effective. * * All of these functions are fully threadsafe and may be called by * general code in libxl even from within event callback FUNCs. * The ctx will be locked on entry to each FUNC and FUNC should not * unlock it. * * Callers of libxl__ev_KIND_register must ensure that the * registration is undone, with _deregister, in libxl_ctx_free. * This means that normally each kind of libxl__evgen (ie each * application-requested event source) needs to be on a list so that * it can be automatically deregistered as promised in libxl_event.h. */ _hidden int libxl__ev_fd_register(libxl__gc*, libxl__ev_fd *ev_out, libxl__ev_fd_callback*, int fd, short events /* as for poll(2) */); _hidden int libxl__ev_fd_modify(libxl__gc*, libxl__ev_fd *ev, short events); _hidden void libxl__ev_fd_deregister(libxl__gc*, libxl__ev_fd *ev); static inline void libxl__ev_fd_init(libxl__ev_fd *efd) { efd->fd = -1; } static inline int libxl__ev_fd_isregistered(const libxl__ev_fd *efd) { return efd->fd >= 0; } _hidden int libxl__ev_time_register_rel(libxl__ao*, libxl__ev_time *ev_out, libxl__ev_time_callback*, int milliseconds /* as for poll(2) */); _hidden int libxl__ev_time_register_abs(libxl__ao*, libxl__ev_time *ev_out, libxl__ev_time_callback*, struct timeval); _hidden int libxl__ev_time_modify_rel(libxl__gc*, libxl__ev_time *ev, int milliseconds /* as for poll(2) */); _hidden int libxl__ev_time_modify_abs(libxl__gc*, libxl__ev_time *ev, struct timeval); _hidden void libxl__ev_time_deregister(libxl__gc*, libxl__ev_time *ev); static inline void libxl__ev_time_init(libxl__ev_time *ev) { ev->func = 0; libxl__ao_abortable_init(&ev->abrt); } static inline int libxl__ev_time_isregistered(const libxl__ev_time *ev) { return !!ev->func; } _hidden int libxl__ev_xswatch_register(libxl__gc*, libxl__ev_xswatch *xsw_out, libxl__ev_xswatch_callback*, const char *path /* copied */); _hidden void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch*); static inline void libxl__ev_xswatch_init(libxl__ev_xswatch *xswatch_out) { xswatch_out->slotnum = -1; } static inline int libxl__ev_xswatch_isregistered(const libxl__ev_xswatch *xw) { return xw->slotnum >= 0; } /* * The evtchn facility is one-shot per call to libxl__ev_evtchn_wait. * You should: * Use libxl__ctx_evtchn_init to make sure CTX->xce is valid; * Call some suitable xc bind function on (or to obtain) the port; * Then call libxl__ev_evtchn_wait. * * When the event is signaled then the callback will be made, once. * Then you must call libxl__ev_evtchn_wait again, if desired. * * You must NOT call xenevtchn_unmask. wait will do that for you. * * Calling libxl__ev_evtchn_cancel will arrange for libxl to disregard * future occurrences of event. Both libxl__ev_evtchn_wait and * libxl__ev_evtchn_cancel are idempotent. * * (Note of course that an event channel becomes signaled when it is * first bound, so you will get one call to libxl__ev_evtchn_wait * "right away"; unless you have won a very fast race, the condition * you were waiting for won't exist yet so when you check for it * you'll find you need to call wait again.) * * You must not wait on the same port twice at once (that is, with * two separate libxl__ev_evtchn's). */ _hidden int libxl__ev_evtchn_wait(libxl__gc*, libxl__ev_evtchn *evev); _hidden void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev); static inline void libxl__ev_evtchn_init(libxl__ev_evtchn *evev) { evev->waiting = 0; } static inline bool libxl__ev_evtchn_iswaiting(const libxl__ev_evtchn *evev) { return evev->waiting; } _hidden int libxl__ctx_evtchn_init(libxl__gc *gc); /* for libxl_ctx_alloc */ /* * For making subprocesses. This is the only permitted mechanism for * code in libxl to do so. * * In the parent, returns the pid, filling in childw_out. * In the child, returns 0. * If it fails, returns a libxl error (all of which are -ve). * * The child should go on to exec (or exit) soon. The child may not * make any further calls to libxl infrastructure, except for memory * allocation and logging. If the child needs to use xenstore it * must open its own xs handle and use it directly, rather than via * the libxl event machinery. * * The parent may signal the child but it must not reap it. That will * be done by the event machinery. * * It is not possible to "deregister" the child death event source. * It will generate exactly one event callback; until then the childw * is Active and may not be reused. */ _hidden pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *childw_out, libxl__ev_child_callback *death); static inline void libxl__ev_child_init(libxl__ev_child *childw_out) { childw_out->pid = -1; } static inline int libxl__ev_child_inuse(const libxl__ev_child *childw_out) { return childw_out->pid >= 0; } /* Useable (only) in the child to once more make the ctx useable for * xenstore operations. logs failure in the form "what: ". */ _hidden int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what); /* * Other event-handling support provided by the libxl event core to * the rest of libxl. */ _hidden void libxl__event_occurred(libxl__egc*, libxl_event *event); /* Arranges to notify the application that the event has occurred. * event should be suitable for passing to libxl_event_free. */ _hidden libxl_event *libxl__event_new(libxl__egc*, libxl_event_type, uint32_t domid, libxl_ev_user for_user); /* Convenience function. * Allocates a new libxl_event, fills in domid and type. * Cannot fail. */ #define NEW_EVENT(egc, type, domid, user) \ libxl__event_new((egc), LIBXL_EVENT_TYPE_##type, (domid), (user)) /* Convenience macro. */ /* * In general, call this via the macro LIBXL__EVENT_DISASTER. * * Event-generating functions may call this if they might have wanted * to generate an event (either an internal one ie a * libxl__ev_FOO_callback or an application event), but are prevented * from doing so due to eg lack of memory. * * NB that this function may return and the caller isn't supposed to * then crash, although it may fail (and henceforth leave things in a * state where many or all calls fail). */ _hidden void libxl__event_disaster(libxl__egc*, const char *msg, int errnoval, libxl_event_type type /* may be 0 */, const char *file, int line, const char *func); #define LIBXL__EVENT_DISASTER(egc, msg, errnoval, type) \ libxl__event_disaster(egc, msg, errnoval, type, __FILE__,__LINE__,__func__) /* Fills in, or disposes of, the resources held by, a poller whose * space the caller has allocated. ctx must be locked. */ _hidden int libxl__poller_init(libxl__gc *gc, libxl__poller *p); _hidden void libxl__poller_dispose(libxl__poller *p); /* Obtain a fresh poller from malloc or the idle list, and put it * away again afterwards. _get can fail, returning NULL. * ctx must be locked. */ _hidden libxl__poller *libxl__poller_get(libxl__gc *gc); _hidden void libxl__poller_put(libxl_ctx*, libxl__poller *p /* may be NULL */); /* Notifies whoever is polling using p that they should wake up. * ctx must be locked. */ _hidden void libxl__poller_wakeup(libxl__egc *egc, libxl__poller *p); /* Internal to fork and child reaping machinery */ extern const libxl_childproc_hooks libxl__childproc_default_hooks; int libxl__sigchld_needed(libxl__gc*); /* non-reentrant idempotent, logs errs */ void libxl__sigchld_notneeded(libxl__gc*); /* non-reentrant idempotent */ void libxl__sigchld_check_stale_handler(void); int libxl__self_pipe_wakeup(int fd); /* returns 0 or -1 setting errno */ int libxl__self_pipe_eatall(int fd); /* returns 0 or -1 setting errno */ _hidden int libxl__atfork_init(libxl_ctx *ctx); /* File references */ typedef struct { /* * Path is always set if the file reference is valid. However if * mapped is true then the actual file may already be unlinked. */ const char * path; int mapped; void * data; size_t size; } libxl__file_reference; _hidden int libxl__file_reference_map(libxl__file_reference *f); _hidden int libxl__file_reference_unmap(libxl__file_reference *f); /* from xl_dom */ _hidden libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid); _hidden int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid); _hidden libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid); _hidden int libxl__sched_set_params(libxl__gc *gc, uint32_t domid, libxl_domain_sched_params *scparams); _hidden int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid, libxl_domain_config *const d_config); typedef struct { uint32_t store_port; uint32_t store_domid; unsigned long store_mfn; uint32_t console_port; uint32_t console_domid; unsigned long console_mfn; char *console_tty; char *saved_state; libxl__file_reference pv_kernel; libxl__file_reference pv_ramdisk; const char * pv_cmdline; xen_vmemrange_t *vmemranges; uint32_t num_vmemranges; xc_domain_configuration_t config; } libxl__domain_build_state; _hidden int libxl__build_pre(libxl__gc *gc, uint32_t domid, libxl_domain_config * const d_config, libxl__domain_build_state *state); _hidden int libxl__build_post(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *info, libxl__domain_build_state *state, char **vms_ents, char **local_ents); _hidden int libxl__build_pv(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *info, libxl__domain_build_state *state); _hidden int libxl__build_hvm(libxl__gc *gc, uint32_t domid, libxl_domain_config *d_config, libxl__domain_build_state *state); _hidden int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid, const char *cmd); _hidden int libxl__domain_rename(libxl__gc *gc, uint32_t domid, const char *old_name, const char *new_name, xs_transaction_t trans); _hidden int libxl__domain_resume_device_model(libxl__gc *gc, uint32_t domid); _hidden const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid, const char *userdata_userid, const char *wh); _hidden void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid); /* Caller must hold userdata store lock before calling * libxl__userdata_{retrieve,store} * See libxl__{un,}lock_domain_userdata. */ _hidden int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid, const char *userdata_userid, uint8_t **data_r, int *datalen_r); _hidden int libxl__userdata_store(libxl__gc *gc, uint32_t domid, const char *userdata_userid, const uint8_t *data, int datalen); _hidden int libxl__domain_resume(libxl__gc *gc, uint32_t domid, int suspend_cancel); /* returns 0 or 1, or a libxl error code */ _hidden int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid); _hidden const char *libxl__domain_pvcontrol_xspath(libxl__gc*, uint32_t domid); _hidden char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t, uint32_t domid); _hidden int libxl__domain_pvcontrol_write(libxl__gc *gc, xs_transaction_t t, uint32_t domid, const char *cmd); /* from xl_device */ _hidden char *libxl__device_disk_string_of_backend(libxl_disk_backend backend); _hidden char *libxl__device_disk_string_of_format(libxl_disk_format format); _hidden int libxl__device_disk_set_backend(libxl__gc*, libxl_device_disk*); _hidden int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor); _hidden int libxl__device_disk_dev_number(const char *virtpath, int *pdisk, int *ppartition); _hidden char *libxl__devid_to_vdev(libxl__gc *gc, int devid); _hidden int libxl__device_console_add(libxl__gc *gc, uint32_t domid, libxl__device_console *console, libxl__domain_build_state *state, libxl__device *device); /* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */ _hidden int libxl__device_exists(libxl__gc *gc, xs_transaction_t t, libxl__device *device); _hidden int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t, libxl__device *device, char **bents, char **fents, char **ro_fents); _hidden char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device); _hidden char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device); _hidden int libxl__parse_backend_path(libxl__gc *gc, const char *path, libxl__device *dev); _hidden int libxl__device_destroy(libxl__gc *gc, libxl__device *dev); _hidden int libxl__wait_for_backend(libxl__gc *gc, const char *be_path, const char *state); _hidden int libxl__nic_type(libxl__gc *gc, libxl__device *dev, libxl_nic_type *nictype); _hidden int libxl__init_console_from_channel(libxl__gc *gc, libxl__device_console *console, int dev_num, libxl_device_channel *channel); _hidden int libxl__device_nextid(libxl__gc *gc, uint32_t domid, char *device); _hidden int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid); /* * For each aggregate type which can be used as an input we provide: * * int libxl___setdefault(gc, *p): * * Idempotently sets any members of "p" which is currently set to * a special value indicating that the defaults should be used * (per libxl__init) to a specific value. * * All libxl API functions are expected to have arranged for this * to be called before using any values within these structures. */ _hidden int libxl__domain_create_info_setdefault(libxl__gc *gc, libxl_domain_create_info *c_info); _hidden int libxl__domain_build_info_setdefault(libxl__gc *gc, libxl_domain_build_info *b_info); _hidden int libxl__device_disk_setdefault(libxl__gc *gc, libxl_device_disk *disk, uint32_t domid); _hidden int libxl__device_nic_setdefault(libxl__gc *gc, libxl_device_nic *nic, uint32_t domid, bool hotplug); _hidden int libxl__device_vfb_setdefault(libxl__gc *gc, libxl_device_vfb *vfb); _hidden int libxl__device_vkb_setdefault(libxl__gc *gc, libxl_device_vkb *vkb); _hidden int libxl__device_pci_setdefault(libxl__gc *gc, libxl_device_pci *pci); _hidden void libxl__rdm_setdefault(libxl__gc *gc, libxl_domain_build_info *b_info); _hidden int libxl__device_p9_setdefault(libxl__gc *gc, libxl_device_p9 *p9); _hidden const char *libxl__device_nic_devname(libxl__gc *gc, uint32_t domid, uint32_t devid, libxl_nic_type type); _hidden int libxl__get_domid(libxl__gc *gc, uint32_t *domid); /*----- xswait: wait for a xenstore node to be suitable -----*/ typedef struct libxl__xswait_state libxl__xswait_state; /* * rc describes the circumstances of this callback: * * rc==0 * * The xenstore path (may have) changed. It has been read for * you. The result is in data (allocated from the ao gc). * data may be NULL, which means that the xenstore read gave * ENOENT. * * If you are satisfied, you MUST call libxl__xswait_stop. * Otherwise, xswait will continue waiting and watching and * will call you back later. * * rc==ERROR_TIMEDOUT, rc==ERROR_ABORTED * * The specified timeout was reached. * This has NOT been logged (except to the debug log). * xswait will not continue (but calling libxl__xswait_stop is OK). * * rc!=0, !=ERROR_TIMEDOUT, !=ERROR_ABORTED * * Some other error occurred. * This HAS been logged. * xswait will not continue (but calling libxl__xswait_stop is OK). * * xswait.path may start with with '@', in which case no read is done * and the callback will always get data==0. */ typedef void libxl__xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa, int rc, const char *data); struct libxl__xswait_state { /* caller must fill these in, and they must all remain valid */ libxl__ao *ao; const char *what; /* for error msgs: noun phrase, what we're waiting for */ const char *path; int timeout_ms; /* as for poll(2) */ libxl__xswait_callback *callback; /* remaining fields are private to xswait */ libxl__ev_time time_ev; libxl__ev_xswatch watch_ev; }; void libxl__xswait_init(libxl__xswait_state*); void libxl__xswait_stop(libxl__gc*, libxl__xswait_state*); /*idempotent*/ bool libxl__xswait_inuse(const libxl__xswait_state *ss); int libxl__xswait_start(libxl__gc*, libxl__xswait_state*); /* * libxl__ev_devstate - waits a given time for a device to * reach a given state. Follows the libxl_ev_* conventions. * Will generate only one event, and after that is automatically * cancelled. */ typedef struct libxl__ev_devstate libxl__ev_devstate; typedef void libxl__ev_devstate_callback(libxl__egc *egc, libxl__ev_devstate*, int rc); /* rc will be 0, ERROR_TIMEDOUT, ERROR_ABORTED, ERROR_INVAL * (meaning path was removed), or ERROR_FAIL if other stuff went * wrong (in which latter case, logged) */ struct libxl__ev_devstate { /* read-only for caller, who may read only when waiting: */ int wanted; libxl__ev_devstate_callback *callback; /* as for the remainder, read-only public parts may also be * read by the caller (notably, watch.path), but only when waiting: */ libxl__xswait_state w; }; static inline void libxl__ev_devstate_init(libxl__ev_devstate *ds) { libxl__xswait_init(&ds->w); } static inline void libxl__ev_devstate_cancel(libxl__gc *gc, libxl__ev_devstate *ds) { libxl__xswait_stop(gc,&ds->w); } _hidden int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds, libxl__ev_devstate_callback cb, const char *state_path, int state, int milliseconds); /* * libxl__ev_domaindeathcheck_register - arranges to call back (once) * if the domain is destroyed. If the domain dies, we log a message * of the form ": ". */ typedef struct libxl__domaindeathcheck libxl__domaindeathcheck; typedef void libxl___domaindeathcheck_callback(libxl__egc *egc, libxl__domaindeathcheck*, int rc /* DESTROYED or ABORTED */); struct libxl__domaindeathcheck { /* must be filled in by caller, and remain valid: */ const char *what; uint32_t domid; libxl___domaindeathcheck_callback *callback; /* private */ libxl__ao_abortable abrt; libxl__ev_xswatch watch; }; _hidden int libxl__domaindeathcheck_start(libxl__ao *ao, libxl__domaindeathcheck *dc); void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc); void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc); /* * libxl__try_phy_backend - Check if there's support for the passed * type of file using the PHY backend * st_mode: mode_t of the file, as returned by stat function * * Returns 1 on success, and 0 if not suitable for phy backend. */ _hidden int libxl__try_phy_backend(mode_t st_mode); _hidden char *libxl__devid_to_localdev(libxl__gc *gc, int devid); _hidden int libxl__pci_numdevs(libxl__gc *gc); _hidden int libxl__pci_topology_init(libxl__gc *gc, physdev_pci_device_t *devs, int num_devs); /* from libxl_pci */ _hidden int libxl__device_pci_add(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int starting); _hidden int libxl__create_pci_backend(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int num); _hidden int libxl__device_pci_destroy_all(libxl__gc *gc, uint32_t domid); _hidden bool libxl__is_igd_vga_passthru(libxl__gc *gc, const libxl_domain_config *d_config); /* from libxl_dtdev */ _hidden int libxl__device_dt_add(libxl__gc *gc, uint32_t domid, const libxl_device_dtdev *dtdev); /* *----- spawn ----- * * Higher-level double-fork and separate detach eg as for device models * * Each libxl__spawn_state is in one of these states * Undefined, Idle, Attached, Detaching */ typedef struct libxl__obsolete_spawn_starting libxl__spawn_starting; /* this type is never defined, so no objects of this type exist * fixme-ao This should go away completely. */ typedef struct libxl__spawn_state libxl__spawn_state; /* Clears out a spawn state; idempotent. */ _hidden void libxl__spawn_init(libxl__spawn_state*); /* * libxl__spawn_spawn - Create a new process which will become daemonic * Forks twice, to allow the child to detach entirely from the parent. * * We call the two generated processes the "middle child" (result of * the first fork) and the "inner child" (result of the second fork * which takes place in the middle child). * * The inner child must soon exit or exec. It must also soon exit or * notify the parent of its successful startup by writing to the * xenstore path xspath. * * The user (in the parent) will be called back (confirm_cb) every * time that xenstore path is modified. * * In both children, the ctx is not fully usable: gc and logging * operations are OK, but operations on Xen and xenstore are not. * (The restrictions are the same as those which apply to children * made with libxl__ev_child_fork.) * * midproc_cb will be called in the middle child, with the pid of the * inner child; this could for example record the pid. midproc_cb * should be fast, and should return. It will be called (reentrantly) * within libxl__spawn_init. * * failure_cb will be called in the parent on failure of the * intermediate or final child; an error message will have been * logged. * * confirm_cb, failure_cb and detached_cb will not be called * reentrantly from within libxl__spawn_spawn. * * what: string describing the spawned process, used for logging * * Logs errors. A copy of "what" is taken. * Return values: * < 0 error, *spawn is now Idle and need not be detached * +1 caller is the parent, *spawn is Attached and must be detached * 0 caller is now the inner child, should probably call libxl__exec * * The spawn state must be Undefined or Idle on entry. */ _hidden int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *spawn); /* * libxl__spawn_request_detach - Detaches the daemonic child. * * Works by killing the intermediate process from spawn_spawn. * After this function returns, failures of either child are no * longer reported via failure_cb. * * This is not synchronous: there will be a further callback when * the detach is complete. * * If called before the inner child has been created, this may prevent * it from running at all. Thus this should be called only when the * inner child has notified that it is ready. Normally it will be * called from within confirm_cb. * * Logs errors. * * The spawn state must be Attached entry and will be Detaching * on return. */ _hidden void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state*); /* * If successful, this should return 0. * * Otherwise it should return a signal number, which will be * sent to the inner child; the overall spawn will then fail. */ typedef int /* signal number */ libxl__spawn_midproc_cb(libxl__gc*, libxl__spawn_state*, pid_t inner); /* * Called if the spawn failed. The reason will have been logged. * The spawn state will be Idle on entry to the callback (and * it may be reused immediately if desired). */ typedef void libxl__spawn_failure_cb(libxl__egc*, libxl__spawn_state*, int rc); /* * Called when the xspath watch triggers. xspath will have been read * and the result placed in xsdata; if that failed because the key * didn't exist, xspath==0. (If it failed for some other reason, * the spawn machinery calls failure_cb instead.) * * If the child has indicated its successful startup, or a failure * has occurred, this should call libxl__spawn_detach. * * If the child is still starting up, should simply return, doing * nothing. * * The spawn state will be Active on entry to the callback; there * are no restrictions on the state on return; it may even have * been detached and reused. */ typedef void libxl__spawn_confirm_cb(libxl__egc*, libxl__spawn_state*, const char *xsdata); /* * Called when the detach (requested by libxl__spawn_initiate_detach) has * completed. On entry to the callback the spawn state is Idle. */ typedef void libxl__spawn_detached_cb(libxl__egc*, libxl__spawn_state*); struct libxl__spawn_state { /* must be filled in by user and remain valid */ libxl__ao *ao; const char *what; const char *xspath; const char *pidpath; /* only used by libxl__spawn_midproc_record_pid */ int timeout_ms; /* -1 means forever */ libxl__spawn_midproc_cb *midproc_cb; libxl__spawn_failure_cb *failure_cb; libxl__spawn_confirm_cb *confirm_cb; libxl__spawn_detached_cb *detached_cb; /* remaining fields are private to libxl_spawn_... */ int detaching; /* we are in Detaching */ int rc; /* might be non-0 whenever we are not Idle */ libxl__ev_child mid; /* always in use whenever we are not Idle */ libxl__xswait_state xswait; }; static inline int libxl__spawn_inuse(const libxl__spawn_state *ss) { return libxl__ev_child_inuse(&ss->mid); } /* * libxl_spawner_record_pid - Record given pid in xenstore * * This function can be passed directly as an intermediate_hook to * libxl__spawn_spawn. On failure, returns the value SIGTERM. */ _hidden int libxl__spawn_record_pid(libxl__gc*, libxl__spawn_state*, pid_t innerchild); /* * libxl__xenstore_child_wait_deprecated - Wait for daemonic child IPC * * This is a NOT function for waiting for ordinary child processes. * If you want to run (fork/exec/wait) subprocesses from libxl: * - Make your libxl entrypoint use the ao machinery * - Use libxl__ev_child_fork, and use the callback programming style * * This function is intended for interprocess communication with a * service process. If the service process does not respond quickly, * the whole caller may be blocked. Therefore this function is * deprecated. This function is currently used only by * libxl__wait_for_device_model_deprecated. * * gc: allocation pool * domid: guest to work with * timeout: how many seconds to wait for the state to appear * what: string describing the spawned process * path: path to the state file in xenstore * state: expected string to wait for in path (optional) * spawning: malloc'd pointer to libxl__spawn_starting (optional) * check_callback: (optional) * check_callback_userdata: data to pass to the callback function * * Returns 0 on success, and < 0 on error. * * This function waits the given timeout for the given path to appear * in xenstore, and optionally for state in path. * If path appears and state matches, check_callback is called. * If check_callback returns > 0, waiting for path or state continues. * Otherwise libxl__xenstore_child_wait_deprecated returns. */ _hidden int libxl__xenstore_child_wait_deprecated(libxl__gc *gc, uint32_t domid, uint32_t timeout, char *what, char *path, char *state, libxl__spawn_starting *spawning, int (*check_callback)(libxl__gc *gc, uint32_t domid, const char *state, void *userdata), void *check_callback_userdata); /* low-level stuff, for synchronous subprocesses etc. */ /* * env should be passed using the following format, * * env[0]: name of env variable * env[1]: value of env variable * env[n]: ... * * So it efectively becomes something like: * export env[n]=env[n+1] * (where n%2 = 0) * * The last entry of the array always has to be NULL. * * stdinfd, stdoutfd, stderrfd will be dup2'd onto the corresponding * fd in the child, if they are not -1. The original copy of the * descriptor will be closed in the child (unless it's 0, 1 or 2 * ie the source descriptor is itself stdin, stdout or stderr). * * Logs errors, never returns. */ _hidden void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd, int stderrfd, const char *arg0, char *const args[], char *const env[]) __attribute__((noreturn)); /* from xl_create */ /* on entry, libxl_domid_valid_guest(domid) must be false; * on exit (even error exit), domid may be valid and refer to a domain */ _hidden int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config, uint32_t *domid, xc_domain_configuration_t *xc_config); _hidden int libxl__domain_build(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid, libxl__domain_build_state *state); /* for device model creation */ _hidden const char *libxl__domain_device_model(libxl__gc *gc, const libxl_domain_build_info *info); _hidden int libxl__need_xenpv_qemu(libxl__gc *gc, libxl_domain_config *d_config); _hidden bool libxl__query_qemu_backend(libxl__gc *gc, uint32_t domid, uint32_t backend_id, const char *type, bool def); _hidden int libxl__dm_active(libxl__gc *gc, uint32_t domid); _hidden int libxl__dm_check_start(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid); /* * This function will fix reserved device memory conflict * according to user's configuration. */ _hidden int libxl__domain_device_construct_rdm(libxl__gc *gc, libxl_domain_config *d_config, uint64_t rdm_mem_guard, struct xc_dom_image *dom); /* * This function will cause the whole libxl process to hang * if the device model does not respond. It is deprecated. * * Instead of calling this function: * - Make your libxl entrypoint use the ao machinery * - Use libxl__ev_xswatch_register, and use the callback programming * style */ _hidden int libxl__wait_for_device_model_deprecated(libxl__gc *gc, uint32_t domid, char *state, libxl__spawn_starting *spawning /* NULL allowed */, int (*check_callback)(libxl__gc *gc, uint32_t domid, const char *state, void *userdata), void *check_callback_userdata); _hidden int libxl__destroy_device_model(libxl__gc *gc, uint32_t domid); _hidden const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *g_cfg); _hidden char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path); #define LIBXL__LOG_DEBUG XTL_DEBUG #define LIBXL__LOG_VERBOSE XTL_VERBOSE #define LIBXL__LOG_INFO XTL_INFO #define LIBXL__LOG_WARNING XTL_WARN #define LIBXL__LOG_ERROR XTL_ERROR _hidden char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid); _hidden char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid); _hidden int libxl__enum_from_string(const libxl_enum_string_table *t, const char *s, int *e); _hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str); _hidden yajl_gen_status libxl__string_gen_json(yajl_gen hand, const char *p); typedef yajl_gen_status (*libxl__gen_json_callback)(yajl_gen hand, void *); _hidden char *libxl__object_to_json(libxl_ctx *ctx, const char *type, libxl__gen_json_callback gen, void *p); /* holds the CPUID response for a single CPUID leaf * input contains the value of the EAX and ECX register, * and each policy string contains a filter to apply to * the host given values for that particular leaf. */ struct libxl__cpuid_policy { uint32_t input[2]; char *policy[4]; }; /* * blktap2 support */ /* libxl__blktap_enabled: * return true if blktap/blktap2 support is available. */ _hidden int libxl__blktap_enabled(libxl__gc *gc); /* libxl__blktap_devpath: * Argument: path and disk image as specified in config file. * The type specifies whether this is aio, qcow, qcow2, etc. * returns device path xenstore wants to have. returns NULL * if no device corresponds to the disk. */ _hidden char *libxl__blktap_devpath(libxl__gc *gc, const char *disk, libxl_disk_format format); /* libxl__device_destroy_tapdisk: * Destroys any tapdisk process associated with the backend represented * by be_path. * Always logs on failure. */ _hidden int libxl__device_destroy_tapdisk(libxl__gc *gc, const char *params); _hidden int libxl__device_from_disk(libxl__gc *gc, uint32_t domid, const libxl_device_disk *disk, libxl__device *device); /* Calls poll() again - useful to check whether a signaled condition * is still true. Cannot fail. Returns currently-true revents. */ _hidden short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events); _hidden char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid); struct libxl__xen_console_reader { char *buffer; unsigned int size; unsigned int count; unsigned int clear; unsigned int incremental; unsigned int index; }; /* parse the string @s as a sequence of 6 colon separated bytes in to @mac */ _hidden int libxl__parse_mac(const char *s, libxl_mac mac); /* compare mac address @a and @b. 0 if the same, -ve if ab */ _hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b); /* return true if mac address is all zero (the default value) */ _hidden int libxl__mac_is_default(libxl_mac *mac); /* init a recursive mutex */ _hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock); _hidden int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r); #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) /* from libxl_qmp */ typedef struct libxl__qmp_handler libxl__qmp_handler; /* Initialise and connect to the QMP socket. * Return an handler or NULL if there is an error */ _hidden libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, uint32_t domid); _hidden int libxl__qmp_run_command_flexarray(libxl__gc *gc, int domid, const char *cmd, flexarray_t *array); /* ask to QEMU the serial port information and store it in xenstore. */ _hidden int libxl__qmp_query_serial(libxl__qmp_handler *qmp); _hidden int libxl__qmp_pci_add(libxl__gc *gc, int d, libxl_device_pci *pcidev); _hidden int libxl__qmp_pci_del(libxl__gc *gc, int domid, libxl_device_pci *pcidev); /* Resume hvm domain */ _hidden int libxl__qmp_system_wakeup(libxl__gc *gc, int domid); /* Suspend QEMU. */ _hidden int libxl__qmp_stop(libxl__gc *gc, int domid); /* Resume QEMU. */ _hidden int libxl__qmp_resume(libxl__gc *gc, int domid); /* Save current QEMU state into fd. */ _hidden int libxl__qmp_save(libxl__gc *gc, int domid, const char *filename); /* Load current QEMU state from file. */ _hidden int libxl__qmp_restore(libxl__gc *gc, int domid, const char *filename); /* Set dirty bitmap logging status */ _hidden int libxl__qmp_set_global_dirty_log(libxl__gc *gc, int domid, bool enable); _hidden int libxl__qmp_insert_cdrom(libxl__gc *gc, int domid, const libxl_device_disk *disk); /* Add a virtual CPU */ _hidden int libxl__qmp_cpu_add(libxl__gc *gc, int domid, int index); /* Query the bitmap of CPUs */ _hidden int libxl__qmp_query_cpus(libxl__gc *gc, int domid, libxl_bitmap *map); /* Start NBD server */ _hidden int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid, const char *host, const char *port); /* Add a disk to NBD server */ _hidden int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid, const char *disk); /* Start replication */ _hidden int libxl__qmp_start_replication(libxl__gc *gc, int domid, bool primary); /* Get replication error that occurs when the vm is running */ _hidden int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid); /* Do checkpoint */ _hidden int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid); /* Stop replication */ _hidden int libxl__qmp_stop_replication(libxl__gc *gc, int domid, bool primary); /* Stop NBD server */ _hidden int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid); /* Add or remove a child to/from quorum */ _hidden int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid, const char *parant, const char *child, const char *node); /* run a hmp command in qmp mode */ _hidden int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line, char **out); /* close and free the QMP handler */ _hidden void libxl__qmp_close(libxl__qmp_handler *qmp); /* remove the socket file, if the file has already been removed, * nothing happen */ _hidden void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid); /* this helper calls qmp_initialize, query_serial and qmp_close */ _hidden int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid, const libxl_domain_config *guest_config); /* on failure, logs */ int libxl__sendmsg_fds(libxl__gc *gc, int carrier, const void *data, size_t datalen, int nfds, const int fds[], const char *what); /* Insists on receiving exactly nfds and datalen. On failure, logs * and leaves *fds untouched. */ int libxl__recvmsg_fds(libxl__gc *gc, int carrier, void *databuf, size_t datalen, int nfds, int fds[], const char *what); /* from libxl_json */ #include _hidden yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str); _hidden yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str); typedef enum { JSON_NULL = (1 << 0), JSON_BOOL = (1 << 1), JSON_INTEGER = (1 << 2), JSON_DOUBLE = (1 << 3), /* number is store in string, it's too big to be a long long or a double */ JSON_NUMBER = (1 << 4), JSON_STRING = (1 << 5), JSON_MAP = (1 << 6), JSON_ARRAY = (1 << 7), JSON_ANY = 255 /* this is a mask of all values above, adjust as needed */ } libxl__json_node_type; typedef struct libxl__json_object { libxl__json_node_type type; union { bool b; long long i; double d; char *string; /* List of libxl__json_object */ flexarray_t *array; /* List of libxl__json_map_node */ flexarray_t *map; } u; struct libxl__json_object *parent; } libxl__json_object; typedef int (*libxl__json_parse_callback)(libxl__gc *gc, libxl__json_object *o, void *p); _hidden int libxl__object_from_json(libxl_ctx *ctx, const char *type, libxl__json_parse_callback parse, void *p, const char *s); typedef struct { char *map_key; libxl__json_object *obj; } libxl__json_map_node; typedef struct libxl__yajl_ctx libxl__yajl_ctx; static inline bool libxl__json_object_is_null(const libxl__json_object *o) { return o != NULL && o->type == JSON_NULL; } static inline bool libxl__json_object_is_bool(const libxl__json_object *o) { return o != NULL && o->type == JSON_BOOL; } static inline bool libxl__json_object_is_string(const libxl__json_object *o) { return o != NULL && o->type == JSON_STRING; } static inline bool libxl__json_object_is_integer(const libxl__json_object *o) { return o != NULL && o->type == JSON_INTEGER; } static inline bool libxl__json_object_is_double(const libxl__json_object *o) { return o != NULL && o->type == JSON_DOUBLE; } static inline bool libxl__json_object_is_number(const libxl__json_object *o) { return o != NULL && o->type == JSON_NUMBER; } static inline bool libxl__json_object_is_map(const libxl__json_object *o) { return o != NULL && o->type == JSON_MAP; } static inline bool libxl__json_object_is_array(const libxl__json_object *o) { return o != NULL && o->type == JSON_ARRAY; } static inline bool libxl__json_object_get_bool(const libxl__json_object *o) { if (libxl__json_object_is_bool(o)) return o->u.b; else return false; } static inline const char *libxl__json_object_get_string(const libxl__json_object *o) { if (libxl__json_object_is_string(o)) return o->u.string; else return NULL; } static inline const char *libxl__json_object_get_number(const libxl__json_object *o) { if (libxl__json_object_is_number(o)) return o->u.string; else return NULL; } static inline flexarray_t *libxl__json_object_get_map(const libxl__json_object *o) { if (libxl__json_object_is_map(o)) return o->u.map; else return NULL; } static inline flexarray_t *libxl__json_object_get_array(const libxl__json_object *o) { if (libxl__json_object_is_array(o)) return o->u.array; else return NULL; } static inline long long libxl__json_object_get_integer(const libxl__json_object *o) { if (libxl__json_object_is_integer(o)) return o->u.i; else return -1; } /* * NOGC can be used with those json_object functions, but the * libxl__json_object* will need to be freed with libxl__json_object_free. */ _hidden libxl__json_object *libxl__json_object_alloc(libxl__gc *gc_opt, libxl__json_node_type type); _hidden int libxl__json_object_append_to(libxl__gc *gc_opt, libxl__json_object *obj, libxl__yajl_ctx *ctx); _hidden libxl__json_object *libxl__json_array_get(const libxl__json_object *o, int i); _hidden libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o, int i); _hidden const libxl__json_object *libxl__json_map_get(const char *key, const libxl__json_object *o, libxl__json_node_type expected_type); _hidden yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc_opt, yajl_gen hand, libxl__json_object *param); _hidden void libxl__json_object_free(libxl__gc *gc_opt, libxl__json_object *obj); _hidden libxl__json_object *libxl__json_parse(libxl__gc *gc_opt, const char *s); /* Based on /local/domain/$domid/dm-version xenstore key * default is qemu xen traditional */ _hidden int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid); /* Return the system-wide default device model */ _hidden libxl_device_model_version libxl__default_device_model(libxl__gc *gc); #define DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, fmt, _a...) \ libxl__sprintf(gc, "/local/domain/%u/device-model/%u" fmt, dm_domid, \ domid, ##_a) /* * Calling context and GC for event-generating functions: * * These are for use by parts of libxl which directly or indirectly * call libxl__event_occurred. These contain a gc but also a list of * deferred events. * * You should never need to initialise an egc unless you are part of * the event machinery itself. Otherwise you will always be given an * egc if you need one. Even functions which generate specific kinds * of events don't need to - rather, they will be passed an egc into * their own callback function and should just use the one they're * given. * * Functions using LIBXL_INIT_EGC may *not* generally be called from * within libxl, because libxl__egc_cleanup may call back into the * application. This should be enforced by declaring all such * functions in libxl.h or libxl_event.h with * LIBXL_EXTERNAL_CALLERS_ONLY. You should in any case not find it * necessary to call egc-creators from within libxl. * * The callbacks must all take place with the ctx unlocked because * the application is entitled to reenter libxl from them. This * would be bad not because the lock is not recursive (it is) but * because the application might make blocking libxl calls which * would hold the lock unreasonably long. * * For the same reason libxl__egc_cleanup (or EGC_FREE) must be called * with the ctx *unlocked*. So the right pattern has the EGC_... * macro calls on the outside of the CTX_... ones. */ /* useful for all functions which take an egc: */ #define EGC_GC \ libxl__gc *const gc __attribute__((unused)) = &egc->gc /* egc initialisation and destruction: */ #define LIBXL_INIT_EGC(egc,ctx) do{ \ LIBXL_INIT_GC((egc).gc,ctx); \ LIBXL_TAILQ_INIT(&(egc).occurred_for_callback); \ LIBXL_TAILQ_INIT(&(egc).aos_for_callback); \ LIBXL_TAILQ_INIT(&(egc).aops_for_callback); \ } while(0) _hidden void libxl__egc_cleanup(libxl__egc *egc); /* Frees memory allocated within this egc's gc, and and report all * occurred events via callback, if applicable. May reenter the * application; see restrictions above. The ctx must be UNLOCKED. */ /* convenience macros: */ #define EGC_INIT(ctx) \ libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx); \ EGC_GC #define EGC_FREE libxl__egc_cleanup(egc) /* * Machinery for asynchronous operations ("ao") * * All "slow" functions (see below for the exact definition) need to * use the asynchronous operation ("ao") machinery. The function * should take a parameter const libxl_asyncop_how *ao_how and must * start with a call to AO_CREATE or equivalent. These functions MAY * NOT be called from inside libxl (regardless of what is passed for * ao_how), because they can cause reentrancy hazards due to * callbacks. * * For the same reason functions taking an ao_how may make themselves * an egc with EGC_INIT (and they will generally want to, to be able * to immediately complete an ao during its setup). * * * "Slow" functions includes any that might block on a guest or an * external script. More broadly, it includes any operations which * are sufficiently slow that an application might reasonably want to * initiate them, and then carry on doing something else, while the * operation completes. That is, a "fast" function must be fast * enough that we do not mind blocking all other management operations * on the same host while it completes. * * There are certain primitive functions which make a libxl operation * necessarily "slow" for API reasons. These are: * - awaiting xenstore watches (although read-modify-write xenstore * transactions are OK for fast functions) * - spawning subprocesses * - anything with a timeout * * * Lifecycle of an ao: * * - Created by libxl__ao_create (or the AO_CREATE convenience macro). * * - After creation, can be used by code which implements * the operation as follows: * - the ao's gc, for allocating memory for the lifetime * of the operation (possibly with the help of the AO_GC * macro to introduce the gc into scope) * - the ao itself may be passed about to sub-functions * so that they can stash it away etc. * - in particular, the ao pointer must be stashed in some * per-operation structure which is also passed as a user * pointer to the internal event generation request routines * libxl__evgen_FOO, so that at some point a CALLBACK will be * made when the operation is complete. * - if the operation provides progress reports, the aop_how(s) * must be copied into the per-operation structure using * libxl__ao_progress_gethow. * * - If the initiation is unsuccessful, the initiating function must * call libxl__ao_create_fail before unlocking and returning whatever * error code is appropriate (AO_CREATE_FAIL macro). * * If initiation is successful: * * - The initiating function must run libxl__ao_inprogress right * before unlocking and returning, and return whatever it returns. * This is best achieved with the AO_INPROGRESS macro. * * - If the operation supports progress reports, it may generate * suitable events with NEW_EVENT and report them with * libxl__ao_progress_report (with the ctx locked). * * - Eventually, some callback function, whose callback has been * requested directly or indirectly, should call libxl__ao_complete * (with the ctx locked, as it will generally already be in any * event callback function). This must happen exactly once for each * ao, as the last that happens with that ao. * * - However, it is permissible for the initiating function to call * libxl__ao_inprogress and/or libxl__ao_complete (directly or * indirectly), before it uses AO_INPROGRESS to return. (The ao * infrastructure will arrange to defer destruction of the ao, etc., * until the proper time.) An initiating function should do this * if it takes a codepath which completes synchronously. * * - Conversely it is forbidden to call libxl__ao_complete in the * initiating function _after_ AO_INPROGRESS, because * libxl__ao_complete requires the ctx to be locked. * * - Note that during callback functions, two gcs are available: * - The one in egc, whose lifetime is only this callback * - The one in ao, whose lifetime is the asynchronous operation * Usually a callback function should use CONTAINER_OF to obtain its * own state structure, containing a pointer to the ao. It should * then obtain the ao and use the ao's gc; this is most easily done * using the convenience macro STATE_AO_GC. */ #define AO_CREATE(ctx, domid, ao_how) \ libxl__ctx_lock(ctx); \ libxl__ao *ao = libxl__ao_create(ctx, domid, ao_how, \ __FILE__, __LINE__, __func__); \ if (!ao) { libxl__ctx_unlock(ctx); return ERROR_NOMEM; } \ libxl__egc egc[1]; LIBXL_INIT_EGC(egc[0],ctx); \ AO_GC; #define AO_INPROGRESS ({ \ libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc); \ CTX_UNLOCK; \ EGC_FREE; \ CTX_LOCK; \ int ao__rc = libxl__ao_inprogress(ao, \ __FILE__, __LINE__, __func__); \ libxl__ctx_unlock(ao__ctx); /* gc is now invalid */ \ (ao__rc); \ }) #define AO_CREATE_FAIL(rc) ({ \ libxl_ctx *ao__ctx = libxl__gc_owner(&ao->gc); \ assert(rc); \ libxl__ao_create_fail(ao); \ libxl__ctx_unlock(ao__ctx); /* gc is now invalid */ \ EGC_FREE; \ (rc); \ }) /* * Given, in scope, * libxl__ao *ao; * produces, in scope, * libxl__gc *gc; */ #define AO_GC \ libxl__gc *const gc __attribute__((unused)) = &ao->gc /* * void STATE_AO_GC(libxl__ao *ao_spec); * // Produces, in scope: * libxl__ao *ao; // set from ao_spec * libxl__gc *gc; */ #define STATE_AO_GC(op_ao) \ libxl__ao *const ao = (op_ao); \ libxl__gc *const gc __attribute__((unused)) = libxl__ao_inprogress_gc(ao) /* All of these MUST be called with the ctx locked. * libxl__ao_inprogress MUST be called with the ctx locked exactly once. */ _hidden libxl__ao *libxl__ao_create(libxl_ctx*, uint32_t domid, const libxl_asyncop_how*, const char *file, int line, const char *func); _hidden int libxl__ao_inprogress(libxl__ao *ao, const char *file, int line, const char *func); /* temporarily unlocks */ _hidden void libxl__ao_create_fail(libxl__ao *ao); _hidden void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc); _hidden libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao); /* Can be called at any time. Use is essential for any aop user. */ _hidden void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state, const libxl_asyncprogress_how *from_app); /* Must be called with the ctx locked. Will fill in ev->for_user, * so caller need not do that. */ _hidden void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao, const libxl_asyncprogress_how *how, libxl_event *ev /* consumed */); /* For use by ao machinery ONLY */ _hidden void libxl__ao__destroy(libxl_ctx*, libxl__ao *ao); _hidden void libxl__ao_complete_check_progress_reports(libxl__egc*, libxl__ao*); /* * Short-lived sub-ao, aka "nested ao". * * Some asynchronous operations are very long-running. Generally, * since an ao has a gc, any allocations made in that ao will live * until the ao is completed. When this is not desirable, these * functions may be used to manage a "sub-ao". * * The returned sub-ao is suitable for passing to gc-related functions * and macros such as libxl__ao_inprogress_gc, AO_GC, and STATE_AO_GC. * * It MUST NOT be used with AO_INPROGRESS, AO_CREATE_FAIL, * libxl__ao_complete, libxl__ao_progress_report, and so on. * * The caller must ensure that all of the sub-ao's are freed before * the parent is. Multiple levels of nesting are OK (although * hopefully they won't be necessary). */ _hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent); /* cannot fail */ _hidden void libxl__nested_ao_free(libxl__ao *child); /* * File descriptors and CLOEXEC */ /* * For libxl functions which create file descriptors, at least one * of the following must be true: * (a) libxl does not care if copies of this open-file are inherited * by random children and might remain open indefinitely * (b) libxl must take extra care for the fd (the actual descriptor, * not the open-file) as below. We call this a "carefd". * * The rules for opening a carefd are: * (i) Before bringing any carefds into existence, * libxl code must call libxl__carefd_begin. * (ii) Then for each carefd brought into existence, * libxl code must call libxl__carefd_record * and remember the libxl__carefd_record*. * (iii) Then it must call libxl__carefd_unlock. * (iv) When in a child process the fd is to be passed across * exec by libxl, the libxl code must unset FD_CLOEXEC * on the fd eg by using libxl_fd_set_cloexec. * (v) Later, when the fd is to be closed in the same process, * libxl code must not call close. Instead, it must call * libxl__carefd_close. * Steps (ii) and (iii) can be combined by calling the convenience * function libxl__carefd_opened. */ /* libxl__carefd_begin and _unlock (or _opened) must be called always * in pairs. They may be called with the CTX lock held. In between * _begin and _unlock, the following are prohibited: * - anything which might block * - any callbacks to the application * - nested calls to libxl__carefd_begin * - fork (libxl__fork) * In general nothing should be done before _unlock that could be done * afterwards. */ typedef struct libxl__carefd libxl__carefd; _hidden void libxl__carefd_begin(void); _hidden void libxl__carefd_unlock(void); /* fd may be -1, in which case this returns a dummy libxl__fd_record * on which it _carefd_close is a no-op. Cannot fail. */ _hidden libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd); /* Combines _record and _unlock in a single call. If fd==-1, * still does the unlock, but returns 0. */ _hidden libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd); /* Works just like close(2). You may pass NULL, in which case it's * a successful no-op. */ _hidden int libxl__carefd_close(libxl__carefd*); /* You may pass NULL in which case the answer is -1. */ _hidden int libxl__carefd_fd(const libxl__carefd*); /* common paths */ _hidden const char *libxl__private_bindir_path(void); _hidden const char *libxl__xenfirmwaredir_path(void); _hidden const char *libxl__xen_config_dir_path(void); _hidden const char *libxl__xen_script_dir_path(void); _hidden const char *libxl__lock_dir_path(void); _hidden const char *libxl__run_dir_path(void); _hidden const char *libxl__seabios_path(void); _hidden const char *libxl__ovmf_path(void); /*----- subprocess execution with timeout -----*/ typedef struct libxl__async_exec_state libxl__async_exec_state; typedef void libxl__async_exec_callback(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status); /* * Meaning of status and rc: * rc==0, status==0 all went well * rc==0, status!=0 everything OK except child exited nonzero (logged) * rc!=0 something else went wrong (status is real * exit status; maybe reflecting SIGKILL, and * therefore not very interesting, if aes code * killed the child). Logged unless ABORTED. */ struct libxl__async_exec_state { /* caller must fill these in */ libxl__ao *ao; const char *what; /* for error msgs, what we're executing */ int timeout_ms; libxl__async_exec_callback *callback; /* caller must fill in; as for libxl__exec */ int stdfds[3]; char **args; /* execution arguments */ char **env; /* execution environment */ /* private */ libxl__ev_time time; libxl__ev_child child; int rc; }; void libxl__async_exec_init(libxl__async_exec_state *aes); int libxl__async_exec_start(libxl__async_exec_state *aes); bool libxl__async_exec_inuse(const libxl__async_exec_state *aes); _hidden void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what); /*----- device addition/removal -----*/ typedef struct libxl__ao_device libxl__ao_device; typedef struct libxl__multidev libxl__multidev; typedef void libxl__device_callback(libxl__egc*, libxl__ao_device*); /* This functions sets the necessary libxl__ao_device struct values to use * safely inside functions. It marks the operation as "active" * since we need to be sure that all device status structs are set * to active before start queueing events, or we might call * ao_complete before all devices had finished * * libxl__initiate_device_{remove/addition} should not be called without * calling libxl__prepare_ao_device first, since it initializes the private * fields of the struct libxl__ao_device to what this functions expect. * * Once _prepare has been called on a libxl__ao_device, it is safe to just * discard this struct, there's no need to call any destroy function. * _prepare can also be called multiple times with the same libxl__ao_device. */ _hidden void libxl__prepare_ao_device(libxl__ao *ao, libxl__ao_device *aodev); /* generic callback for devices that only need to set ao_complete */ _hidden void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev); struct libxl__ao_device { /* filled in by user */ libxl__ao *ao; libxl__device_action action; libxl__device *dev; int force; libxl__device_callback *callback; /* return value, zeroed by user on entry, is valid on callback */ int rc; /* private for multidev */ int active; libxl__multidev *multidev; /* reference to the containing multidev */ /* private for add/remove implementation */ libxl__ev_devstate backend_ds; /* Bodge for Qemu devices */ libxl__ev_time timeout; /* xenstore watch for backend path of driver domains */ libxl__xswait_state xswait; int num_exec; /* for calling hotplug scripts */ libxl__async_exec_state aes; /* If we need to update JSON config */ bool update_json; /* for asynchronous execution of synchronous-only syscalls etc. */ libxl__ev_child child; }; /* * Multiple devices "multidev" handling. * * Firstly, you should * libxl__multidev_begin * multidev->callback = ... * Then zero or more times * libxl__multidev_prepare * libxl__initiate_device_{remove/addition} * (or some other thing which will eventually call * aodev->callback or libxl__multidev_one_callback) * Finally, once * libxl__multidev_prepared * which will result (perhaps reentrantly) in one call to * multidev->callback(). */ /* Starts preparing to add/remove a bunch of devices. */ _hidden void libxl__multidev_begin(libxl__ao *ao, libxl__multidev*); /* Prepares to add/remove one of many devices. * Calls libxl__prepare_ao_device on libxl__ao_device argument provided and * also sets the aodev->callback (to libxl__multidev_one_callback) * The user should not mess with aodev->callback. */ _hidden void libxl__multidev_prepare_with_aodev(libxl__multidev*, libxl__ao_device*); /* A wrapper function around libxl__multidev_prepare_with_aodev. * Allocates a libxl__ao_device and prepares it for addition/removal. * Returns the newly allocated libxl__ao_dev. */ _hidden libxl__ao_device *libxl__multidev_prepare(libxl__multidev*); /* Indicates to multidev that this one device has been processed. * Normally the multidev user does not need to touch this function, as * multidev_prepare will name it in aodev->callback. However, if you * want to do something more complicated you can set aodev->callback * yourself to something else, so long as you eventually call * libxl__multidev_one_callback. */ _hidden void libxl__multidev_one_callback(libxl__egc *egc, libxl__ao_device *aodev); /* Notifies the multidev machinery that we have now finished preparing * and initiating devices. multidev->callback may then be called as * soon as there are no prepared but not completed operations * outstanding, perhaps reentrantly. If rc!=0 (error should have been * logged) multidev->callback will get a non-zero rc. * callback may be set by the user at any point before prepared. */ _hidden void libxl__multidev_prepared(libxl__egc*, libxl__multidev*, int rc); typedef void libxl__devices_callback(libxl__egc*, libxl__multidev*, int rc); struct libxl__multidev { /* set by user: */ libxl__devices_callback *callback; /* for private use by libxl__...ao_devices... machinery: */ libxl__ao *ao; libxl__ao_device **array; int used, allocd; libxl__ao_device *preparation; }; /* * Algorithm for handling device removal (including domain * destruction). This is somewhat subtle because we may already have * killed the domain and caused the death of qemu. * * In current versions of qemu there is no mechanism for ensuring that * the resources used by its devices (both emulated and any PV devices * provided by qemu) are freed (eg, fds closed) before it shuts down, * and no confirmation from a terminating qemu back to the toolstack. * * This will need to be fixed in future Xen versions. In the meantime * (Xen 4.2) we implement a bodge. * * WE WANT TO UNPLUG WE WANT TO SHUT DOWN OR DESTROY * | | * | LIBXL SENDS SIGHUP TO QEMU * | .....................|........................ * | : XEN 4.3+ PLANNED | : * | : QEMU TEARS DOWN ALL DEVICES : * | : FREES RESOURCES (closing fds) : * | : SETS PV BACKENDS TO STATE 5, : * | : waits for PV frontends to shut down : * | : SETS PV BACKENDS TO STATE 6 : * | : | : * | : QEMU NOTIFIES TOOLSTACK (via : * | : xenstore) that it is exiting : * | : QEMU EXITS (parent may be init) : * | : | : * | : TOOLSTACK WAITS FOR QEMU : * | : notices qemu has finished : * | :....................|.......................: * | .--------------------' * V V * for each device * we want to unplug/remove * ..................|........................................... * : V XEN 4.2 RACY BODGE : * : device is provided by qemu : * : | `-----------. : * : something| V : * : else, eg| domain (that is domain for which : * : blkback| this PV device is the backend, : * : | which might be the stub dm) : * : | is still alive? : * : | | | : * : | |alive |dead : * : |<-----------------' | : * : | hopefully qemu is | : * : | still running | : * :............|................. | : * ,----->| : we may be racing : * | backend state? : with qemu's death : * ^ | | : | : * xenstore| |other |6 : WAIT 2.0s : * conflict| | | : TIMEOUT : * | WRITE B.E. | : | : * | STATE:=5 | : hopefully qemu has : * `---' | | : gone by now and : * |ok | : freed its resources : * | | : | : * WAIT FOR | : SET B.E. : * STATE==6 | : STATE:=6 : * / | | :..........|...................: * timeout/ ok| | | * / | | | * | RUN HOTPLUG <-'<----------------' * | SCRIPT * | | * `---> NUKE * BACKEND * | * DONE. */ /* AO operation to connect a disk device, called by * libxl_device_disk_add and libxl__add_disks. This function calls * libxl__wait_device_connection to wait for the device to * finish the connection (might involve executing hotplug scripts). * * Once finished, aodev->callback will be executed. */ /* * As of Xen 4.5 we maintain various infomation, including hotplug * device information, in JSON files, so that we can use this JSON * file as a template to reconstruct domain configuration. * * In essense there are now two views of device state, one is xenstore, * the other is JSON file. We use xenstore as primary reference. * * Here we maintain one invariant: every device in xenstore must have * an entry in JSON file. * * All device hotplug routines should comply to following pattern: * lock json config (json_lock) * read json config * update in-memory json config with new entry, replacing * any stale entry * for loop -- xs transaction * open xs transaction * check device existence, bail if it exists * write in-memory json config to disk * commit xs transaction * end for loop * unlock json config * * Device removal routines are not touched. * * Here is the proof that we always maintain that invariant and we * don't leak files during interaction of hotplug thread and other * threads / processes. * * # Safe against parallel add * * When another thread / process tries to add same device, it's * blocked by json_lock. The loser of two threads will bail at * existence check, so that we don't overwrite anything. * * # Safe against domain destruction * * If the thread / process trying to destroy domain loses the race, it's * blocked by json_lock. If the hotplug thread is loser, it bails at * acquiring lock because lock acquisition function checks existence of * the domain. * * # Safe against parallel removal * * When another thread / process tries to remove a device, it's _NOT_ * blocked by json_lock, but xenstore transaction can help maintain * invariant. The removal threads either a) sees that device in * xenstore, b) doesn't see that device in xenstore. * * In a), it sees that device in xenstore. At that point hotplug is * already finished (both JSON and xenstore changes committed). So that * device can be safely removed. JSON entry is left untouched and * becomes stale, but this is a valid state -- next time when a * device with same identifier gets added, the stale entry gets * overwritten. * * In b), it doesn't see that device in xenstore, but it will commence * anyway. Eventually a forcibly removal is initiated, which will forcely * remove xenstore entry. * * If hotplug threads creates xenstore entry (therefore JSON entry as * well) before force removal, that xenstore entry is removed. We're * left with JSON stale entry but not xenstore entry, which is a valid * state. * * If hotplug thread has not created xenstore entry when the removal * is committed, we're obviously safe. Hotplug thread will add in * xenstore entry afterwards. We have both JSON and xenstore entry, * it's a valid state. */ /* Internal function to connect a vkb device */ _hidden int libxl__device_vkb_add(libxl__gc *gc, uint32_t domid, libxl_device_vkb *vkb); /* Internal function to connect a vfb device */ _hidden int libxl__device_vfb_add(libxl__gc *gc, uint32_t domid, libxl_device_vfb *vfb); /* Internal function to connect a 9pfs device */ _hidden int libxl__device_p9_add(libxl__gc *gc, uint32_t domid, libxl_device_p9 *p9); /* Waits for the passed device to reach state XenbusStateInitWait. * This is not really useful by itself, but is important when executing * hotplug scripts, since we need to be sure the device is in the correct * state before executing them. * * Once finished, aodev->callback will be executed. */ _hidden void libxl__wait_device_connection(libxl__egc*, libxl__ao_device *aodev); /* Arranges that dev will be removed to the guest, and the * hotplug scripts will be executed (if necessary). When * this is done (or an error happens), the callback in * aodev->callback will be called. * * The libxl__ao_device passed to this function should be * prepared using libxl__prepare_ao_device prior to calling * this function. * * Once finished, aodev->callback will be executed. */ _hidden void libxl__initiate_device_generic_remove(libxl__egc *egc, libxl__ao_device *aodev); _hidden void libxl__initiate_device_usbctrl_remove(libxl__egc *egc, libxl__ao_device *aodev); /* * libxl__get_hotplug_script_info returns the args and env that should * be passed to the hotplug script for the requested device. * * Since a device might not need to execute any hotplug script, this function * can return the following values: * < 0: Error * 0: No need to execute hotplug script * 1: Execute hotplug script * * The last parameter, "num_exec" refeers to the number of times hotplug * scripts have been called for this device. * * The main body of libxl will, for each device, keep calling * libxl__get_hotplug_script_info, with incrementing values of * num_exec, and executing the resulting script accordingly, * until libxl__get_hotplug_script_info returns<=0. */ _hidden int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action, int num_exec); /*----- local disk attach: attach a disk locally to run the bootloader -----*/ typedef struct libxl__disk_local_state libxl__disk_local_state; typedef void libxl__disk_local_state_callback(libxl__egc*, libxl__disk_local_state*, int rc); /* A libxl__disk_local_state may be in the following states: * Undefined, Idle, Attaching, Attached, Detaching. */ struct libxl__disk_local_state { /* filled by the user */ libxl__ao *ao; const libxl_device_disk *in_disk; libxl_device_disk disk; const char *blkdev_start; libxl__disk_local_state_callback *callback; /* filled by libxl__device_disk_local_initiate_attach */ char *diskpath; /* private for implementation of local detach */ libxl__ao_device aodev; int rc; }; /* * Prepares a dls for use. * State Undefined -> Idle */ static inline void libxl__device_disk_local_init(libxl__disk_local_state *dls) { dls->rc = 0; } /* * See if we can find a way to access a disk locally */ _hidden char * libxl__device_disk_find_local_path(libxl__gc *gc, libxl_domid guest_domid, const libxl_device_disk *disk, bool qdisk_direct); /* Make a disk available in this (the control) domain. Always calls * dls->callback when finished. * State Idle -> Attaching * * The state of dls on entry to the callback depends on the value * of rc passed to the callback: * rc == 0: Attached if rc == 0 * rc != 0: Idle */ _hidden void libxl__device_disk_local_initiate_attach(libxl__egc *egc, libxl__disk_local_state *dls); /* Disconnects a disk device form the control domain. If the passed * dls is not attached (or has already been detached), * libxl__device_disk_local_initiate_detach will just call the callback * directly. * State Idle/Attached -> Detaching * * The state of dls on entry to the callback is Idle. */ _hidden void libxl__device_disk_local_initiate_detach(libxl__egc *egc, libxl__disk_local_state *dls); /*----- datacopier: copies data from one fd to another -----*/ typedef struct libxl__datacopier_state libxl__datacopier_state; typedef struct libxl__datacopier_buf libxl__datacopier_buf; /* onwrite==1 means problem happened when writing * rc==FAIL errnoval >0 we had a write error, logged * onwrite==0 means problem happened when reading * rc==0 errnoval==0 we got eof and all data was written * rc==FAIL errnoval >0 we had a read error, logged * onwrite==-1 means some other internal problem * rc==FAIL errnoval==EIO some other internal failure, logged * rc==ABORTED errnoval==0 abort requested, not logged * If we get POLLHUP, we call callback_pollhup with * rc==FAIL errnoval==-1 POLLHUP signalled * or if callback_pollhup==0 this is treated as eof (if POLLIN|POLLHUP * on the reading fd) or an internal failure (otherwise), as above. * In all cases copier is killed before calling this callback */ typedef void libxl__datacopier_callback(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); struct libxl__datacopier_buf { /* private to datacopier */ LIBXL_TAILQ_ENTRY(libxl__datacopier_buf) entry; int used; char buf[1000]; }; struct libxl__datacopier_state { /* caller must fill these in, and they must all remain valid */ libxl__ao *ao; int readfd, writefd; ssize_t maxsz; ssize_t bytes_to_read; /* set to -1 to read until EOF */ const char *copywhat, *readwhat, *writewhat; /* for error msgs */ FILE *log; /* gets a copy of everything */ libxl__datacopier_callback *callback; libxl__datacopier_callback *callback_pollhup; void *readbuf; /* Set this to read data into it without writing to an fd. The buffer should be at least as large as the bytes_to_read parameter, which should not be -1. */ /* remaining fields are private to datacopier */ libxl__ao_abortable abrt; libxl__ev_fd toread, towrite; ssize_t used; LIBXL_TAILQ_HEAD(libxl__datacopier_bufs, libxl__datacopier_buf) bufs; }; _hidden void libxl__datacopier_init(libxl__datacopier_state *dc); _hidden void libxl__datacopier_kill(libxl__datacopier_state *dc); _hidden int libxl__datacopier_start(libxl__datacopier_state *dc); /* Inserts literal data into the output stream. The data is copied. * May safely be used only immediately after libxl__datacopier_start * (before the ctx is unlocked). But may be called multiple times. * NB exceeding maxsz will fail an assertion! */ _hidden void libxl__datacopier_prefixdata(libxl__egc*, libxl__datacopier_state*, const void *data, size_t len); /*----- Save/restore helper (used by creation and suspend) -----*/ typedef struct libxl__srm_save_callbacks { libxl__srm_save_autogen_callbacks a; } libxl__srm_save_callbacks; typedef struct libxl__srm_restore_callbacks { libxl__srm_restore_autogen_callbacks a; } libxl__srm_restore_callbacks; /* a pointer to this struct is also passed as "user" to the * save callout helper callback functions */ typedef struct libxl__save_helper_state { /* public, caller of run_helper initialises */ libxl__ao *ao; uint32_t domid; union { libxl__srm_save_callbacks save; libxl__srm_restore_callbacks restore; } callbacks; int (*recv_callback)(const unsigned char *msg, uint32_t len, void *user); void (*completion_callback)(libxl__egc *egc, void *caller_state, int rc, int retval, int errnoval); void *caller_state; int need_results; /* set to 0 or 1 by caller of run_helper; * if set to 1 then the ultimate caller's * results function must set it to 0 */ /* private */ int rc; int completed; /* retval/errnoval valid iff completed */ int retval, errnoval; /* from xc_domain_save / xc_domain_restore */ libxl__ao_abortable abrt; libxl__carefd *pipes[2]; /* 0 = helper's stdin, 1 = helper's stdout */ libxl__ev_fd readable; libxl__ev_child child; const char *stdin_what, *stdout_what; libxl__egc *egc; /* valid only for duration of each event callback; * is here in this struct for the benefit of the * marshalling and xc callback functions */ } libxl__save_helper_state; /*----- checkpoint device related state structure -----*/ /* * The abstract checkpoint device layer exposes a common * set of API to [external] libxl for manipulating devices attached to * a guest protected by Remus/COLO. The device layer also exposes a set of * [internal] interfaces that every device type must implement. * * The following API are exposed to libxl: * * One-time configuration operations: * +libxl__checkpoint_devices_setup * > Enable output buffering for NICs, setup disk replication, etc. * +libxl__checkpoint_devices_teardown * > Disable output buffering and disk replication; teardown any * associated external setups like qdiscs for NICs. * * Operations executed every checkpoint (in order of invocation): * +libxl__checkpoint_devices_postsuspend * +libxl__checkpoint_devices_preresume * +libxl__checkpoint_devices_commit * * Each device type needs to implement the interfaces specified in * the libxl__checkpoint_device_instance_ops if it wishes to support Remus/COLO. * * The high-level control flow through the checkpoint device layer is shown * below: * * xl remus * |-> libxl_domain_remus_start * |-> libxl__checkpoint_devices_setup * |-> Per-checkpoint libxl__checkpoint_devices_[postsuspend,preresume,commit] * ... * |-> On backup failure, network error or other internal errors: * libxl__checkpoint_devices_teardown */ typedef struct libxl__checkpoint_device libxl__checkpoint_device; typedef struct libxl__checkpoint_devices_state libxl__checkpoint_devices_state; typedef struct libxl__checkpoint_device_instance_ops libxl__checkpoint_device_instance_ops; /* * Interfaces to be implemented by every device subkind that wishes to * support Remus/COLO. Functions must be implemented unless otherwise * stated. Many of these functions are asynchronous. They call * dev->aodev.callback when done. The actual implementations may be * synchronous and call dev->aodev.callback directly (as the last * thing they do). */ struct libxl__checkpoint_device_instance_ops { /* the device kind this ops belongs to... */ libxl__device_kind kind; /* * Checkpoint operations. May be NULL, meaning the op is not * implemented and the caller should treat them as a no-op (and do * nothing when checkpointing). * Asynchronous. */ void (*postsuspend)(libxl__egc *egc, libxl__checkpoint_device *dev); void (*preresume)(libxl__egc *egc, libxl__checkpoint_device *dev); void (*commit)(libxl__egc *egc, libxl__checkpoint_device *dev); /* * setup() and teardown() are refer to the actual checkpoint device. * Asynchronous. * teardown is called even if setup fails. */ /* * setup() should first determines whether the subkind matches the specific * device. If matched, the device will then be managed with this set of * subkind operations. * Yields 0 if the device successfully set up. * CHECKPOINT_DEVOPS_DOES_NOT_MATCH if the ops does not match the device. * any other rc indicates failure. */ void (*setup)(libxl__egc *egc, libxl__checkpoint_device *dev); void (*teardown)(libxl__egc *egc, libxl__checkpoint_device *dev); }; int init_subkind_nic(libxl__checkpoint_devices_state *cds); void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds); int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds); void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds); typedef void libxl__checkpoint_callback(libxl__egc *, libxl__checkpoint_devices_state *, int rc); /* * State associated with a checkpoint invocation, including parameters * passed to the checkpoint abstract device layer by the remus * save/restore machinery. */ struct libxl__checkpoint_devices_state { /*-- must be set by caller of libxl__checkpoint_device_(setup|teardown) --*/ libxl__ao *ao; uint32_t domid; libxl__checkpoint_callback *callback; void *concrete_data; int device_kind_flags; /* The ops must be pointer array, and the last ops must be NULL. */ const libxl__checkpoint_device_instance_ops **ops; /*----- private for abstract layer only -----*/ int num_devices; /* * this array is allocated before setup the checkpoint devices by the * checkpoint abstract layer. * devs may be NULL, means there's no checkpoint devices that has been * set up. * the size of this array is 'num_devices', which is the total number * of libxl nic devices and disk devices(num_nics + num_disks). */ libxl__checkpoint_device **devs; libxl_device_nic *nics; int num_nics; libxl_device_disk *disks; int num_disks; libxl__multidev multidev; }; /* * Information about a single device being handled by remus. * Allocated by the checkpoint abstract layer. */ struct libxl__checkpoint_device { /*----- shared between abstract and concrete layers -----*/ /* * if this is true, that means the subkind ops match the device */ bool matched; /*----- set by checkpoint device abstruct layer -----*/ /* libxl__device_* which this checkpoint device related to */ const void *backend_dev; libxl__device_kind kind; libxl__checkpoint_devices_state *cds; libxl__ao_device aodev; /*----- private for abstract layer only -----*/ /* * Control and state variables for the asynchronous callback * based loops which iterate over device subkinds, and over * individual devices. */ int ops_index; const libxl__checkpoint_device_instance_ops *ops; /*----- private for concrete (device-specific) layer -----*/ /* concrete device's private data */ void *concrete_data; }; /* the following 5 APIs are async ops, call cds->callback when done */ _hidden void libxl__checkpoint_devices_setup(libxl__egc *egc, libxl__checkpoint_devices_state *cds); _hidden void libxl__checkpoint_devices_teardown(libxl__egc *egc, libxl__checkpoint_devices_state *cds); _hidden void libxl__checkpoint_devices_postsuspend(libxl__egc *egc, libxl__checkpoint_devices_state *cds); _hidden void libxl__checkpoint_devices_preresume(libxl__egc *egc, libxl__checkpoint_devices_state *cds); _hidden void libxl__checkpoint_devices_commit(libxl__egc *egc, libxl__checkpoint_devices_state *cds); /*----- Remus related state structure -----*/ typedef struct libxl__remus_state libxl__remus_state; struct libxl__remus_state { /* private */ libxl__ev_time checkpoint_timeout; /* used for Remus checkpoint */ int interval; /* checkpoint interval */ /*----- private for concrete (device-specific) layer only -----*/ /* private for nic device subkind ops */ char *netbufscript; struct nl_sock *nlsock; struct nl_cache *qdisc_cache; /* private for drbd disk subkind ops */ char *drbd_probe_script; }; _hidden int libxl__netbuffer_enabled(libxl__gc *gc); /*----- Legacy conversion helper -----*/ typedef struct libxl__conversion_helper_state libxl__conversion_helper_state; struct libxl__conversion_helper_state { /* Public - Must be filled by caller unless noted. */ libxl__ao *ao; int legacy_fd; /* fd to read the legacy stream from. */ bool hvm; /* pv or hvm domain? */ libxl__carefd *v2_carefd; /* Filled by successful call to * libxl__convert_legacy_stream(). Caller * assumes ownership of the fd. */ void (*completion_callback)( libxl__egc *egc, libxl__conversion_helper_state *chs, int rc); /* private */ int rc; libxl__ao_abortable abrt; libxl__ev_child child; }; _hidden void libxl__conversion_helper_init (libxl__conversion_helper_state *chs); _hidden int libxl__convert_legacy_stream(libxl__egc *egc, libxl__conversion_helper_state *chs); _hidden void libxl__conversion_helper_abort(libxl__egc *egc, libxl__conversion_helper_state *chs, int rc); static inline bool libxl__conversion_helper_inuse (const libxl__conversion_helper_state *chs) { return libxl__ev_child_inuse(&chs->child); } /* State for reading a libxl migration v2 stream */ typedef struct libxl__stream_read_state libxl__stream_read_state; typedef struct libxl__sr_record_buf { /* private to stream read helper */ LIBXL_STAILQ_ENTRY(struct libxl__sr_record_buf) entry; libxl__sr_rec_hdr hdr; void *body; /* iff hdr.length != 0 */ } libxl__sr_record_buf; struct libxl__stream_read_state { /* filled by the user */ libxl__ao *ao; libxl__domain_create_state *dcs; int fd; bool legacy; bool back_channel; void (*completion_callback)(libxl__egc *egc, libxl__stream_read_state *srs, int rc); void (*checkpoint_callback)(libxl__egc *egc, libxl__stream_read_state *srs, int rc); /* Private */ int rc; bool running; bool in_checkpoint; bool sync_teardown; /* Only used to coordinate shutdown on error path. */ bool in_checkpoint_state; libxl__save_helper_state shs; libxl__conversion_helper_state chs; /* Main stream-reading data. */ libxl__datacopier_state dc; /* Only used when reading a record */ libxl__sr_hdr hdr; LIBXL_STAILQ_HEAD(, libxl__sr_record_buf) record_queue; /* NOGC */ enum { SRS_PHASE_NORMAL, SRS_PHASE_BUFFERING, SRS_PHASE_UNBUFFERING, } phase; bool recursion_guard; /* Only used while actively reading a record from the stream. */ libxl__sr_record_buf *incoming_record; /* NOGC */ /* Both only used when processing an EMULATOR record. */ libxl__datacopier_state emu_dc; libxl__carefd *emu_carefd; }; _hidden void libxl__stream_read_init(libxl__stream_read_state *stream); _hidden void libxl__stream_read_start(libxl__egc *egc, libxl__stream_read_state *stream); _hidden void libxl__stream_read_start_checkpoint(libxl__egc *egc, libxl__stream_read_state *stream); _hidden void libxl__stream_read_checkpoint_state(libxl__egc *egc, libxl__stream_read_state *stream); _hidden void libxl__stream_read_abort(libxl__egc *egc, libxl__stream_read_state *stream, int rc); static inline bool libxl__stream_read_inuse(const libxl__stream_read_state *stream) { return stream->running; } #include "libxl_colo.h" /*----- Domain suspend (save) state structure -----*/ /* * "suspend" refers to quiescing the VM, so pausing qemu, making a * remote_shutdown(SHUTDOWN_suspend) hypercall etc. * * "save" refers to the actions involved in actually shuffling the * state of the VM, so xc_domain_save() etc. */ typedef struct libxl__domain_suspend_state libxl__domain_suspend_state; typedef struct libxl__domain_save_state libxl__domain_save_state; typedef void libxl__domain_save_cb(libxl__egc*, libxl__domain_save_state*, int rc); typedef void libxl__save_device_model_cb(libxl__egc*, libxl__domain_save_state*, int rc); /* State for writing a libxl migration v2 stream */ typedef struct libxl__stream_write_state libxl__stream_write_state; typedef void (*sws_record_done_cb)(libxl__egc *egc, libxl__stream_write_state *sws); struct libxl__stream_write_state { /* filled by the user */ libxl__ao *ao; libxl__domain_save_state *dss; int fd; bool back_channel; void (*completion_callback)(libxl__egc *egc, libxl__stream_write_state *sws, int rc); void (*checkpoint_callback)(libxl__egc *egc, libxl__stream_write_state *sws, int rc); /* Private */ int rc; bool running; bool in_checkpoint; bool sync_teardown; /* Only used to coordinate shutdown on error path. */ bool in_checkpoint_state; libxl__save_helper_state shs; /* Main stream-writing data. */ libxl__datacopier_state dc; sws_record_done_cb record_done_callback; /* Cache device model version. */ libxl_device_model_version device_model_version; /* Only used when constructing EMULATOR records. */ libxl__datacopier_state emu_dc; libxl__carefd *emu_carefd; libxl__sr_rec_hdr emu_rec_hdr; libxl__sr_emulator_hdr emu_sub_hdr; void *emu_body; }; _hidden void libxl__stream_write_init(libxl__stream_write_state *stream); _hidden void libxl__stream_write_start(libxl__egc *egc, libxl__stream_write_state *stream); _hidden void libxl__stream_write_start_checkpoint(libxl__egc *egc, libxl__stream_write_state *stream); _hidden void libxl__stream_write_checkpoint_state(libxl__egc *egc, libxl__stream_write_state *stream, libxl_sr_checkpoint_state *srcs); _hidden void libxl__stream_write_abort(libxl__egc *egc, libxl__stream_write_state *stream, int rc); static inline bool libxl__stream_write_inuse(const libxl__stream_write_state *stream) { return stream->running; } typedef struct libxl__logdirty_switch { /* Set by caller of libxl__domain_common_switch_qemu_logdirty */ libxl__ao *ao; void (*callback)(libxl__egc *egc, struct libxl__logdirty_switch *lds, int rc); const char *cmd; const char *cmd_path; const char *ret_path; libxl__ev_xswatch watch; libxl__ev_time timeout; } libxl__logdirty_switch; _hidden void libxl__logdirty_init(libxl__logdirty_switch *lds); struct libxl__domain_suspend_state { /* set by caller of libxl__domain_suspend_init */ libxl__ao *ao; uint32_t domid; /* private */ libxl_domain_type type; libxl__ev_evtchn guest_evtchn; int guest_evtchn_lockfd; int guest_responded; libxl__xswait_state pvcontrol; libxl__ev_xswatch guest_watch; libxl__ev_time guest_timeout; const char *dm_savefile; void (*callback_common_done)(libxl__egc*, struct libxl__domain_suspend_state*, int ok); }; int libxl__domain_suspend_init(libxl__egc *egc, libxl__domain_suspend_state *dsps, libxl_domain_type type); struct libxl__domain_save_state { /* set by caller of libxl__domain_save */ libxl__ao *ao; libxl__domain_save_cb *callback; uint32_t domid; int fd; int fdfl; /* original flags on fd */ int recv_fd; libxl_domain_type type; int live; int debug; int checkpointed_stream; const libxl_domain_remus_info *remus; /* private */ int rc; int hvm; int xcflags; libxl__domain_suspend_state dsps; union { /* for Remus */ libxl__remus_state rs; /* for COLO */ libxl__colo_save_state css; }; libxl__checkpoint_devices_state cds; libxl__stream_write_state sws; libxl__logdirty_switch logdirty; }; /*----- openpty -----*/ /* * opens count (>0) ptys like count calls to openpty, and then * calls back. On entry, all op[].master and op[].slave must be * 0. On callback, either rc==0 and master and slave are non-0, * or rc is a libxl error and they are both 0. If libxl__openpty * returns non-0 no callback will happen and everything is left * cleaned up. */ typedef struct libxl__openpty_state libxl__openpty_state; typedef struct libxl__openpty_result libxl__openpty_result; typedef void libxl__openpty_callback(libxl__egc *egc, libxl__openpty_state *op); struct libxl__openpty_state { /* caller must fill these in, and they must all remain valid */ libxl__ao *ao; libxl__openpty_callback *callback; int count; libxl__openpty_result *results; /* actual size is count, out parameter */ /* public, result, caller may only read in callback */ int rc; /* private for implementation */ libxl__ev_child child; }; struct libxl__openpty_result { libxl__carefd *master, *slave; }; int libxl__openptys(libxl__openpty_state *op, struct termios *termp, struct winsize *winp); /*----- bootloader -----*/ typedef struct libxl__bootloader_state libxl__bootloader_state; typedef void libxl__run_bootloader_callback(libxl__egc*, libxl__bootloader_state*, int rc); typedef void libxl__bootloader_console_callback(libxl__egc*, libxl__bootloader_state*); struct libxl__bootloader_state { /* caller must fill these in, and they must all remain valid */ libxl__ao *ao; libxl__run_bootloader_callback *callback; libxl__bootloader_console_callback *console_available; const libxl_domain_build_info *info; libxl_device_disk *disk; /* Should be zeroed by caller on entry. Will be filled in by * bootloader machinery; represents the local attachment of the * disk for the benefit of the bootloader. Must be detached by * the caller using libxl__device_disk_local_initiate_detach. * (This is safe to do after ->callback() has happened since * the domain's kernel and initramfs will have been copied * out of the guest's disk into a temporary directory, mapped * as file references, and deleted. */ libxl__disk_local_state dls; uint32_t domid; /* outputs: * - caller must initialise kernel and ramdisk to point to file * references, these will be updated and mapped; * - caller must initialise cmdline to NULL, it will be updated with a * string allocated from the gc; */ libxl__file_reference *kernel, *ramdisk; const char *cmdline; /* private to libxl__run_bootloader */ char *outputpath, *outputdir, *logfile; libxl__openpty_state openpty; libxl__openpty_result ptys[2]; /* [0] is for bootloader */ libxl__ev_child child; libxl__domaindeathcheck deathcheck; int nargs, argsspace; const char **args; libxl__datacopier_state keystrokes, display; int rc, got_pollhup; }; _hidden void libxl__bootloader_init(libxl__bootloader_state *bl); /* Will definitely call st->callback, perhaps reentrantly. * If callback is passed rc==0, will have updated st->info appropriately */ _hidden void libxl__bootloader_run(libxl__egc*, libxl__bootloader_state *st); /*----- Generic Device Handling -----*/ #define LIBXL_DEFINE_DEVICE_ADD(type) \ int libxl_device_##type##_add(libxl_ctx *ctx, \ uint32_t domid, libxl_device_##type *type, \ const libxl_asyncop_how *ao_how) \ { \ AO_CREATE(ctx, domid, ao_how); \ libxl__ao_device *aodev; \ \ GCNEW(aodev); \ libxl__prepare_ao_device(ao, aodev); \ aodev->action = LIBXL__DEVICE_ACTION_ADD; \ aodev->callback = device_addrm_aocomplete; \ aodev->update_json = true; \ libxl__device_##type##_add(egc, domid, type, aodev); \ \ return AO_INPROGRESS; \ } #define LIBXL_DEFINE_DEVICES_ADD(type) \ void libxl__add_##type##s(libxl__egc *egc, libxl__ao *ao, uint32_t domid, \ libxl_domain_config *d_config, \ libxl__multidev *multidev) \ { \ AO_GC; \ int i; \ for (i = 0; i < d_config->num_##type##s; i++) { \ libxl__ao_device *aodev = libxl__multidev_prepare(multidev); \ libxl__device_##type##_add(egc, domid, &d_config->type##s[i], \ aodev); \ } \ } #define LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, remtype, removedestroy, f) \ int libxl_device_##type##_##removedestroy(libxl_ctx *ctx, \ uint32_t domid, libxl_device_##type *type, \ const libxl_asyncop_how *ao_how) \ { \ AO_CREATE(ctx, domid, ao_how); \ libxl__device *device; \ libxl__ao_device *aodev; \ int rc; \ \ GCNEW(device); \ rc = libxl__device_from_##type(gc, domid, type, device); \ if (rc != 0) goto out; \ \ GCNEW(aodev); \ libxl__prepare_ao_device(ao, aodev); \ aodev->action = LIBXL__DEVICE_ACTION_REMOVE; \ aodev->dev = device; \ aodev->callback = device_addrm_aocomplete; \ aodev->force = f; \ libxl__initiate_device_##remtype##_remove(egc, aodev); \ \ out: \ if (rc) return AO_CREATE_FAIL(rc); \ return AO_INPROGRESS; \ } #define LIBXL_DEFINE_DEVICE_REMOVE(type) \ LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, remove, 0) \ LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, generic, destroy, 1) #define LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(type) \ LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, remove, 0) \ LIBXL_DEFINE_DEVICE_REMOVE_EXT(type, type, destroy, 1) struct libxl_device_type { char *type; int skip_attach; /* Skip entry in domcreate_attach_devices() if 1 */ int ptr_offset; /* Offset of device array ptr in libxl_domain_config */ int num_offset; /* Offset of # of devices in libxl_domain_config */ int dev_elem_size; /* Size of one device element in array */ void (*add)(libxl__egc *, libxl__ao *, uint32_t, libxl_domain_config *, libxl__multidev *); void *(*list)(libxl_ctx *, uint32_t, int *); void (*dispose)(void *); int (*compare)(void *, void *); void (*merge)(libxl_ctx *, void *, void *); int (*dm_needed)(void *, unsigned); void (*update_config)(libxl__gc *, void *, void *); }; #define DEFINE_DEVICE_TYPE_STRUCT_X(name, sname, ...) \ const struct libxl_device_type libxl__ ## name ## _devtype = { \ .type = #sname, \ .ptr_offset = offsetof(libxl_domain_config, name ## s), \ .num_offset = offsetof(libxl_domain_config, num_ ## name ## s), \ .dev_elem_size = sizeof(libxl_device_ ## sname), \ .add = libxl__add_ ## name ## s, \ .list = (void *(*)(libxl_ctx *, uint32_t, int *)) \ libxl_device_ ## sname ## _list, \ .dispose = (void (*)(void *))libxl_device_ ## sname ## _dispose, \ .compare = (int (*)(void *, void *)) \ libxl_device_ ## sname ## _compare, \ __VA_ARGS__ \ } #define DEFINE_DEVICE_TYPE_STRUCT(name, ...) \ DEFINE_DEVICE_TYPE_STRUCT_X(name, name, __VA_ARGS__) static inline void **libxl__device_type_get_ptr( const struct libxl_device_type *dt, const libxl_domain_config *d_config) { return (void **)((void *)d_config + dt->ptr_offset); } static inline void *libxl__device_type_get_elem( const struct libxl_device_type *dt, const libxl_domain_config *d_config, int e) { return *libxl__device_type_get_ptr(dt, d_config) + dt->dev_elem_size * e; } static inline int *libxl__device_type_get_num( const struct libxl_device_type *dt, const libxl_domain_config *d_config) { return (int *)((void *)d_config + dt->num_offset); } extern const struct libxl_device_type libxl__disk_devtype; extern const struct libxl_device_type libxl__nic_devtype; extern const struct libxl_device_type libxl__vtpm_devtype; extern const struct libxl_device_type libxl__usbctrl_devtype; extern const struct libxl_device_type libxl__usbdev_devtype; extern const struct libxl_device_type libxl__pcidev_devtype; extern const struct libxl_device_type *device_type_tbl[]; /*----- Domain destruction -----*/ /* Domain destruction has been split into two functions: * * libxl__domain_destroy is the main destroy function, which detects * stubdoms and calls libxl__destroy_domid on the domain and its * stubdom if present, creating a different libxl__destroy_domid_state * for each one of them. * * libxl__destroy_domid actually destroys the domain, but it * doesn't check for stubdomains, since that would involve * recursion, which we want to avoid. */ typedef struct libxl__domain_destroy_state libxl__domain_destroy_state; typedef struct libxl__destroy_domid_state libxl__destroy_domid_state; typedef struct libxl__devices_remove_state libxl__devices_remove_state; typedef void libxl__domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, int rc); typedef void libxl__domid_destroy_cb(libxl__egc *egc, libxl__destroy_domid_state *dis, int rc); typedef void libxl__devices_remove_callback(libxl__egc *egc, libxl__devices_remove_state *drs, int rc); struct libxl__devices_remove_state { /* filled in by user */ libxl__ao *ao; uint32_t domid; libxl__devices_remove_callback *callback; int force; /* libxl_device_TYPE_destroy rather than _remove */ /* private */ libxl__multidev multidev; int num_devices; }; struct libxl__destroy_domid_state { /* filled in by user */ libxl__ao *ao; uint32_t domid; libxl__domid_destroy_cb *callback; /* private to implementation */ libxl__devices_remove_state drs; libxl__ev_child destroyer; bool soft_reset; }; struct libxl__domain_destroy_state { /* filled by the user */ libxl__ao *ao; uint32_t domid; libxl__domain_destroy_cb *callback; /* Private */ int rc; uint32_t stubdomid; libxl__destroy_domid_state stubdom; int stubdom_finished; libxl__destroy_domid_state domain; int domain_finished; bool soft_reset; }; /* * Entry point for domain destruction * This function checks for stubdom presence and then calls * libxl__destroy_domid on the passed domain and its stubdom if found. */ _hidden void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds); /* Used to destroy a domain with the passed id (it doesn't check for stubs) */ _hidden void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis); /* Entry point for devices destruction */ _hidden void libxl__devices_destroy(libxl__egc *egc, libxl__devices_remove_state *drs); /* Helper function to add a bunch of disks. This should be used when * the caller is inside an async op. "multidev" will NOT be prepared by * this function, so the caller must make sure to call * libxl__multidev_begin before calling this function. * * The "callback" will be called for each device, and the user is responsible * for calling libxl__ao_device_check_last on the callback. */ _hidden void libxl__add_disks(libxl__egc *egc, libxl__ao *ao, uint32_t domid, libxl_domain_config *d_config, libxl__multidev *multidev); _hidden void libxl__add_nics(libxl__egc *egc, libxl__ao *ao, uint32_t domid, libxl_domain_config *d_config, libxl__multidev *multidev); /*----- device model creation -----*/ /* First layer; wraps libxl__spawn_spawn. */ typedef struct libxl__dm_spawn_state libxl__dm_spawn_state; typedef void libxl__dm_spawn_cb(libxl__egc *egc, libxl__dm_spawn_state*, int rc /* if !0, error was logged */); struct libxl__dm_spawn_state { /* mixed - spawn.ao must be initialised by user; rest is private: */ libxl__spawn_state spawn; /* filled in by user, must remain valid: */ uint32_t guest_domid; /* domain being served */ libxl_domain_config *guest_config; libxl__domain_build_state *build_state; /* relates to guest_domid */ libxl__dm_spawn_cb *callback; }; _hidden void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state*); /* Stubdom device models. */ typedef struct { /* Mixed - user must fill in public parts EXCEPT callback, * which may be undefined on entry. (See above for details) */ libxl__dm_spawn_state dm; /* the stub domain device model */ /* filled in by user, must remain valid: */ libxl__dm_spawn_cb *callback; /* called as callback(,&sdss->dm,) */ /* private to libxl__spawn_stub_dm: */ libxl_domain_config dm_config; libxl__domain_build_state dm_state; libxl__dm_spawn_state pvqemu; libxl__destroy_domid_state dis; libxl__multidev multidev; libxl__xswait_state xswait; } libxl__stub_dm_spawn_state; _hidden void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state*); _hidden char *libxl__stub_dm_name(libxl__gc *gc, const char * guest_name); /* Qdisk backend launch helpers */ _hidden void libxl__spawn_qdisk_backend(libxl__egc *egc, libxl__dm_spawn_state *dmss); _hidden int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid); /*----- Domain creation -----*/ struct libxl__domain_create_state { /* filled in by user */ libxl__ao *ao; libxl_domain_config *guest_config; libxl_domain_config guest_config_saved; /* vanilla config */ int restore_fd, libxc_fd; int restore_fdfl; /* original flags of restore_fd */ int send_back_fd; libxl_domain_restore_params restore_params; uint32_t domid_soft_reset; libxl__domain_create_cb *callback; libxl_asyncprogress_how aop_console_how; /* private to domain_create */ int guest_domid; int device_type_idx; const char *colo_proxy_script; libxl__domain_build_state build_state; libxl__colo_restore_state crs; libxl__checkpoint_devices_state cds; libxl__bootloader_state bl; libxl__stub_dm_spawn_state sdss; /* If we're not doing stubdom, we use only dmss.dm, * for the non-stubdom device model. */ libxl__stream_read_state srs; /* necessary if the domain creation failed and we have to destroy it */ libxl__domain_destroy_state dds; libxl__multidev multidev; }; _hidden int libxl__device_nic_set_devids(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid); /*----- Domain suspend (save) functions -----*/ /* calls dss->callback when done */ _hidden void libxl__domain_save(libxl__egc *egc, libxl__domain_save_state *dss); /* calls libxl__xc_domain_suspend_done when done */ _hidden void libxl__xc_domain_save(libxl__egc *egc, libxl__domain_save_state *dss, libxl__save_helper_state *shs); /* If rc==0 then retval is the return value from xc_domain_save * and errnoval is the errno value it provided. * If rc!=0, retval and errnoval are undefined. */ _hidden void libxl__xc_domain_save_done(libxl__egc*, void *dss_void, int rc, int retval, int errnoval); /* Used by asynchronous callbacks: ie ones which xc regards as * returning a value, but which we want to handle asynchronously. * Such functions' actual callback function return void in libxl * When they are ready to indicate completion, they call this. */ void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc, libxl__save_helper_state *shs, int return_value); _hidden void libxl__domain_suspend_common_switch_qemu_logdirty (int domid, unsigned int enable, void *data); _hidden void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc, int domid, unsigned enable, libxl__logdirty_switch *lds); _hidden int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss, char **buf, uint32_t *len); _hidden int libxl__restore_emulator_xenstore_data (libxl__domain_create_state *dcs, const char *ptr, uint32_t size); /* calls libxl__xc_domain_restore_done when done */ _hidden void libxl__xc_domain_restore(libxl__egc *egc, libxl__domain_create_state *dcs, libxl__save_helper_state *shs, int hvm, int pae, int superpages); /* If rc==0 then retval is the return value from xc_domain_save * and errnoval is the errno value it provided. * If rc!=0, retval and errnoval are undefined. */ _hidden void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void, int rc, int retval, int errnoval); _hidden void libxl__save_helper_init(libxl__save_helper_state *shs); _hidden void libxl__save_helper_abort(libxl__egc *egc, libxl__save_helper_state *shs); static inline bool libxl__save_helper_inuse(const libxl__save_helper_state *shs) { return libxl__ev_child_inuse(&shs->child); } /* Each time the dm needs to be saved, we must call suspend and then save */ _hidden int libxl__domain_suspend_device_model(libxl__gc *gc, libxl__domain_suspend_state *dsps); _hidden const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid); /* calls dsps->callback_common_done when done */ _hidden void libxl__domain_suspend(libxl__egc *egc, libxl__domain_suspend_state *dsps); /* used by libxc to suspend the guest during migration */ _hidden void libxl__domain_suspend_callback(void *data); /* Remus setup and teardown */ _hidden void libxl__remus_setup(libxl__egc *egc, libxl__remus_state *rs); _hidden void libxl__remus_teardown(libxl__egc *egc, libxl__remus_state *rs, int rc); _hidden void libxl__remus_restore_setup(libxl__egc *egc, libxl__domain_create_state *dcs); /* * Convenience macros. */ /* * CONTAINER_OF work like this. Given: * typedef struct { * ... * member_type member_name; * ... * } outer_type; * outer_type outer, *outer_var; * member_type *inner_ptr = &outer->member_name; * * Then, effectively: * outer_type *CONTAINER_OF(member_type *inner_ptr, * *outer_var, // or type name for outer_type * member_name); * * So that: * CONTAINER_OF(inner_ptr, *outer_var, member_name) == &outer * CONTAINER_OF(inner_ptr, outer_type, member_name) == &outer */ #define CONTAINER_OF(inner_ptr, outer, member_name) \ ({ \ typeof(outer) *container_of_; \ container_of_ = (void*)((char*)(inner_ptr) - \ offsetof(typeof(outer), member_name)); \ (void)(&container_of_->member_name == \ (typeof(inner_ptr))0) /* type check */; \ container_of_; \ }) #define FILLZERO LIBXL_FILLZERO /* * All of these assume (or define) * libxl__gc *gc; * as a local variable. */ #define GC_INIT(ctx) libxl__gc gc[1]; LIBXL_INIT_GC(gc[0],ctx) #define GC_FREE libxl__free_all(gc) #define CTX libxl__gc_owner(gc) #define NOGC (&CTX->nogc_gc) /* pass only to consenting functions */ /* Allocation macros all of which use the gc. */ #define ARRAY_SIZE_OK(ptr, nmemb) ((nmemb) < INT_MAX / (sizeof(*(ptr)) * 2)) /* * Expression statement *GCNEW( *var); * Uses libxl__gc *gc; * * Allocates a new object of type from the gc and zeroes it * with memset. Sets var to point to the new object or zero (setting * errno). Returns the new value of var. */ #define GCNEW(var) \ (((var) = libxl__zalloc((gc),sizeof(*(var))))) /* * Expression statement *GCNEW_ARRAY( *var, ssize_t nmemb); * Uses libxl__gc *gc; * * Like GCNEW but allocates an array of nmemb elements, as if from * calloc. Does check for integer overflow due to large nmemb. If * nmemb is 0 may succeed by returning 0. */ #define GCNEW_ARRAY(var, nmemb) \ ((var) = libxl__calloc((gc), (nmemb), sizeof(*(var)))) /* * Expression statement *GCREALLOC_ARRAY( *var, size_t nmemb); * Uses libxl__gc *gc; * * Reallocates the array var to be of size nmemb elements. Updates * var and returns the new value of var. Does check for integer * overflow due to large nmemb. * * Do not pass nmemb==0. old may be 0 on entry. */ #define GCREALLOC_ARRAY(var, nmemb) \ (assert(nmemb > 0), \ assert(ARRAY_SIZE_OK((var), (nmemb))), \ (var) = libxl__realloc((gc), (var), (nmemb)*sizeof(*(var)))) /* * Expression char *GCSPRINTF(const char *fmt, ...); * Uses libxl__gc *gc; * * Trivial convenience wrapper for libxl__sprintf. */ #define GCSPRINTF(fmt, ...) (libxl__sprintf((gc), (fmt), __VA_ARGS__)) /* * Expression statements * void LOG(, const char *fmt, ...); * void LOGE(, const char *fmt, ...); * void LOGEV(, int errnoval, const char *fmt, ...); * * void LOGD(, uint32_t domid, const char *fmt, ...); * void LOGED(, uint32_t domid, const char *fmt, ...); * void LOGEVD(, int errnoval, uint32_t domid, const char *fmt, ...); * Use * libxl__gc *gc; * * Trivial convenience wrappers for LIBXL__LOG, LIBXL__LOG_ERRNO, * LIBXL__LOG_ERRNOVAL, LIBXL__LOGD, LIBXL__LOGD_ERRNO and * LIBXL__LOGD_ERRNOVAL respectively (and thus for libxl__log). * * XTL_ should exist and be an xentoollog.h log level * So should be one of * DEBUG VERBOSE DETAIL PROGRESS INFO NOTICE WARN ERROR ERROR CRITICAL * Of these, most of libxl uses * DEBUG INFO WARN ERROR * * The LOG*D family will preprend the log message with a string formatted * as follows: 'Domain %PRIu32:'. This should help better automatic sorting * of log messages per domain. */ #define LOG(l,f, ...) LIBXL__LOG(CTX,XTL_##l,(f),##__VA_ARGS__) #define LOGE(l,f, ...) LIBXL__LOG_ERRNO(CTX,XTL_##l,(f),##__VA_ARGS__) #define LOGEV(l,e,f, ...) LIBXL__LOG_ERRNOVAL(CTX,XTL_##l,(e),(f),##__VA_ARGS__) #define LOGD(l,d,f, ...) LIBXL__LOGD(CTX,XTL_##l,(d),(f),##__VA_ARGS__) #define LOGED(l,d,f, ...) LIBXL__LOGD_ERRNO(CTX,XTL_##l,(d),(f),##__VA_ARGS__) #define LOGEVD(l,e,d,f, ...) LIBXL__LOGD_ERRNOVAL(CTX,XTL_##l,(e),(d),(f),##__VA_ARGS__) /* Locking functions. See comment for "lock" member of libxl__ctx. */ static inline void libxl__ctx_lock(libxl_ctx *ctx) { int r = pthread_mutex_lock(&ctx->lock); assert(!r); } static inline void libxl__ctx_unlock(libxl_ctx *ctx) { int r = pthread_mutex_unlock(&ctx->lock); assert(!r); } #define CTX_LOCK (libxl__ctx_lock(CTX)) #define CTX_UNLOCK (libxl__ctx_unlock(CTX)) /* * Automatic NUMA placement * * These functions and data structures deal with the initial placement of a * domain onto the host NUMA nodes. * * The key concept here is the one of "NUMA placement candidate", which is * basically a set of nodes whose characteristics have been successfully * checked against some specific requirements. More precisely, a candidate * is the nodemap associated with one of the possible subset of the host * NUMA nodes providing a certain amount of free memory, or a given number * of cpus, or even both (depending in what the caller wants). For * convenience of use, some of this information are stored within the * candidate itself, instead of always being dynamically computed. A single * node can be valid placement candidate, as well as it is possible for a * candidate to contain all the nodes of the host. The fewer nodes there * are in a candidate, the better performance a domain placed onto it * should get (at least from a NUMA point of view). For instance, looking * for a numa candidates with 2GB of free memory means we want the subsets * of the host NUMA nodes with, cumulatively, at least 2GB of free memory. * This condition can be satisfied by just one particular node, or it may * require more nodes, depending on the characteristics of the host, on how * many domains have been created already, on how big they are, etc. * * The intended usage is as follows: * 1. first of all, call libxl__get_numa_candidates(), and specify the * proper constraints to it (e.g., the amount of memory a domain need * as the minimum amount of free memory for the candidates). If a * candidate comparison function is provided, the candidate with fewer * nodes that is found to be best according to what such fucntion says * is returned. If no comparison function is passed, the very first * candidate is. * 2. The chosen candidate's nodemap should be utilized for computing the * actual affinity of the domain which, given the current NUMA support * in the hypervisor, is what determines the placement of the domain's * vcpus and memory. */ typedef struct { int nr_cpus, nr_nodes; int nr_vcpus; uint64_t free_memkb; libxl_bitmap nodemap; } libxl__numa_candidate; /* Signature for the comparison function between two candidates */ typedef int (*libxl__numa_candidate_cmpf)(const libxl__numa_candidate *c1, const libxl__numa_candidate *c2); /* * This looks for the best NUMA placement candidate satisfying some * specific conditions. If min_nodes and/or max_nodes are not 0, their * value is used to determine the minimum and maximum number of nodes the * candidate can have. If they are 0, it means the candidate can contain * from 1 node (min_nodes=0) to the total number of nodes of the host * (max_ndoes=0). If min_free_memkb and/or min_cpus are not 0, the caller * only wants candidates with at least the amount of free memory and the * number of cpus they specify, respectively. If they are 0, the * candidates' free memory and/or number of cpus won't be checked at all. * * Candidates are compared among each others by calling numa_cmpf(), which * is where the heuristics for determining which candidate is the best * one is actually implemented. The only bit of it that is hardcoded in * this function is the fact that candidates with fewer nodes are always * preferrable. * * If at least one suitable candidate is found, it is returned in cndt_out, * cndt_found is set to one, and the function returns successfully. On the * other hand, if not even one single candidate can be found, the function * still returns successfully but cndt_found will be zero. * * Finally, suitable_cpumap is useful for telling that only the cpus in that * mask should be considered when generating placement candidates (for * example because of cpupools). * * It is up to the function to properly allocate cndt_out (by calling * libxl__numa_candidate_alloc()), while it is the caller that should init * (libxl__numa_candidate_init()) and free (libxl__numa_candidate_dispose()) * it. */ _hidden int libxl__get_numa_candidate(libxl__gc *gc, uint64_t min_free_memkb, int min_cpus, int min_nodes, int max_nodes, const libxl_bitmap *suitable_cpumap, libxl__numa_candidate_cmpf numa_cmpf, libxl__numa_candidate *cndt_out, int *cndt_found); /* Initialization, allocation and deallocation for placement candidates */ static inline void libxl__numa_candidate_init(libxl__numa_candidate *cndt) { cndt->free_memkb = 0; cndt->nr_cpus = cndt->nr_nodes = cndt->nr_vcpus = 0; libxl_bitmap_init(&cndt->nodemap); } static inline int libxl__numa_candidate_alloc(libxl__gc *gc, libxl__numa_candidate *cndt) { return libxl_node_bitmap_alloc(CTX, &cndt->nodemap, 0); } static inline void libxl__numa_candidate_dispose(libxl__numa_candidate *cndt) { libxl_bitmap_dispose(&cndt->nodemap); } /* Retrieve (in nodemap) the node map associated to placement candidate cndt */ static inline void libxl__numa_candidate_get_nodemap(libxl__gc *gc, const libxl__numa_candidate *cndt, libxl_bitmap *nodemap) { libxl_bitmap_copy(CTX, nodemap, &cndt->nodemap); } /* Set the node map of placement candidate cndt to match nodemap */ static inline void libxl__numa_candidate_put_nodemap(libxl__gc *gc, libxl__numa_candidate *cndt, const libxl_bitmap *nodemap) { libxl_bitmap_copy(CTX, &cndt->nodemap, nodemap); } /* Check if vNUMA config is valid. Returns 0 if valid, * ERROR_VNUMA_CONFIG_INVALID otherwise. */ int libxl__vnuma_config_check(libxl__gc *gc, const libxl_domain_build_info *b_info, const libxl__domain_build_state *state); int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *b_info, libxl__domain_build_state *state); int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *b_info, libxl__domain_build_state *state); int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *b_info, libxl__domain_build_state *state, struct xc_dom_image *dom); bool libxl__vnuma_configured(const libxl_domain_build_info *b_info); _hidden int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid, const libxl_ms_vm_genid *id); /* Som handy macros for defbool type. */ #define LIBXL__DEFBOOL_DEFAULT (0) #define LIBXL__DEFBOOL_FALSE (-1) #define LIBXL__DEFBOOL_TRUE (1) #define LIBXL__DEFBOOL_STR_DEFAULT "" #define LIBXL__DEFBOOL_STR_FALSE "False" #define LIBXL__DEFBOOL_STR_TRUE "True" static inline int libxl__defbool_is_default(libxl_defbool *db) { return !db->val; } /* * Inserts "elm_new" into the sorted list "head". * * "elm_search" must be a loop search variable of the same type as * "elm_new". "new_after_search_p" must be an expression which is * true iff the element "elm_new" sorts after the element * "elm_search". * * "search_body" can be empty, or some declaration(s) and statement(s) * needed for "new_after_search_p". */ #define LIBXL_TAILQ_INSERT_SORTED(head, entry, elm_new, elm_search, \ search_body, new_after_search_p) \ do { \ for ((elm_search) = LIBXL_TAILQ_FIRST((head)); \ (elm_search); \ (elm_search) = LIBXL_TAILQ_NEXT((elm_search), entry)) { \ search_body; \ if (!(new_after_search_p)) \ break; \ } \ /* now elm_search is either the element before which we want \ * to place elm_new, or NULL meaning we want to put elm_new at \ * the end */ \ if ((elm_search)) \ LIBXL_TAILQ_INSERT_BEFORE((elm_search), (elm_new), entry); \ else \ LIBXL_TAILQ_INSERT_TAIL((head), (elm_new), entry); \ } while(0) /* * int CTYPE(ISFOO, char c); * int CTYPE(toupper, char c); * int CTYPE(tolower, char c); * * This is necessary because passing a simple char to a ctype.h * is forbidden. ctype.h macros take ints derived from _unsigned_ chars. * * If you have a char which might be EOF then you should already have * it in an int representing an unsigned char, and you can use the * macros directly. This generally happens only with values * from fgetc et al. * * For any value known to be a character (eg, anything that came from * a char[]), use CTYPE. */ #define CTYPE(isfoo,c) (isfoo((unsigned char)(c))) int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_defbool *p); int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o, bool *p); int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_mac *p); int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_bitmap *p); int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_uuid *p); int libxl__cpuid_policy_list_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_cpuid_policy_list *p); int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_string_list *p); int libxl__key_value_list_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_key_value_list *p); int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_hwcap *p); int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_ms_vm_genid *p); int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o, void *p); int libxl__uint8_parse_json(libxl__gc *gc, const libxl__json_object *o, void *p); int libxl__uint16_parse_json(libxl__gc *gc, const libxl__json_object *o, void *p); int libxl__uint32_parse_json(libxl__gc *gc, const libxl__json_object *o, void *p); int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o, void *p); int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o, char **p); int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len); #include "_libxl_types_private.h" #include "_libxl_types_internal_private.h" /* This always return false, there's no "default value" for hw cap */ static inline int libxl__hwcap_is_default(libxl_hwcap *hwcap) { return 0; } static inline int libxl__string_list_is_empty(libxl_string_list *psl) { return !libxl_string_list_length(psl); } static inline int libxl__key_value_list_is_empty(libxl_key_value_list *pkvl) { return !libxl_key_value_list_length(pkvl); } int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl); /* Portability note: a proper flock(2) implementation is required */ typedef struct { libxl__carefd *carefd; char *path; /* path of the lock file itself */ } libxl__domain_userdata_lock; /* The CTX_LOCK must be held around uses of this lock */ libxl__domain_userdata_lock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid); void libxl__unlock_domain_userdata(libxl__domain_userdata_lock *lock); /* * Retrieve / store domain configuration from / to libxl private * data store. The registry entry in libxl private data store * is "libxl-json". * Caller must hold user data lock. */ int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid, libxl_domain_config *d_config); int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid, libxl_domain_config *d_config); /* ------ Things related to updating domain configurations ----- */ void libxl__update_domain_configuration(libxl__gc *gc, libxl_domain_config *dst, const libxl_domain_config *src); /* Target memory in xenstore is different from what user has * asked for. The difference is video_memkb + (possible) fudge. * See libxl_set_memory_target. */ static inline uint64_t libxl__get_targetmem_fudge(libxl__gc *gc, const libxl_domain_build_info *info) { int64_t mem_target_fudge = (info->type == LIBXL_DOMAIN_TYPE_HVM && info->max_memkb > info->target_memkb) ? LIBXL_MAXMEM_CONSTANT : 0; return info->video_memkb + mem_target_fudge; } int libxl__get_memory_target(libxl__gc *gc, uint32_t domid, uint64_t *out_target_memkb, uint64_t *out_max_memkb); void libxl__xcinfo2xlinfo(libxl_ctx *ctx, const xc_domaininfo_t *xcinfo, libxl_dominfo *xlinfo); /* Macros used to compare device identifier. Returns true if the two * devices have same identifier. */ #define COMPARE_DEVID(a, b) ((a)->devid == (b)->devid) #define COMPARE_DISK(a, b) (!strcmp((a)->vdev, (b)->vdev)) #define COMPARE_PCI(a, b) ((a)->func == (b)->func && \ (a)->bus == (b)->bus && \ (a)->dev == (b)->dev) #define COMPARE_USB(a, b) ((a)->ctrl == (b)->ctrl && \ (a)->port == (b)->port) #define COMPARE_USBCTRL(a, b) ((a)->devid == (b)->devid) /* DEVICE_ADD * * Add a device in libxl_domain_config structure * * It takes 6 parameters: * type: the type of the device, say nic, vtpm, disk, pci etc * ptr: pointer to the start of the array, the array must be * of type libxl_device_#type * domid: domain id of target domain * dev: the device that is to be added / removed / updated * compare: the COMPARE_* macro used to compare @dev's identifier to * those in the array pointed to by @ptr * d_config: pointer to template domain config * * For most device types (nic, vtpm), the array pointer @ptr can be * derived from @type, pci device being the exception, hence we need * to have @ptr. * * If there is already a device with the same identifier in d_config, * that entry is updated. */ #define DEVICE_ADD(type, ptr, domid, dev, compare, d_config) \ ({ \ int DA_x; \ libxl_device_##type *DA_p = NULL; \ \ /* Check for existing device */ \ for (DA_x = 0; DA_x < (d_config)->num_##ptr; DA_x++) { \ if (compare(&(d_config)->ptr[DA_x], (dev))) { \ DA_p = &(d_config)->ptr[DA_x]; \ break; \ } \ } \ \ if (!DA_p) { \ (d_config)->ptr = \ libxl__realloc(NOGC, (d_config)->ptr, \ ((d_config)->num_##ptr + 1) * \ sizeof(libxl_device_##type)); \ DA_p = &(d_config)->ptr[(d_config)->num_##ptr]; \ (d_config)->num_##ptr++; \ } else { \ libxl_device_##type##_dispose(DA_p); \ } \ \ libxl_device_##type##_init(DA_p); \ libxl_device_##type##_copy(CTX, DA_p, (dev)); \ }) /* This function copies X bytes from source to destination bitmap, * where X is the smaller of the two sizes. * * If destination's size is larger than source, the extra bytes are * untouched. * * XXX This is introduced to fix a regression for 4.5. It shall * be revisited in 4.6 time frame. */ void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr, const libxl_bitmap *sptr); int libxl__count_physical_sockets(libxl__gc *gc, int *sockets); _hidden int libxl__read_sysfs_file_contents(libxl__gc *gc, const char *filename, void **data_r, int *datalen_r); #define LIBXL_QEMU_USER_PREFIX "xen-qemuuser" #define LIBXL_QEMU_USER_BASE LIBXL_QEMU_USER_PREFIX"-domid" #define LIBXL_QEMU_USER_SHARED LIBXL_QEMU_USER_PREFIX"-shared" static inline bool libxl__acpi_defbool_val(const libxl_domain_build_info *b_info) { return libxl_defbool_val(b_info->acpi) && libxl_defbool_val(b_info->u.hvm.acpi); } #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlutil.h0000664000175000017500000001100113256712137015101 0ustar smbsmb/* * Copyright (C) 2010 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef LIBXLUTIL_H #define LIBXLUTIL_H #include #include "libxl.h" enum XLU_ConfigValueType { XLU_STRING, XLU_LIST, }; /* Unless otherwise stated, all functions return an errno value. */ typedef struct XLU_Config XLU_Config; typedef struct XLU_ConfigList XLU_ConfigList; typedef struct XLU_ConfigValue XLU_ConfigValue; XLU_Config *xlu_cfg_init(FILE *report, const char *report_filename); /* 0 means we got ENOMEM. */ /* report_filename is copied; report is saved and must remain valid * until the Config is destroyed. */ int xlu_cfg_readfile(XLU_Config*, const char *real_filename); int xlu_cfg_readdata(XLU_Config*, const char *data, int length); /* If these fail, then it is undefined behaviour to call xlu_cfg_get_... * functions. You have to just xlu_cfg_destroy. */ void xlu_cfg_destroy(XLU_Config*); /* All of the following print warnings to "report" if there is a problem. * Return values are: * 0 OK * ESRCH not defined * EINVAL value found but wrong format for request (prints warning unless dont_warn=true) * ERANGE value out of range (from strtol) */ int xlu_cfg_get_string(const XLU_Config*, const char *n, const char **value_r, int dont_warn); /* free/strdup version */ int xlu_cfg_replace_string(const XLU_Config *cfg, const char *n, char **value_r, int dont_warn); int xlu_cfg_get_long(const XLU_Config*, const char *n, long *value_r, int dont_warn); int xlu_cfg_get_defbool(const XLU_Config*, const char *n, libxl_defbool *b, int dont_warn); int xlu_cfg_get_list(const XLU_Config*, const char *n, XLU_ConfigList **list_r /* may be 0 */, int *entries_r /* may be 0 */, int dont_warn); /* there is no need to free *list_r; lifetime is that of the XLU_Config */ int xlu_cfg_get_list_as_string_list(const XLU_Config *cfg, const char *n, libxl_string_list *sl, int dont_warn); const char *xlu_cfg_get_listitem(const XLU_ConfigList*, int entry); /* xlu_cfg_get_listitem cannot fail, except that if entry is * out of range it returns 0 (not setting errno) */ enum XLU_ConfigValueType xlu_cfg_value_type(const XLU_ConfigValue *value); int xlu_cfg_value_get_string(const XLU_Config *cfg, XLU_ConfigValue *value, char **value_r, int dont_warn); int xlu_cfg_value_get_list(const XLU_Config *cfg, XLU_ConfigValue *value, XLU_ConfigList **value_r, int dont_warn); XLU_ConfigValue *xlu_cfg_get_listitem2(const XLU_ConfigList *list, int entry); /* * Disk specification parsing. */ int xlu_disk_parse(XLU_Config *cfg, int nspecs, const char *const *specs, libxl_device_disk *disk); /* disk must have been initialised. * * On error, returns errno value. Bad strings cause EINVAL and * print a message to cfg's report (that's all cfg is used for). * * Normally one would pass nspecs==1 and only specs[0]. But it is * permitted to pass more strings in which case each is parsed as a * string containing a collection of parameters (but they all refer * to of the configuration for a single disk). * * nspecs==0 is permitted but since it does not specify some mandatory * properties, it produces a run-time configuration error if the * resulting disk struct is used with libxl. */ /* * PCI specification parsing */ int xlu_pci_parse_bdf(XLU_Config *cfg, libxl_device_pci *pcidev, const char *str); /* * RDM parsing */ int xlu_rdm_parse(XLU_Config *cfg, libxl_rdm_reserve *rdm, const char *str); /* * Vif rate parsing. */ int xlu_vif_parse_rate(XLU_Config *cfg, const char *rate, libxl_device_nic *nic); #endif /* LIBXLUTIL_H */ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/idl.py0000664000175000017500000003061213256712137014053 0ustar smbsmbimport sys PASS_BY_VALUE = 1 PASS_BY_REFERENCE = 2 DIR_NONE = 0 DIR_IN = 1 DIR_OUT = 2 DIR_BOTH = 3 _default_namespace = "" def namespace(s): if type(s) != str: raise TypeError, "Require a string for the default namespace." global _default_namespace _default_namespace = s def _get_default_namespace(): global _default_namespace return _default_namespace _default_hidden = False def hidden(b): global _default_hidden _default_hidden = b def _get_default_hidden(): global _default_hidden return _default_hidden class Type(object): def __init__(self, typename, **kwargs): self.namespace = kwargs.setdefault('namespace', _get_default_namespace()) self._hidden = kwargs.setdefault('hidden', _get_default_hidden()) self.dir = kwargs.setdefault('dir', DIR_BOTH) if self.dir not in [DIR_NONE, DIR_IN, DIR_OUT, DIR_BOTH]: raise ValueError self.passby = kwargs.setdefault('passby', PASS_BY_VALUE) if self.passby not in [PASS_BY_VALUE, PASS_BY_REFERENCE]: raise ValueError self.private = kwargs.setdefault('private', False) if typename is None: # Anonymous type self.typename = None self.rawname = None elif self.namespace is None: # e.g. system provided types self.typename = typename self.rawname = typename else: self.typename = self.namespace + typename self.rawname = typename if self.typename is not None: self.dispose_fn = kwargs.setdefault('dispose_fn', self.typename + "_dispose") else: self.dispose_fn = kwargs.setdefault('dispose_fn', None) self.autogenerate_dispose_fn = kwargs.setdefault('autogenerate_dispose_fn', True) if self.typename is not None: self.copy_fn = kwargs.setdefault('copy_fn', self.typename + "_copy") else: self.copy_fn = kwargs.setdefault('copy_fn', None) self.autogenerate_copy_fn = kwargs.setdefault('autogenerate_copy_fn', True) self.init_fn = kwargs.setdefault('init_fn', None) self.init_val = kwargs.setdefault('init_val', None) self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', False) self.check_default_fn = kwargs.setdefault('check_default_fn', None) if self.typename is not None and not self.private: self.json_gen_fn = kwargs.setdefault('json_gen_fn', self.typename + "_gen_json") self.json_parse_type = kwargs.setdefault('json_parse_type', "JSON_ANY") if self.namespace is not None: self.json_parse_fn = kwargs.setdefault('json_parse_fn', self.namespace + "_" + self.rawname + "_parse_json") else: self.json_parse_fn = kwargs.setdefault('json_parse_fn', self.typename + "_parse_json") else: self.json_gen_fn = kwargs.setdefault('json_gen_fn', None) self.json_parse_type = kwargs.setdefault('json_parse_type', None) self.json_parse_fn = kwargs.setdefault('json_parse_fn', None) self.autogenerate_json = kwargs.setdefault('autogenerate_json', True) def marshal_in(self): return self.dir in [DIR_IN, DIR_BOTH] def marshal_out(self): return self.dir in [DIR_OUT, DIR_BOTH] def hidden(self): if self._hidden: return "_hidden " else: return "" def make_arg(self, n, passby=None): if passby is None: passby = self.passby if passby == PASS_BY_REFERENCE: return "%s *%s" % (self.typename, n) else: return "%s %s" % (self.typename, n) def pass_arg(self, n, isref=None, passby=None): if passby is None: passby = self.passby if isref is None: isref = self.passby == PASS_BY_REFERENCE if passby == PASS_BY_REFERENCE: if isref: return "%s" % (n) else: return "&%s" % (n) else: if isref: return "*%s" % (n) else: return "%s" % (n) class Builtin(Type): """Builtin type""" def __init__(self, typename, **kwargs): kwargs.setdefault('dispose_fn', None) kwargs.setdefault('autogenerate_dispose_fn', False) kwargs.setdefault('autogenerate_json', False) Type.__init__(self, typename, **kwargs) class Number(Builtin): def __init__(self, ctype, **kwargs): kwargs.setdefault('namespace', None) kwargs.setdefault('dispose_fn', None) kwargs.setdefault('copy_fn', None) kwargs.setdefault('signed', False) kwargs.setdefault('json_gen_fn', "yajl_gen_integer") kwargs.setdefault('json_parse_type', "JSON_INTEGER") # json_parse_fn might be overriden on specific type kwargs.setdefault('json_parse_fn', "libxl__int_parse_json") self.signed = kwargs['signed'] Builtin.__init__(self, ctype, **kwargs) class UInt(Number): def __init__(self, w, **kwargs): kwargs.setdefault('namespace', None) kwargs.setdefault('dispose_fn', None) kwargs.setdefault('json_parse_fn', "libxl__uint%d_parse_json" % w) kwargs.setdefault('copy_fn', None) Number.__init__(self, "uint%d_t" % w, **kwargs) self.width = w class EnumerationValue(object): def __init__(self, enum, value, name, **kwargs): self.enum = enum self.valuename = str.upper(name) self.rawname = str.upper(enum.rawname) + "_" + self.valuename self.name = str.upper(enum.value_namespace) + self.rawname self.value = value class Enumeration(Type): def __init__(self, typename, values, **kwargs): kwargs.setdefault('dispose_fn', None) kwargs.setdefault('copy_fn', None) kwargs.setdefault('json_parse_type', "JSON_STRING") Type.__init__(self, typename, **kwargs) self.value_namespace = kwargs.setdefault('value_namespace', self.namespace) self.values = [] for v in values: # (value, name) (num,name) = v self.values.append(EnumerationValue(self, num, name, typename=self.rawname)) def lookup(self, name): for v in self.values: if v.valuename == str.upper(name): return v return ValueError class Field(object): """An element of an Aggregate type""" def __init__(self, type, name, **kwargs): self.type = type self.name = name self.const = kwargs.setdefault('const', False) self.enumname = kwargs.setdefault('enumname', None) self.init_val = kwargs.setdefault('init_val', None) class Aggregate(Type): """A type containing a collection of other types""" def __init__(self, kind, typename, fields, **kwargs): kwargs.setdefault('json_parse_type', "JSON_MAP") Type.__init__(self, typename, **kwargs) if self.typename is not None: self.init_fn = kwargs.setdefault('init_fn', self.typename + "_init") else: self.init_fn = kwargs.setdefault('init_fn', None) self.autogenerate_init_fn = kwargs.setdefault('autogenerate_init_fn', True) self.kind = kind self.fields = [] for f in fields: # (name, type[, {kw args}]) if len(f) == 2: n,t = f kw = {} elif len(f) == 3: n,t,kw = f else: raise ValueError if n is None: raise ValueError self.fields.append(Field(t,n,**kw)) # Returns a tuple (stem, field-expr) # # field-expr is a C expression for a field "f" within the struct # "v". # # stem is the stem common to both "f" and any other sibbling field # within the "v". def member(self, v, f, isref): if isref: deref = v + "->" else: deref = v + "." if f.name is None: # Anonymous return (deref, deref) else: return (deref, deref + f.name) class Struct(Aggregate): def __init__(self, name, fields, **kwargs): kwargs.setdefault('passby', PASS_BY_REFERENCE) Aggregate.__init__(self, "struct", name, fields, **kwargs) def has_fields(self): return len(self.fields) != 0 class Union(Aggregate): def __init__(self, name, fields, **kwargs): # Generally speaking some intelligence is required to free a # union therefore any specific instance of this class will # need to provide an explicit destructor function. kwargs.setdefault('passby', PASS_BY_REFERENCE) kwargs.setdefault('dispose_fn', None) Aggregate.__init__(self, "union", name, fields, **kwargs) class KeyedUnion(Aggregate): """A union which is keyed of another variable in the parent structure""" def __init__(self, name, keyvar_type, keyvar_name, fields, **kwargs): Aggregate.__init__(self, "union", name, [], **kwargs) if not isinstance(keyvar_type, Enumeration): raise ValueError kv_kwargs = dict([(x.lstrip('keyvar_'),y) for (x,y) in kwargs.items() if x.startswith('keyvar_')]) self.keyvar = Field(keyvar_type, keyvar_name, **kv_kwargs) for f in fields: # (name, enum, type) e, ty = f ev = keyvar_type.lookup(e) en = ev.name self.fields.append(Field(ty, e, enumname=en)) # # Standard Types # void = Builtin("void *", namespace = None) bool = Builtin("bool", namespace = None, copy_fn=None, json_gen_fn = "yajl_gen_bool", json_parse_type = "JSON_BOOL", json_parse_fn = "libxl__bool_parse_json", autogenerate_json = False) size_t = Number("size_t", namespace = None) integer = Number("int", namespace = None, signed = True) uint8 = UInt(8) uint16 = UInt(16) uint32 = UInt(32) uint64 = UInt(64, json_gen_fn = "libxl__uint64_gen_json") string = Builtin("char *", namespace = None, copy_fn = "libxl_string_copy", dispose_fn = "free", json_gen_fn = "libxl__string_gen_json", json_parse_type = "JSON_STRING | JSON_NULL", json_parse_fn = "libxl__string_parse_json", autogenerate_json = False) class Array(Type): """An array of the same type""" def __init__(self, elem_type, lenvar_name, **kwargs): kwargs.setdefault('dispose_fn', 'free') kwargs.setdefault('json_parse_type', 'JSON_ARRAY') Type.__init__(self, namespace=elem_type.namespace, typename=elem_type.rawname + " *", **kwargs) lv_kwargs = dict([(x.lstrip('lenvar_'),y) for (x,y) in kwargs.items() if x.startswith('lenvar_')]) self.lenvar = Field(integer, lenvar_name, **lv_kwargs) self.elem_type = elem_type class OrderedDict(dict): """A dictionary which remembers insertion order. push to back on duplicate insertion""" def __init__(self): dict.__init__(self) self.__ordered = [] def __setitem__(self, key, value): try: self.__ordered.remove(key) except ValueError: pass self.__ordered.append(key) dict.__setitem__(self, key, value) def ordered_keys(self): return self.__ordered def ordered_values(self): return [self[x] for x in self.__ordered] def ordered_items(self): return [(x,self[x]) for x in self.__ordered] def parse(f): print >>sys.stderr, "Parsing %s" % f globs = {} locs = OrderedDict() for n,t in globals().items(): if isinstance(t, Type): globs[n] = t elif isinstance(t,type(object)) and issubclass(t, Type): globs[n] = t elif n in ['PASS_BY_REFERENCE', 'PASS_BY_VALUE', 'DIR_NONE', 'DIR_IN', 'DIR_OUT', 'DIR_BOTH', 'namespace', 'hidden']: globs[n] = t try: execfile(f, globs, locs) except SyntaxError,e: raise SyntaxError, \ "Errors were found at line %d while processing %s:\n\t%s"\ %(e.lineno,f,e.text) types = [t for t in locs.ordered_values() if isinstance(t,Type)] builtins = [t for t in types if isinstance(t,Builtin)] types = [t for t in types if not isinstance(t,Builtin)] return (builtins,types) xen-4.9.2/tools/libxl/libxl_utils.c0000664000175000017500000007634113256712137015440 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include #include "libxl_internal.h" #include "_paths.h" #ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE const #endif char *libxl_basename(const char *name) { const char *filename; if (name == NULL) return strdup("."); if (name[0] == '\0') return strdup("."); filename = strrchr(name, '/'); if (filename) return strdup(filename+1); return strdup(name); } unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus) { /* 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M map, plus 1 page per MiB of RAM to shadow the resident processes. This is higher than the minimum that Xen would allocate if no value were given (but the Xen minimum is for safety, not performance). */ return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024)); } char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid) { unsigned int len; char path[strlen("/local/domain") + 12]; char *s; snprintf(path, sizeof(path), "/local/domain/%d/name", domid); s = xs_read(ctx->xsh, XBT_NULL, path, &len); return s; } char *libxl__domid_to_name(libxl__gc *gc, uint32_t domid) { char *s = libxl_domid_to_name(CTX, domid); libxl__ptr_add(gc, s); return s; } int libxl_name_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid) { int i, nb_domains; char *domname; libxl_dominfo *dominfo; int ret = ERROR_INVAL; dominfo = libxl_list_domain(ctx, &nb_domains); if (!dominfo) return ERROR_NOMEM; for (i = 0; i < nb_domains; i++) { domname = libxl_domid_to_name(ctx, dominfo[i].domid); if (!domname) continue; if (strcmp(domname, name) == 0) { *domid = dominfo[i].domid; ret = 0; free(domname); break; } free(domname); } free(dominfo); return ret; } int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid) { int i, rv; for (i=0; name[i]; i++) { if (!CTYPE(isdigit, name[i])) { goto nondigit_found; } } *domid = strtoul(name, NULL, 10); return 0; nondigit_found: /* this could also check for uuids */ rv = libxl_name_to_domid(ctx, name, domid); return rv; } static int qualifier_to_id(const char *p, uint32_t *id_r) { int i, alldigit; alldigit = 1; for (i = 0; p[i]; i++) { if (!isdigit((uint8_t)p[i])) { alldigit = 0; break; } } if (i > 0 && alldigit) { *id_r = strtoul(p, NULL, 10); return 0; } else { /* check here if it's a uuid and do proper conversion */ } return 1; } int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p, uint32_t *poolid_r, int *was_name_r) { int was_name; was_name = qualifier_to_id(p, poolid_r); if (was_name_r) *was_name_r = was_name; return was_name ? libxl_name_to_cpupoolid(ctx, p, poolid_r) : 0; } char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid) { unsigned int len; char path[strlen("/local/pool") + 12]; char *s; snprintf(path, sizeof(path), "/local/pool/%d/name", poolid); s = xs_read(ctx->xsh, XBT_NULL, path, &len); if (!s && (poolid == 0)) return strdup("Pool-0"); return s; } /* This is a bit horrid but without xs_exists it seems like the only way. */ int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid) { int ret; char *s = libxl_cpupoolid_to_name(ctx, poolid); ret = (s != NULL); free(s); return ret; } char *libxl__cpupoolid_to_name(libxl__gc *gc, uint32_t poolid) { char *s = libxl_cpupoolid_to_name(CTX, poolid); libxl__ptr_add(gc, s); return s; } int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name, uint32_t *poolid) { int i, nb_pools; char *poolname; libxl_cpupoolinfo *poolinfo; int ret = ERROR_INVAL; poolinfo = libxl_list_cpupool(ctx, &nb_pools); if (!poolinfo) return ERROR_NOMEM; for (i = 0; i < nb_pools; i++) { if (ret && ((poolname = libxl_cpupoolid_to_name(ctx, poolinfo[i].poolid)) != NULL)) { if (strcmp(poolname, name) == 0) { *poolid = poolinfo[i].poolid; ret = 0; } free(poolname); } } libxl_cpupoolinfo_list_free(poolinfo, nb_pools); return ret; } int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid) { GC_INIT(ctx); char * stubdom_id_s; int ret; stubdom_id_s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/image/device-model-domid", libxl__xs_get_dompath(gc, guest_domid))); if (stubdom_id_s) ret = atoi(stubdom_id_s); else ret = 0; GC_FREE; return ret; } int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid) { GC_INIT(ctx); char *target, *endptr; uint32_t value; int ret = 0; target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/target", libxl__xs_get_dompath(gc, domid))); if (!target) goto out; value = strtol(target, &endptr, 10); if (*endptr != '\0') goto out; if (target_domid) *target_domid = value; ret = 1; out: GC_FREE; return ret; } static int logrename(libxl__gc *gc, const char *old, const char *new) { int r; r = rename(old, new); if (r) { if (errno == ENOENT) return 0; /* ok */ LOGE(ERROR, "failed to rotate logfile - " "could not rename %s to %s", old, new); return ERROR_FAIL; } return 0; } int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name) { GC_INIT(ctx); struct stat stat_buf; char *logfile, *logfile_new; int i, rc; logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name); if (stat(logfile, &stat_buf) == 0) { /* file exists, rotate */ logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.10", name); unlink(logfile); for (i = 9; i > 0; i--) { logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i); logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.%d", name, i + 1); rc = logrename(gc, logfile, logfile_new); if (rc) goto out; } logfile = GCSPRINTF(XEN_LOG_DIR "/%s.log", name); logfile_new = GCSPRINTF(XEN_LOG_DIR "/%s.log.1", name); rc = logrename(gc, logfile, logfile_new); if (rc) goto out; } else { if (errno != ENOENT) LOGE(WARN, "problem checking existence of logfile %s, " "which might have needed to be rotated", name); } *full_name = strdup(logfile); rc = 0; out: GC_FREE; return rc; } int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend) { char *p; int rc = 0; if (!strcmp(s, "phy")) { *backend = LIBXL_DISK_BACKEND_PHY; } else if (!strcmp(s, "file")) { *backend = LIBXL_DISK_BACKEND_TAP; } else if (!strcmp(s, "qdisk")) { *backend = LIBXL_DISK_BACKEND_QDISK; } else if (!strcmp(s, "tap")) { p = strchr(s, ':'); if (!p) { rc = ERROR_INVAL; goto out; } p++; if (!strcmp(p, "vhd")) { *backend = LIBXL_DISK_BACKEND_TAP; } else if (!strcmp(p, "qcow")) { *backend = LIBXL_DISK_BACKEND_QDISK; } else if (!strcmp(p, "qcow2")) { *backend = LIBXL_DISK_BACKEND_QDISK; } else if (!strcmp(p, "qed")) { *backend = LIBXL_DISK_BACKEND_QDISK; } } out: return rc; } int libxl_read_file_contents(libxl_ctx *ctx, const char *filename, void **data_r, int *datalen_r) { GC_INIT(ctx); FILE *f = 0; uint8_t *data = 0; int datalen = 0; int e; struct stat stab; ssize_t rs; f = fopen(filename, "r"); if (!f) { if (errno == ENOENT) return ENOENT; LOGE(ERROR, "failed to open %s", filename); goto xe; } if (fstat(fileno(f), &stab)) { LOGE(ERROR, "failed to fstat %s", filename); goto xe; } if (!S_ISREG(stab.st_mode)) { LOGE(ERROR, "%s is not a plain file", filename); errno = ENOTTY; goto xe; } if (stab.st_size > INT_MAX) { LOG(ERROR, "file %s is far too large", filename); errno = EFBIG; goto xe; } datalen = stab.st_size; if (stab.st_size && data_r) { data = malloc(datalen); if (!data) goto xe; rs = fread(data, 1, datalen, f); if (rs != datalen) { if (ferror(f)) LOGE(ERROR, "failed to read %s", filename); else if (feof(f)) LOG(ERROR, "%s changed size while we were reading it", filename); else abort(); goto xe; } } if (fclose(f)) { f = 0; LOGE(ERROR, "failed to close %s", filename); goto xe; } if (data_r) *data_r = data; if (datalen_r) *datalen_r = datalen; GC_FREE; return 0; xe: GC_FREE; e = errno; assert(e != ENOENT); if (f) fclose(f); free(data); return e; } int libxl__read_sysfs_file_contents(libxl__gc *gc, const char *filename, void **data_r, int *datalen_r) { FILE *f = 0; uint8_t *data = 0; int datalen = 0; int e; struct stat stab; ssize_t rs; f = fopen(filename, "r"); if (!f) { if (errno == ENOENT) return ENOENT; LOGE(ERROR, "failed to open %s", filename); goto xe; } if (fstat(fileno(f), &stab)) { LOGE(ERROR, "failed to fstat %s", filename); goto xe; } if (!S_ISREG(stab.st_mode)) { LOGE(ERROR, "%s is not a plain file", filename); errno = ENOTTY; goto xe; } if (stab.st_size > INT_MAX) { LOG(ERROR, "file %s is far too large", filename); errno = EFBIG; goto xe; } datalen = stab.st_size; if (stab.st_size && data_r) { data = libxl__malloc(gc, datalen); /* For sysfs file, datalen is always PAGE_SIZE. 'read' * will return the number of bytes of the actual content, * rs <= datalen is expected. */ rs = fread(data, 1, datalen, f); if (rs < datalen) { if (ferror(f)) { LOGE(ERROR, "failed to read %s", filename); goto xe; } datalen = rs; data = libxl__realloc(gc, data, datalen); } } if (fclose(f)) { f = 0; LOGE(ERROR, "failed to close %s", filename); goto xe; } if (data_r) *data_r = data; if (datalen_r) *datalen_r = datalen; return 0; xe: e = errno; assert(e != ENOENT); if (f) fclose(f); return e; } #define READ_WRITE_EXACTLY(rw, zero_is_eof, constdata) \ \ int libxl_##rw##_exactly(libxl_ctx *ctx, int fd, \ constdata void *data, ssize_t sz, \ const char *source, const char *what) { \ ssize_t got; \ GC_INIT(ctx); \ \ while (sz > 0) { \ got = rw(fd, data, sz); \ if (got == -1) { \ if (errno == EINTR) continue; \ if (!ctx) { GC_FREE; return errno; } \ LOGE(ERROR, "failed to "#rw" %s%s%s", \ what ? what : "", what ? " from " : "", source); \ GC_FREE; \ return errno; \ } \ if (got == 0) { \ if (!ctx) { GC_FREE; return EPROTO; } \ LOG(ERROR, zero_is_eof \ ? "file/stream truncated reading %s%s%s" \ : "file/stream write returned 0! writing %s%s%s", \ what ? what : "", what ? " from " : "", source); \ GC_FREE; \ return EPROTO; \ } \ sz -= got; \ data = (char*)data + got; \ } \ GC_FREE; \ return 0; \ } READ_WRITE_EXACTLY(read, 1, /* */) READ_WRITE_EXACTLY(write, 0, const) int libxl__remove_file(libxl__gc *gc, const char *path) { for (;;) { int r = unlink(path); if (!r) return 0; if (errno == ENOENT) return 0; if (errno == EINTR) continue; LOGE(ERROR, "failed to remove file %s", path); return ERROR_FAIL; } } int libxl__remove_file_or_directory(libxl__gc *gc, const char *path) { for (;;) { int r = rmdir(path); if (!r) return 0; if (errno == ENOENT) return 0; if (errno == ENOTEMPTY) return libxl__remove_directory(gc, path); if (errno == ENOTDIR) return libxl__remove_file(gc, path); if (errno == EINTR) continue; LOGE(ERROR, "failed to remove %s", path); return ERROR_FAIL; } } int libxl__remove_directory(libxl__gc *gc, const char *dirpath) { int rc = 0; DIR *d = 0; d = opendir(dirpath); if (!d) { if (errno == ENOENT) goto out; LOGE(ERROR, "failed to opendir %s for removal", dirpath); rc = ERROR_FAIL; goto out; } struct dirent *de; for (;;) { errno = 0; de = readdir(d); if (!de && errno) { LOGE(ERROR, "failed to readdir %s for removal", dirpath); rc = ERROR_FAIL; break; } if (!de) break; if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; const char *subpath = GCSPRINTF("%s/%s", dirpath, de->d_name); if (libxl__remove_file_or_directory(gc, subpath)) rc = ERROR_FAIL; } for (;;) { int r = rmdir(dirpath); if (!r) break; if (errno == ENOENT) goto out; if (errno == EINTR) continue; LOGE(ERROR, "failed to remove emptied directory %s", dirpath); rc = ERROR_FAIL; } out: if (d) closedir(d); return rc; } int libxl_pipe(libxl_ctx *ctx, int pipes[2]) { GC_INIT(ctx); int ret = 0; if (pipe(pipes) < 0) { LOG(ERROR, "Failed to create a pipe"); ret = -1; } GC_FREE; return ret; } int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits) { GC_INIT(ctx); int sz; sz = (n_bits + 7) / 8; bitmap->map = libxl__calloc(NOGC, sizeof(*bitmap->map), sz); bitmap->size = sz; GC_FREE; return 0; } void libxl_bitmap_init(libxl_bitmap *map) { memset(map, '\0', sizeof(*map)); } void libxl_bitmap_dispose(libxl_bitmap *map) { if (!map) return; free(map->map); map->map = NULL; map->size = 0; } void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr, const libxl_bitmap *sptr) { int sz; assert(dptr->size == sptr->size); sz = dptr->size = sptr->size; memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map)); } /* This function copies X bytes from source to destination bitmap, * where X is the smaller of the two sizes. * * If destination's size is larger than source, the extra bytes are * untouched. */ void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr, const libxl_bitmap *sptr) { int sz; sz = dptr->size < sptr->size ? dptr->size : sptr->size; memcpy(dptr->map, sptr->map, sz * sizeof(*dptr->map)); } void libxl_bitmap_copy_alloc(libxl_ctx *ctx, libxl_bitmap *dptr, const libxl_bitmap *sptr) { GC_INIT(ctx); dptr->map = libxl__calloc(NOGC, sptr->size, sizeof(*sptr->map)); dptr->size = sptr->size; memcpy(dptr->map, sptr->map, sptr->size * sizeof(*sptr->map)); GC_FREE; } int libxl_bitmap_is_full(const libxl_bitmap *bitmap) { int i; for (i = 0; i < bitmap->size; i++) if (bitmap->map[i] != (uint8_t)-1) return 0; return 1; } int libxl_bitmap_is_empty(const libxl_bitmap *bitmap) { int i; for (i = 0; i < bitmap->size; i++) if (bitmap->map[i]) return 0; return 1; } int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit) { if (bit >= bitmap->size * 8) return 0; return (bitmap->map[bit / 8] & (1 << (bit & 7))) ? 1 : 0; } void libxl_bitmap_set(libxl_bitmap *bitmap, int bit) { if (bit >= bitmap->size * 8) return; bitmap->map[bit / 8] |= 1 << (bit & 7); } void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit) { if (bit >= bitmap->size * 8) return; bitmap->map[bit / 8] &= ~(1 << (bit & 7)); } int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map, const libxl_bitmap *map1, const libxl_bitmap *map2) { GC_INIT(ctx); int rc; uint32_t i; const libxl_bitmap *large_map; const libxl_bitmap *small_map; if (map1->size > map2->size) { large_map = map1; small_map = map2; } else { large_map = map2; small_map = map1; } rc = libxl_bitmap_alloc(ctx, or_map, large_map->size * 8); if (rc) goto out; /* * If bitmaps aren't the same size, their union (logical or) will * be size of larger bit map. Any bit past the end of the * smaller bit map, will match the larger one. */ for (i = 0; i < small_map->size; i++) or_map->map[i] = (small_map->map[i] | large_map->map[i]); for (i = small_map->size; i < large_map->size; i++) or_map->map[i] = large_map->map[i]; out: GC_FREE; return rc; } int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map, const libxl_bitmap *map1, const libxl_bitmap *map2) { GC_INIT(ctx); int rc; uint32_t i; const libxl_bitmap *large_map; const libxl_bitmap *small_map; if (map1->size > map2->size) { large_map = map1; small_map = map2; } else { large_map = map2; small_map = map1; } rc = libxl_bitmap_alloc(ctx, and_map, small_map->size * 8); if (rc) goto out; /* * If bitmaps aren't same size, their 'and' will be size of * smaller bit map */ for (i = 0; i < and_map->size; i++) and_map->map[i] = (large_map->map[i] & small_map->map[i]); out: GC_FREE; return rc; } int libxl_bitmap_count_set(const libxl_bitmap *bitmap) { int i, nr_set_bits = 0; libxl_for_each_set_bit(i, *bitmap) nr_set_bits++; return nr_set_bits; } /* NB. caller is responsible for freeing the memory */ char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap) { GC_INIT(ctx); int i = bitmap->size; char *p = libxl__zalloc(NOGC, bitmap->size * 2 + 3); char *q = p; strncpy(p, "0x", 3); p += 2; while(--i >= 0) { sprintf(p, "%02x", bitmap->map[i]); p += 2; } *p = '\0'; GC_FREE; return q; } int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus) { GC_INIT(ctx); int rc = 0; if (max_cpus < 0) { rc = ERROR_INVAL; LOG(ERROR, "invalid number of cpus provided"); goto out; } if (max_cpus == 0) max_cpus = libxl_get_max_cpus(ctx); if (max_cpus < 0) { LOG(ERROR, "failed to retrieve the maximum number of cpus"); rc = max_cpus; goto out; } /* This can't fail: no need to check and log */ libxl_bitmap_alloc(ctx, cpumap, max_cpus); out: GC_FREE; return rc; } int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap, int max_nodes) { GC_INIT(ctx); int rc = 0; if (max_nodes < 0) { rc = ERROR_INVAL; LOG(ERROR, "invalid number of nodes provided"); goto out; } if (max_nodes == 0) max_nodes = libxl_get_max_nodes(ctx); if (max_nodes < 0) { LOG(ERROR, "failed to retrieve the maximum number of nodes"); rc = max_nodes; goto out; } /* This can't fail: no need to check and log */ libxl_bitmap_alloc(ctx, nodemap, max_nodes); out: GC_FREE; return rc; } int libxl__count_physical_sockets(libxl__gc *gc, int *sockets) { int rc; libxl_physinfo info; libxl_physinfo_init(&info); rc = libxl_get_physinfo(CTX, &info); if (rc) return rc; *sockets = info.nr_cpus / info.threads_per_core / info.cores_per_socket; libxl_physinfo_dispose(&info); return 0; } int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap, int max_sockets) { GC_INIT(ctx); int rc = 0; if (max_sockets < 0) { rc = ERROR_INVAL; LOG(ERROR, "invalid number of sockets provided"); goto out; } if (max_sockets == 0) { rc = libxl__count_physical_sockets(gc, &max_sockets); if (rc) { LOGE(ERROR, "failed to get system socket count"); goto out; } } /* This can't fail: no need to check and log */ libxl_bitmap_alloc(ctx, socketmap, max_sockets); out: GC_FREE; return rc; } int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap) { libxl_cputopology *tinfo = NULL; int nr_cpus = 0, i, rc = 0; tinfo = libxl_get_cpu_topology(ctx, &nr_cpus); if (tinfo == NULL) { rc = ERROR_FAIL; goto out; } libxl_bitmap_set_none(socketmap); for (i = 0; i < nr_cpus; i++) if (tinfo[i].socket != XEN_INVALID_SOCKET_ID && !libxl_bitmap_test(socketmap, tinfo[i].socket)) libxl_bitmap_set(socketmap, tinfo[i].socket); out: libxl_cputopology_list_free(tinfo, nr_cpus); return rc; } int libxl_nodemap_to_cpumap(libxl_ctx *ctx, const libxl_bitmap *nodemap, libxl_bitmap *cpumap) { libxl_cputopology *tinfo = NULL; int nr_cpus = 0, i, rc = 0; tinfo = libxl_get_cpu_topology(ctx, &nr_cpus); if (tinfo == NULL) { rc = ERROR_FAIL; goto out; } libxl_bitmap_set_none(cpumap); for (i = 0; i < nr_cpus; i++) { if (libxl_bitmap_test(nodemap, tinfo[i].node)) libxl_bitmap_set(cpumap, i); } out: libxl_cputopology_list_free(tinfo, nr_cpus); return rc; } int libxl_node_to_cpumap(libxl_ctx *ctx, int node, libxl_bitmap *cpumap) { libxl_bitmap nodemap; int rc = 0; libxl_bitmap_init(&nodemap); rc = libxl_node_bitmap_alloc(ctx, &nodemap, 0); if (rc) goto out; libxl_bitmap_set_none(&nodemap); libxl_bitmap_set(&nodemap, node); rc = libxl_nodemap_to_cpumap(ctx, &nodemap, cpumap); out: libxl_bitmap_dispose(&nodemap); return rc; } int libxl_cpumap_to_nodemap(libxl_ctx *ctx, const libxl_bitmap *cpumap, libxl_bitmap *nodemap) { libxl_cputopology *tinfo = NULL; int nr_cpus = 0, i, rc = 0; tinfo = libxl_get_cpu_topology(ctx, &nr_cpus); if (tinfo == NULL) { rc = ERROR_FAIL; goto out; } libxl_bitmap_set_none(nodemap); libxl_for_each_set_bit(i, *cpumap) { if (i >= nr_cpus) break; libxl_bitmap_set(nodemap, tinfo[i].node); } out: libxl_cputopology_list_free(tinfo, nr_cpus); return rc; } int libxl_get_max_cpus(libxl_ctx *ctx) { int max_cpus = xc_get_max_cpus(ctx->xch); return max_cpus < 0 ? ERROR_FAIL : max_cpus; } int libxl_get_online_cpus(libxl_ctx *ctx) { int online_cpus = xc_get_online_cpus(ctx->xch); return online_cpus < 0 ? ERROR_FAIL : online_cpus; } int libxl_get_max_nodes(libxl_ctx *ctx) { int max_nodes = xc_get_max_nodes(ctx->xch); return max_nodes < 0 ? ERROR_FAIL : max_nodes; } int libxl__enum_from_string(const libxl_enum_string_table *t, const char *s, int *e) { if (!t) return ERROR_INVAL; for( ; t->s; t++) { if (!strcasecmp(t->s, s)) { *e = t->v; return 0; } } return ERROR_FAIL; } void libxl_cputopology_list_free(libxl_cputopology *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_cputopology_dispose(&list[i]); free(list); } void libxl_pcitopology_list_free(libxl_pcitopology *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_pcitopology_dispose(&list[i]); free(list); } void libxl_numainfo_list_free(libxl_numainfo *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_numainfo_dispose(&list[i]); free(list); } void libxl_vcpuinfo_list_free(libxl_vcpuinfo *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_vcpuinfo_dispose(&list[i]); free(list); } int libxl__sendmsg_fds(libxl__gc *gc, int carrier, const void *data, size_t datalen, int nfds, const int fds[], const char *what) { struct msghdr msg = { 0 }; struct cmsghdr *cmsg; size_t spaceneeded = nfds * sizeof(fds[0]); char control[CMSG_SPACE(spaceneeded)]; struct iovec iov; int r; iov.iov_base = (void*)data; iov.iov_len = datalen; /* compose the message */ msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof(control); /* attach open fd */ cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(spaceneeded); memcpy(CMSG_DATA(cmsg), fds, spaceneeded); msg.msg_controllen = cmsg->cmsg_len; r = sendmsg(carrier, &msg, 0); if (r < 0) { LOGE(ERROR, "failed to send fd-carrying message (%s)", what); return ERROR_FAIL; } return 0; } int libxl__recvmsg_fds(libxl__gc *gc, int carrier, void *databuf, size_t datalen, int nfds, int fds[], const char *what) { struct msghdr msg = { 0 }; struct cmsghdr *cmsg; size_t spaceneeded = nfds * sizeof(fds[0]); char control[CMSG_SPACE(spaceneeded)]; struct iovec iov; int r; iov.iov_base = databuf; iov.iov_len = datalen; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof(control); for (;;) { r = recvmsg(carrier, &msg, 0); if (r < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return -1; LOGE(ERROR,"recvmsg failed (%s)", what); return ERROR_FAIL; } if (r == 0) { LOG(ERROR,"recvmsg got EOF (%s)", what); return ERROR_FAIL; } cmsg = CMSG_FIRSTHDR(&msg); if (cmsg->cmsg_len <= CMSG_LEN(0)) { LOG(ERROR,"recvmsg got no control msg" " when expecting fds (%s)", what); return ERROR_FAIL; } if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { LOG(ERROR, "recvmsg got unexpected" " cmsg_level %d (!=%d) or _type %d (!=%d) (%s)", cmsg->cmsg_level, SOL_SOCKET, cmsg->cmsg_type, SCM_RIGHTS, what); return ERROR_FAIL; } if (cmsg->cmsg_len != CMSG_LEN(spaceneeded) || msg.msg_controllen != cmsg->cmsg_len) { LOG(ERROR, "recvmsg got unexpected" " number of fds or extra control data" " (%ld bytes' worth, expected %ld) (%s)", (long)CMSG_LEN(spaceneeded), (long)cmsg->cmsg_len, what); int i, fd; unsigned char *p; for (i=0, p=CMSG_DATA(cmsg); CMSG_SPACE(i * sizeof(fds[0])); i++, i+=sizeof(fd)) { memcpy(&fd, p, sizeof(fd)); close(fd); } return ERROR_FAIL; } memcpy(fds, CMSG_DATA(cmsg), spaceneeded); return 0; } } void libxl_dominfo_list_free(libxl_dominfo *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_dominfo_dispose(&list[i]); free(list); } void libxl_vminfo_list_free(libxl_vminfo *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_vminfo_dispose(&list[i]); free(list); } void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_cpupoolinfo_dispose(&list[i]); free(list); } int libxl_domid_valid_guest(uint32_t domid) { /* returns 1 if the value _could_ be a valid guest domid, 0 otherwise * does not check whether the domain actually exists */ return domid > 0 && domid < DOMID_FIRST_RESERVED; } void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src) { GC_INIT(ctx); if (*src) *dst = libxl__strdup(NOGC, *src); else *dst = NULL; GC_FREE; } /* * Fill @buf with @len random bytes. */ int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len) { static const char *dev = "/dev/urandom"; int fd; int ret; fd = open(dev, O_RDONLY); if (fd < 0) { LOGE(ERROR, "failed to open \"%s\"", dev); return ERROR_FAIL; } ret = libxl_fd_set_cloexec(CTX, fd, 1); if (ret) { close(fd); return ERROR_FAIL; } ret = libxl_read_exactly(CTX, fd, buf, len, dev, NULL); close(fd); return ret; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_nocpuid.c0000664000175000017500000000344713256712137015736 0ustar smbsmb/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl) { return 1; } void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list) { } int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) { return 0; } int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid, const char* str) { return 0; } void libxl_cpuid_apply_policy(libxl_ctx *ctx, uint32_t domid) { } void libxl_cpuid_set(libxl_ctx *ctx, uint32_t domid, libxl_cpuid_policy_list cpuid) { } yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand, libxl_cpuid_policy_list *pcpuid) { return 0; } int libxl__cpuid_policy_list_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_cpuid_policy_list *p) { return 0; } void libxl_cpuid_policy_list_copy(libxl_ctx *ctx, libxl_cpuid_policy_list *dst, const libxl_cpuid_policy_list *src) { } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_json.c0000664000175000017500000007354513256712137015254 0ustar smbsmb/* * Copyright (C) 2011 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include #include #include #include "libxl_internal.h" /* #define DEBUG_ANSWER */ struct libxl__yajl_ctx { libxl__gc *gc; yajl_handle hand; libxl__json_object *head; libxl__json_object *current; #ifdef DEBUG_ANSWER yajl_gen g; #endif }; #ifdef DEBUG_ANSWER #if YAJL_VERSION < 20000 # define DEBUG_GEN_ALLOC(ctx) \ if ((ctx)->g == NULL) { \ yajl_gen_config conf = { 1, " " }; \ (ctx)->g = yajl_gen_alloc(&conf, NULL); \ } #else /* YAJL2 */ # define DEBUG_GEN_ALLOC(ctx) \ if ((ctx)->g == NULL) { \ (ctx)->g = yajl_gen_alloc(NULL); \ yajl_gen_config((ctx)->g, yajl_gen_beautify, 1); \ yajl_gen_config((ctx)->g, yajl_gen_indent_string, " "); \ } #endif # define DEBUG_GEN_FREE(ctx) \ if ((ctx)->g) yajl_gen_free((ctx)->g) # define DEBUG_GEN(ctx, type) yajl_gen_##type(ctx->g) # define DEBUG_GEN_VALUE(ctx, type, value) yajl_gen_##type(ctx->g, value) # define DEBUG_GEN_STRING(ctx, str, n) yajl_gen_string(ctx->g, str, n) # define DEBUG_GEN_NUMBER(ctx, str, n) yajl_gen_number(ctx->g, str, n) # define DEBUG_GEN_REPORT(yajl_ctx) \ do { \ const unsigned char *buf = NULL; \ size_t len = 0; \ yajl_gen_get_buf((yajl_ctx)->g, &buf, &len); \ LIBXL__LOG(libxl__gc_owner((yajl_ctx)->gc), LIBXL__LOG_DEBUG, "response:\n", buf); \ yajl_gen_free((yajl_ctx)->g); \ (yajl_ctx)->g = NULL; \ } while (0) #else # define DEBUG_GEN_ALLOC(ctx) ((void)0) # define DEBUG_GEN_FREE(ctx) ((void)0) # define DEBUG_GEN(ctx, type) ((void)0) # define DEBUG_GEN_VALUE(ctx, type, value) ((void)0) # define DEBUG_GEN_STRING(ctx, value, length) ((void)0) # define DEBUG_GEN_NUMBER(ctx, value, length) ((void)0) # define DEBUG_GEN_REPORT(ctx) ((void)0) #endif /* * YAJL Helper */ yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str) { return yajl_gen_string(hand, (const unsigned char *)str, strlen(str)); } yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str) { if (str) return libxl__yajl_gen_asciiz(hand, str); else return yajl_gen_null(hand); } /* * YAJL generators for builtin libxl types. */ yajl_gen_status libxl_defbool_gen_json(yajl_gen hand, libxl_defbool *db) { return libxl__yajl_gen_asciiz(hand, libxl_defbool_to_string(*db)); } int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_defbool *p) { const char *s; if (!libxl__json_object_is_string(o)) return ERROR_FAIL; s = libxl__json_object_get_string(o); if (!strncmp(s, LIBXL__DEFBOOL_STR_DEFAULT, strlen(LIBXL__DEFBOOL_STR_DEFAULT))) p->val = LIBXL__DEFBOOL_DEFAULT; else if (!strncmp(s, LIBXL__DEFBOOL_STR_TRUE, strlen(LIBXL__DEFBOOL_STR_TRUE))) p->val = LIBXL__DEFBOOL_TRUE; else if (!strncmp(s, LIBXL__DEFBOOL_STR_FALSE, strlen(LIBXL__DEFBOOL_STR_FALSE))) p->val = LIBXL__DEFBOOL_FALSE; else return ERROR_FAIL; return 0; } int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o, bool *p) { if (!libxl__json_object_is_bool(o)) return ERROR_FAIL; *p = libxl__json_object_get_bool(o); return 0; } yajl_gen_status libxl_uuid_gen_json(yajl_gen hand, libxl_uuid *uuid) { char buf[LIBXL_UUID_FMTLEN+1]; snprintf(buf, sizeof(buf), LIBXL_UUID_FMT, LIBXL_UUID_BYTES((*uuid))); return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_UUID_FMTLEN); } int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_uuid *p) { if (!libxl__json_object_is_string(o)) return ERROR_FAIL; return libxl_uuid_from_string(p, o->u.string); } yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand, libxl_bitmap *bitmap) { yajl_gen_status s; int i; s = yajl_gen_array_open(hand); if (s != yajl_gen_status_ok) goto out; libxl_for_each_bit(i, *bitmap) { if (libxl_bitmap_test(bitmap, i)) { s = yajl_gen_integer(hand, i); if (s != yajl_gen_status_ok) goto out; } } s = yajl_gen_array_close(hand); out: return s; } int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_bitmap *p) { int i; int size; const libxl__json_object *t; flexarray_t *array; if (!libxl__json_object_is_array(o)) return ERROR_FAIL; array = libxl__json_object_get_array(o); if (!array->count) { libxl_bitmap_init(p); return 0; } t = libxl__json_array_get(o, array->count - 1); if (!libxl__json_object_is_integer(t)) return ERROR_FAIL; size = libxl__json_object_get_integer(t) + 1; libxl_bitmap_alloc(CTX, p, size); for (i = 0; (t = libxl__json_array_get(o, i)); i++) { if (!libxl__json_object_is_integer(t)) return ERROR_FAIL; libxl_bitmap_set(p, libxl__json_object_get_integer(t)); } return 0; } yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand, libxl_key_value_list *pkvl) { libxl_key_value_list kvl = *pkvl; yajl_gen_status s; int i; s = yajl_gen_map_open(hand); if (s != yajl_gen_status_ok) goto out; if (!kvl) goto empty; for (i = 0; kvl[i] != NULL; i += 2) { s = libxl__yajl_gen_asciiz(hand, kvl[i]); if (s != yajl_gen_status_ok) goto out; if (kvl[i + 1]) s = libxl__yajl_gen_asciiz(hand, kvl[i+1]); else s = yajl_gen_null(hand); if (s != yajl_gen_status_ok) goto out; } empty: s = yajl_gen_map_close(hand); out: return s; } int libxl__key_value_list_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_key_value_list *p) { libxl__json_map_node *node = NULL; flexarray_t *maps = NULL; int i, size; libxl_key_value_list kvl; if (!libxl__json_object_is_map(o)) return ERROR_FAIL; maps = libxl__json_object_get_map(o); size = maps->count * 2; kvl = *p = libxl__calloc(NOGC, size+1, sizeof(char *)); for (i = 0; i < maps->count; i++) { int idx = i * 2; if (flexarray_get(maps, i, (void**)&node) != 0) return ERROR_FAIL; if (!libxl__json_object_is_string(node->obj) && !libxl__json_object_is_null(node->obj)) return ERROR_FAIL; kvl[idx] = libxl__strdup(NOGC, node->map_key); if (libxl__json_object_is_string(node->obj)) kvl[idx+1] = libxl__strdup(NOGC, libxl__json_object_get_string(node->obj)); else kvl[idx+1] = NULL; } return 0; } yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *pl) { libxl_string_list l = *pl; yajl_gen_status s; int i; s = yajl_gen_array_open(hand); if (s != yajl_gen_status_ok) goto out; if (!l) goto empty; for (i = 0; l[i] != NULL; i++) { s = libxl__yajl_gen_asciiz(hand, l[i]); if (s != yajl_gen_status_ok) goto out; } empty: s = yajl_gen_array_close(hand); out: return s; } int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_string_list *p) { const libxl__json_object *t; libxl_string_list l; flexarray_t *array = NULL; int i, size; if (!libxl__json_object_is_array(o)) return ERROR_FAIL; array = libxl__json_object_get_array(o); size = array->count; if (size == 0) { *p = NULL; return 0; } /* need one extra slot as sentinel */ l = *p = libxl__calloc(NOGC, size + 1, sizeof(char *)); for (i = 0; (t = libxl__json_array_get(o, i)); i++) { if (!libxl__json_object_is_string(t)) return ERROR_FAIL; l[i] = libxl__strdup(NOGC, libxl__json_object_get_string(t)); } return 0; } yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *mac) { char buf[LIBXL_MAC_FMTLEN+1]; snprintf(buf, sizeof(buf), LIBXL_MAC_FMT, LIBXL_MAC_BYTES((*mac))); return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_MAC_FMTLEN); } int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_mac *p) { if (!libxl__json_object_is_string(o)) return ERROR_FAIL; return libxl__parse_mac(libxl__json_object_get_string(o), *p); } yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand, libxl_hwcap *p) { yajl_gen_status s; int i; s = yajl_gen_array_open(hand); if (s != yajl_gen_status_ok) goto out; for(i=0; i<4; i++) { s = yajl_gen_integer(hand, (*p)[i]); if (s != yajl_gen_status_ok) goto out; } s = yajl_gen_array_close(hand); out: return s; } int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_hwcap *p) { int i; if (!libxl__json_object_is_array(o)) return ERROR_FAIL; for (i = 0; i<4; i++) { const libxl__json_object *t; t = libxl__json_array_get(o, i); if (!t || !libxl__json_object_is_integer(t)) return ERROR_FAIL; (*p)[i] = libxl__json_object_get_integer(t); } return 0; } yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p) { yajl_gen_status s; int i; s = yajl_gen_array_open(hand); if (s != yajl_gen_status_ok) return s; for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) { s = yajl_gen_integer(hand, p->bytes[i]); if (s != yajl_gen_status_ok) return s; } return yajl_gen_array_close(hand); } int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_ms_vm_genid *p) { unsigned int i; if (!libxl__json_object_is_array(o)) return ERROR_FAIL; for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) { const libxl__json_object *t; t = libxl__json_array_get(o, i); if (!t || !libxl__json_object_is_integer(t)) return ERROR_FAIL; p->bytes[i] = libxl__json_object_get_integer(t); } return 0; } yajl_gen_status libxl__string_gen_json(yajl_gen hand, const char *p) { if (p) return libxl__yajl_gen_asciiz(hand, p); else return yajl_gen_null(hand); } int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o, char **p) { if (!libxl__json_object_is_string(o) && !libxl__json_object_is_null(o)) return ERROR_FAIL; if (libxl__json_object_is_null(o)) *p = NULL; else *p = libxl__strdup(NOGC, libxl__json_object_get_string(o)); return 0; } /* * libxl__json_object helper functions */ libxl__json_object *libxl__json_object_alloc(libxl__gc *gc, libxl__json_node_type type) { libxl__json_object *obj; obj = libxl__zalloc(gc, sizeof(*obj)); obj->type = type; if (type == JSON_MAP || type == JSON_ARRAY) { flexarray_t *array = flexarray_make(gc, 1, 1); if (type == JSON_MAP) obj->u.map = array; else obj->u.array = array; } return obj; } int libxl__json_object_append_to(libxl__gc *gc, libxl__json_object *obj, libxl__yajl_ctx *ctx) { libxl__json_object *dst = ctx->current; if (dst) { switch (dst->type) { case JSON_MAP: { libxl__json_map_node *last; if (dst->u.map->count == 0) { LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, "Try to add a value to an empty map (with no key)"); return ERROR_FAIL; } flexarray_get(dst->u.map, dst->u.map->count - 1, (void**)&last); last->obj = obj; break; } case JSON_ARRAY: flexarray_append(dst->u.array, obj); break; default: LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, "Try append an object is not a map/array (%i)", dst->type); return ERROR_FAIL; } } obj->parent = dst; if (libxl__json_object_is_map(obj) || libxl__json_object_is_array(obj)) ctx->current = obj; if (ctx->head == NULL) ctx->head = obj; return 0; } void libxl__json_object_free(libxl__gc *gc, libxl__json_object *obj) { int idx = 0; if (obj == NULL) return; switch (obj->type) { case JSON_STRING: case JSON_NUMBER: free(obj->u.string); break; case JSON_MAP: { libxl__json_map_node *node = NULL; for (idx = 0; idx < obj->u.map->count; idx++) { if (flexarray_get(obj->u.map, idx, (void**)&node) != 0) break; libxl__json_object_free(gc, node->obj); free(node->map_key); free(node); node = NULL; } flexarray_free(obj->u.map); break; } case JSON_ARRAY: { libxl__json_object *node = NULL; for (idx = 0; idx < obj->u.array->count; idx++) { if (flexarray_get(obj->u.array, idx, (void**)&node) != 0) break; libxl__json_object_free(gc, node); node = NULL; } flexarray_free(obj->u.array); break; } default: break; } free(obj); } libxl__json_object *libxl__json_array_get(const libxl__json_object *o, int i) { flexarray_t *array = NULL; libxl__json_object *obj = NULL; if ((array = libxl__json_object_get_array(o)) == NULL) { return NULL; } if (i >= array->count) return NULL; if (flexarray_get(array, i, (void**)&obj) != 0) return NULL; return obj; } libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o, int i) { flexarray_t *array = NULL; libxl__json_map_node *obj = NULL; if ((array = libxl__json_object_get_map(o)) == NULL) { return NULL; } if (i >= array->count) return NULL; if (flexarray_get(array, i, (void**)&obj) != 0) return NULL; return obj; } const libxl__json_object *libxl__json_map_get(const char *key, const libxl__json_object *o, libxl__json_node_type expected_type) { flexarray_t *maps = NULL; int idx = 0; if (libxl__json_object_is_map(o)) { libxl__json_map_node *node = NULL; maps = o->u.map; for (idx = 0; idx < maps->count; idx++) { if (flexarray_get(maps, idx, (void**)&node) != 0) return NULL; if (strcmp(key, node->map_key) == 0) { if (expected_type == JSON_ANY || (node->obj && (node->obj->type & expected_type))) { return node->obj; } else { return NULL; } } } } return NULL; } yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc, yajl_gen hand, libxl__json_object *obj) { int idx = 0; yajl_status rc; #define CONVERT_YAJL_GEN_TO_STATUS(gen) \ ((gen) == yajl_gen_status_ok ? yajl_status_ok : yajl_status_error) switch (obj->type) { case JSON_NULL: return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_null(hand)); case JSON_BOOL: return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_bool(hand, obj->u.b)); case JSON_INTEGER: return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_integer(hand, obj->u.i)); case JSON_DOUBLE: return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_double(hand, obj->u.d)); case JSON_NUMBER: return CONVERT_YAJL_GEN_TO_STATUS( yajl_gen_number(hand, obj->u.string, strlen(obj->u.string))); case JSON_STRING: return CONVERT_YAJL_GEN_TO_STATUS( libxl__yajl_gen_asciiz(hand, obj->u.string)); case JSON_MAP: { libxl__json_map_node *node = NULL; rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_open(hand)); if (rc != yajl_status_ok) return rc; for (idx = 0; idx < obj->u.map->count; idx++) { if (flexarray_get(obj->u.map, idx, (void**)&node) != 0) break; rc = CONVERT_YAJL_GEN_TO_STATUS( libxl__yajl_gen_asciiz(hand, node->map_key)); if (rc != yajl_status_ok) return rc; rc = libxl__json_object_to_yajl_gen(gc, hand, node->obj); if (rc != yajl_status_ok) return rc; } return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_close(hand)); } case JSON_ARRAY: { libxl__json_object *node = NULL; rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_open(hand)); if (rc != yajl_status_ok) return rc; for (idx = 0; idx < obj->u.array->count; idx++) { if (flexarray_get(obj->u.array, idx, (void**)&node) != 0) break; rc = libxl__json_object_to_yajl_gen(gc, hand, node); if (rc != yajl_status_ok) return rc; } return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_close(hand)); } case JSON_ANY: /* JSON_ANY is not a valid value for obj->type. */ ; } abort(); #undef CONVERT_YAJL_GEN_TO_STATUS } /* * JSON callbacks */ static int json_callback_null(void *opaque) { libxl__yajl_ctx *ctx = opaque; libxl__json_object *obj; DEBUG_GEN(ctx, null); obj = libxl__json_object_alloc(ctx->gc, JSON_NULL); if (libxl__json_object_append_to(ctx->gc, obj, ctx)) return 0; return 1; } static int json_callback_boolean(void *opaque, int boolean) { libxl__yajl_ctx *ctx = opaque; libxl__json_object *obj; DEBUG_GEN_VALUE(ctx, bool, boolean); obj = libxl__json_object_alloc(ctx->gc, JSON_BOOL); obj->u.b = boolean; if (libxl__json_object_append_to(ctx->gc, obj, ctx)) return 0; return 1; } static bool is_decimal(const char *s, unsigned len) { const char *end = s + len; for (; s < end; s++) { if (*s == '.') return true; } return false; } static int json_callback_number(void *opaque, const char *s, libxl_yajl_length len) { libxl__yajl_ctx *ctx = opaque; libxl__json_object *obj = NULL; char *t = NULL; DEBUG_GEN_NUMBER(ctx, s, len); if (is_decimal(s, len)) { double d = strtod(s, NULL); if ((d == HUGE_VALF || d == HUGE_VALL) && errno == ERANGE) { goto error; } obj = libxl__json_object_alloc(ctx->gc, JSON_DOUBLE); obj->u.d = d; } else { long long i = strtoll(s, NULL, 10); if ((i == LLONG_MIN || i == LLONG_MAX) && errno == ERANGE) { goto error; } obj = libxl__json_object_alloc(ctx->gc, JSON_INTEGER); obj->u.i = i; } goto out; error: /* If the conversion fail, we just store the original string. */ obj = libxl__json_object_alloc(ctx->gc, JSON_NUMBER); t = libxl__zalloc(ctx->gc, len + 1); strncpy(t, s, len); t[len] = 0; obj->u.string = t; out: if (libxl__json_object_append_to(ctx->gc, obj, ctx)) return 0; return 1; } static int json_callback_string(void *opaque, const unsigned char *str, libxl_yajl_length len) { libxl__yajl_ctx *ctx = opaque; char *t = NULL; libxl__json_object *obj = NULL; t = libxl__zalloc(ctx->gc, len + 1); DEBUG_GEN_STRING(ctx, str, len); strncpy(t, (const char *) str, len); t[len] = 0; obj = libxl__json_object_alloc(ctx->gc, JSON_STRING); obj->u.string = t; if (libxl__json_object_append_to(ctx->gc, obj, ctx)) return 0; return 1; } static int json_callback_map_key(void *opaque, const unsigned char *str, libxl_yajl_length len) { libxl__yajl_ctx *ctx = opaque; char *t = NULL; libxl__json_object *obj = ctx->current; libxl__gc *gc = ctx->gc; t = libxl__zalloc(gc, len + 1); DEBUG_GEN_STRING(ctx, str, len); strncpy(t, (const char *) str, len); t[len] = 0; if (libxl__json_object_is_map(obj)) { libxl__json_map_node *node; GCNEW(node); node->map_key = t; node->obj = NULL; flexarray_append(obj->u.map, node); } else { LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, "Current json object is not a map"); return 0; } return 1; } static int json_callback_start_map(void *opaque) { libxl__yajl_ctx *ctx = opaque; libxl__json_object *obj = NULL; DEBUG_GEN(ctx, map_open); obj = libxl__json_object_alloc(ctx->gc, JSON_MAP); if (libxl__json_object_append_to(ctx->gc, obj, ctx)) return 0; return 1; } static int json_callback_end_map(void *opaque) { libxl__yajl_ctx *ctx = opaque; DEBUG_GEN(ctx, map_close); if (ctx->current) { ctx->current = ctx->current->parent; } else { LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, "No current libxl__json_object, cannot use his parent."); return 0; } return 1; } static int json_callback_start_array(void *opaque) { libxl__yajl_ctx *ctx = opaque; libxl__json_object *obj = NULL; DEBUG_GEN(ctx, array_open); obj = libxl__json_object_alloc(ctx->gc, JSON_ARRAY); if (libxl__json_object_append_to(ctx->gc, obj, ctx)) return 0; return 1; } static int json_callback_end_array(void *opaque) { libxl__yajl_ctx *ctx = opaque; DEBUG_GEN(ctx, array_close); if (ctx->current) { ctx->current = ctx->current->parent; } else { LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR, "No current libxl__json_object, cannot use his parent."); return 0; } return 1; } static yajl_callbacks callbacks = { json_callback_null, json_callback_boolean, NULL, NULL, json_callback_number, json_callback_string, json_callback_start_map, json_callback_map_key, json_callback_end_map, json_callback_start_array, json_callback_end_array }; static void yajl_ctx_free(libxl__yajl_ctx *yajl_ctx) { if (yajl_ctx->hand) { yajl_free(yajl_ctx->hand); yajl_ctx->hand = NULL; } DEBUG_GEN_FREE(yajl_ctx); } libxl__json_object *libxl__json_parse(libxl__gc *gc, const char *s) { yajl_status status; libxl__yajl_ctx yajl_ctx; libxl__json_object *o = NULL; unsigned char *str = NULL; memset(&yajl_ctx, 0, sizeof (yajl_ctx)); yajl_ctx.gc = gc; DEBUG_GEN_ALLOC(&yajl_ctx); if (yajl_ctx.hand == NULL) { yajl_ctx.hand = libxl__yajl_alloc(&callbacks, NULL, &yajl_ctx); } status = yajl_parse(yajl_ctx.hand, (const unsigned char *)s, strlen(s)); if (status != yajl_status_ok) goto out; status = yajl_complete_parse(yajl_ctx.hand); if (status != yajl_status_ok) goto out; o = yajl_ctx.head; DEBUG_GEN_REPORT(&yajl_ctx); yajl_ctx.head = NULL; yajl_ctx_free(&yajl_ctx); return o; out: str = yajl_get_error(yajl_ctx.hand, 1, (const unsigned char*)s, strlen(s)); LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, "yajl error: %s", str); yajl_free_error(yajl_ctx.hand, str); yajl_ctx_free(&yajl_ctx); return NULL; } static const char *yajl_gen_status_to_string(yajl_gen_status s) { switch (s) { case yajl_gen_status_ok: abort(); case yajl_gen_keys_must_be_strings: return "keys must be strings"; case yajl_max_depth_exceeded: return "max depth exceeded"; case yajl_gen_in_error_state: return "in error state"; case yajl_gen_generation_complete: return "generation complete"; case yajl_gen_invalid_number: return "invalid number"; #if 0 /* This is in the docs but not implemented in the version I am running. */ case yajl_gen_no_buf: return "no buffer"; case yajl_gen_invalid_string: return "invalid string"; #endif default: return "unknown error"; } } char *libxl__object_to_json(libxl_ctx *ctx, const char *type, libxl__gen_json_callback gen, void *p) { const unsigned char *buf; char *ret = NULL; libxl_yajl_length len = 0; yajl_gen_status s; yajl_gen hand; hand = libxl_yajl_gen_alloc(NULL); if (!hand) return NULL; s = gen(hand, p); if (s != yajl_gen_status_ok) goto out; s = yajl_gen_get_buf(hand, &buf, &len); if (s != yajl_gen_status_ok) goto out; ret = strdup((const char *)buf); out: yajl_gen_free(hand); if (s != yajl_gen_status_ok) { LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "unable to convert %s to JSON representation. " "YAJL error code %d: %s", type, s, yajl_gen_status_to_string(s)); } else if (!ret) { LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "unable to allocate space for to JSON representation of %s", type); } return ret; } yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val) { char *num; int len; yajl_gen_status s; len = asprintf(&num, "%"PRIu64, val); if (len == -1) { s = yajl_gen_in_error_state; goto out; } s = yajl_gen_number(hand, num, len); free(num); out: return s; } int libxl__object_from_json(libxl_ctx *ctx, const char *type, libxl__json_parse_callback parse, void *p, const char *s) { GC_INIT(ctx); libxl__json_object *o; int rc; o = libxl__json_parse(gc, s); if (!o) { LOG(ERROR, "unable to generate libxl__json_object from JSON representation of %s.", type); rc = ERROR_FAIL; goto out; } rc = parse(gc, o, p); if (rc) { LOG(ERROR, "unable to convert libxl__json_object to %s. (rc=%d)", type, rc); rc = ERROR_FAIL; goto out; } rc = 0; out: GC_FREE; return rc; } int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o, void *p) { long long i; if (!libxl__json_object_is_integer(o)) return ERROR_FAIL; i = libxl__json_object_get_integer(o); if (i > INT_MAX || i < INT_MIN) return ERROR_FAIL; *((int *)p) = i; return 0; } /* Macro to generate: * libxl__uint8_parse_json * libxl__uint16_parse_json * libxl__uint32_parse_json */ #define PARSE_UINT(width) \ int libxl__uint ## width ## _parse_json(libxl__gc *gc, \ const libxl__json_object *o,\ void *p) \ { \ long long i; \ \ if (!libxl__json_object_is_integer(o)) \ return ERROR_FAIL; \ \ i = libxl__json_object_get_integer(o); \ \ if (i < 0 || i > UINT ## width ## _MAX) \ return ERROR_FAIL; \ \ *((uint ## width ## _t *)p) = i; \ \ return 0; \ } PARSE_UINT(8); PARSE_UINT(16); PARSE_UINT(32); int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o, void *p) { if (!libxl__json_object_is_integer(o) && !libxl__json_object_is_number(o)) return ERROR_FAIL; if (libxl__json_object_is_integer(o)) { long long i = libxl__json_object_get_integer(o); if (i < 0) return ERROR_FAIL; *((uint64_t *)p) = i; } else { const char *s; unsigned long long i; int saved_errno = errno; s = libxl__json_object_get_number(o); errno = 0; i = strtoull(s, NULL, 10); if (i == ULLONG_MAX && errno == ERANGE) return ERROR_FAIL; errno = saved_errno; *((uint64_t *)p) = i; } return 0; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_exec.c0000664000175000017500000003403113256712137015212 0ustar smbsmb /* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" static void check_open_fds(const char *what) { const char *env_debug; int debug; int i, flags, badness = 0; env_debug = getenv("_LIBXL_DEBUG_EXEC_FDS"); if (!env_debug) return; debug = strtol(env_debug, (char **) NULL, 10); if (debug <= 0) return; for (i = 4; i < 256; i++) { #ifdef __linux__ ssize_t len; char path[PATH_MAX]; char linkpath[PATH_MAX+1]; #endif flags = fcntl(i, F_GETFD); if ( flags == -1 ) { if ( errno != EBADF ) fprintf(stderr, "libxl: execing %s: fd %d flags returned %s (%d)\n", what, i, strerror(errno), errno); continue; } if ( flags & FD_CLOEXEC ) continue; badness++; #ifdef __linux__ snprintf(path, PATH_MAX, "/proc/%d/fd/%d", getpid(), i); len = readlink(path, linkpath, PATH_MAX); if (len > 0) { linkpath[len] = '\0'; fprintf(stderr, "libxl: execing %s: fd %d is open to %s with flags %#x\n", what, i, linkpath, flags); } else #endif fprintf(stderr, "libxl: execing %s: fd %d is open with flags %#x\n", what, i, flags); } if (debug < 2) return; if (badness) abort(); } void libxl__exec(libxl__gc *gc, int stdinfd, int stdoutfd, int stderrfd, const char *arg0, char *const args[], char *const env[]) /* call this in the child */ { if (stdinfd != -1) dup2(stdinfd, STDIN_FILENO); if (stdoutfd != -1) dup2(stdoutfd, STDOUT_FILENO); if (stderrfd != -1) dup2(stderrfd, STDERR_FILENO); if (stdinfd > 2) close(stdinfd); if (stdoutfd > 2 && stdoutfd != stdinfd) close(stdoutfd); if (stderrfd > 2 && stderrfd != stdinfd && stderrfd != stdoutfd) close(stderrfd); check_open_fds(arg0); signal(SIGPIPE, SIG_DFL); /* in case our caller set it to IGN. subprocesses are entitled * to assume they got DFL. */ if (env != NULL) { for (int i = 0; env[i] != NULL && env[i+1] != NULL; i += 2) { if (setenv(env[i], env[i+1], 1) < 0) { LOGEV(ERROR, errno, "setting env vars (%s = %s)", env[i], env[i+1]); goto out; } } } execvp(arg0, args); out: fprintf(stderr, "libxl: cannot execute %s: %s\n", arg0, strerror(errno)); _exit(-1); } void libxl_report_child_exitstatus(libxl_ctx *ctx, xentoollog_level level, const char *what, pid_t pid, int status) { if (WIFEXITED(status)) { int st = WEXITSTATUS(status); if (st) LIBXL__LOG(ctx, level, "%s [%ld] exited" " with error status %d", what, (unsigned long)pid, st); else LIBXL__LOG(ctx, level, "%s [%ld] unexpectedly" " exited status zero", what, (unsigned long)pid); } else if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); const char *str = strsignal(sig); const char *coredump = WCOREDUMP(status) ? " (core dumped)" : ""; if (str) LIBXL__LOG(ctx, level, "%s [%ld] died due to" " fatal signal %s%s", what, (unsigned long)pid, str, coredump); else LIBXL__LOG(ctx, level, "%s [%ld] died due to unknown" " fatal signal number %d%s", what, (unsigned long)pid, sig, coredump); } else { LIBXL__LOG(ctx, level, "%s [%ld] gave unknown" " wait status 0x%x", what, (unsigned long)pid, status); } } int libxl__spawn_record_pid(libxl__gc *gc, libxl__spawn_state *spawn, pid_t pid) { int r, rc; rc = libxl__ev_child_xenstore_reopen(gc, spawn->what); if (rc) goto out; r = libxl__xs_printf(gc, XBT_NULL, spawn->pidpath, "%d", pid); if (r) { LOGE(ERROR, "write %s = %d: xenstore write failed", spawn->pidpath, pid); rc = ERROR_FAIL; goto out; } rc = 0; out: return rc ? SIGTERM : 0; } int libxl__xenstore_child_wait_deprecated(libxl__gc *gc, uint32_t domid, uint32_t timeout, char *what, char *path, char *state, libxl__spawn_starting *spawning, int (*check_callback)(libxl__gc *gc, uint32_t domid, const char *state, void *userdata), void *check_callback_userdata) { char *p; unsigned int len; int rc = 0; struct xs_handle *xsh; int nfds; fd_set rfds; struct timeval tv; unsigned int num; char **l = NULL; xsh = xs_daemon_open(); if (xsh == NULL) { LOG(ERROR, "Unable to open xenstore connection"); goto err; } xs_watch(xsh, path, path); tv.tv_sec = timeout; tv.tv_usec = 0; nfds = xs_fileno(xsh) + 1; assert(!spawning); while (rc > 0 || (!rc && tv.tv_sec > 0)) { p = xs_read(xsh, XBT_NULL, path, &len); if ( NULL == p ) goto again; if ( NULL != state && strcmp(p, state) ) goto again; if ( NULL != check_callback ) { rc = (*check_callback)(gc, domid, p, check_callback_userdata); if ( rc > 0 ) goto again; } free(p); xs_unwatch(xsh, path, path); xs_daemon_close(xsh); return rc; again: free(p); FD_ZERO(&rfds); FD_SET(xs_fileno(xsh), &rfds); rc = select(nfds, &rfds, NULL, NULL, &tv); if (rc > 0) { if (FD_ISSET(xs_fileno(xsh), &rfds)) { l = xs_read_watch(xsh, &num); if (l != NULL) free(l); else goto again; } } } LOG(ERROR, "%s not ready", what); xs_unwatch(xsh, path, path); xs_daemon_close(xsh); err: return -1; } /*----- spawn implementation -----*/ /* * Full set of possible states of a libxl__spawn_state and its _detachable: * * detaching rc mid timeout xswatch * - Undefined undef undef - undef undef * - Idle any any Idle Idle Idle * - Attached OK 0 0 Active Active Active * - Attached Failed 0 non-0 Active Idle Idle * - Detaching 1 maybe Active Idle Idle * - Partial any any Idle Active/Idle Active/Idle * * When in states Detaching or Attached Failed, the middle process has * been sent a SIGKILL. * * The difference between Attached OK and Attached Failed is not * directly visible to callers - callers see these two the same, * although of course Attached OK will hopefully eventually result in * a call to detached_cb, whereas Attached Failed will end up * in a call to failure_cb. */ /* Event callbacks. */ static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa, int rc, const char *xsdata); static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw, pid_t pid, int status); /* Precondition: Partial. Results: Idle. */ static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss); /* Precondition: Attached or Detaching; caller has logged failure reason. * Results: Detaching, or Attached Failed */ static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc); void libxl__spawn_init(libxl__spawn_state *ss) { libxl__ev_child_init(&ss->mid); libxl__xswait_init(&ss->xswait); } int libxl__spawn_spawn(libxl__egc *egc, libxl__spawn_state *ss) { STATE_AO_GC(ss->ao); int r; pid_t child; int status, rc; libxl__spawn_init(ss); ss->rc = ss->detaching = 0; ss->xswait.ao = ao; ss->xswait.what = GCSPRINTF("%s startup", ss->what); ss->xswait.path = ss->xspath; ss->xswait.timeout_ms = ss->timeout_ms; ss->xswait.callback = spawn_watch_event; rc = libxl__xswait_start(gc, &ss->xswait); if (rc) goto out_err; pid_t middle = libxl__ev_child_fork(gc, &ss->mid, spawn_middle_death); if (middle ==-1) { rc = ERROR_FAIL; goto out_err; } if (middle) { /* parent */ return 1; } /* we are now the middle process */ pid_t (*fork_replacement)(void*) = CTX->childproc_hooks ? CTX->childproc_hooks->fork_replacement : 0; child = fork_replacement ? fork_replacement(CTX->childproc_user) : fork(); if (child == -1) exit(255); if (!child) { return 0; /* caller runs child code */ } int failsig = ss->midproc_cb(gc, ss, child); if (failsig) { kill(child, failsig); _exit(127); } for (;;) { pid_t got = waitpid(child, &status, 0); if (got == -1) { assert(errno == EINTR); continue; } assert(got == child); break; } r = (WIFEXITED(status) && WEXITSTATUS(status) <= 127 ? WEXITSTATUS(status) : WIFSIGNALED(status) && WTERMSIG(status) < 127 ? WTERMSIG(status)+128 : -1); _exit(r); out_err: spawn_cleanup(gc, ss); return rc; } static void spawn_cleanup(libxl__gc *gc, libxl__spawn_state *ss) { assert(!libxl__ev_child_inuse(&ss->mid)); libxl__xswait_stop(gc, &ss->xswait); } static void spawn_detach(libxl__gc *gc, libxl__spawn_state *ss) /* Precondition: Attached or Detaching, but caller must have just set * at least one of detaching or rc. * Results: Detaching or Attached Failed */ { int r; assert(libxl__ev_child_inuse(&ss->mid)); assert(ss->detaching || ss->rc); libxl__xswait_stop(gc, &ss->xswait); pid_t child = ss->mid.pid; r = kill(child, SIGKILL); if (r && errno != ESRCH) LOGE(WARN, "%s: failed to kill intermediate child (pid=%lu)", ss->what, (unsigned long)child); } void libxl__spawn_initiate_detach(libxl__gc *gc, libxl__spawn_state *ss) { ss->detaching = 1; spawn_detach(gc, ss); } static void spawn_fail(libxl__egc *egc, libxl__spawn_state *ss, int rc) /* Caller must have logged. Must be last thing in calling function, * as it may make the callback. Precondition: Attached or Detaching. */ { EGC_GC; assert(rc); ss->rc = rc; spawn_detach(gc, ss); } static void spawn_watch_event(libxl__egc *egc, libxl__xswait_state *xswa, int rc, const char *p) { /* On entry, is Attached. */ EGC_GC; libxl__spawn_state *ss = CONTAINER_OF(xswa, *ss, xswait); if (rc) { if (rc == ERROR_TIMEDOUT) LOG(ERROR, "%s: startup timed out", ss->what); spawn_fail(egc, ss, rc); /* must be last */ return; } LOG(DEBUG, "%s: spawn watch p=%s", ss->what, p); ss->confirm_cb(egc, ss, p); /* must be last */ } static void spawn_middle_death(libxl__egc *egc, libxl__ev_child *childw, pid_t pid, int status) /* On entry, is Attached or Detaching */ { EGC_GC; libxl__spawn_state *ss = CONTAINER_OF(childw, *ss, mid); if ((ss->rc || ss->detaching) && ((WIFEXITED(status) && WEXITSTATUS(status)==0) || (WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL))) { /* as expected */ const char *what = GCSPRINTF("%s (dying as expected)", ss->what); libxl_report_child_exitstatus(CTX, XTL_DEBUG, what, pid, status); } else if (!WIFEXITED(status)) { int loglevel = ss->detaching ? XTL_WARN : XTL_ERROR; const char *what = GCSPRINTF("%s intermediate process (startup monitor)", ss->what); libxl_report_child_exitstatus(CTX, loglevel, what, pid, status); ss->rc = ERROR_FAIL; } else { if (!status) LOG(ERROR, "%s [%ld]: unexpectedly exited with exit status 0," " when we were waiting for it to confirm startup", ss->what, (unsigned long)pid); else if (status <= 127) LOG(ERROR, "%s [%ld]: failed startup with non-zero exit status %d", ss->what, (unsigned long)pid, status); else if (status < 255) { int sig = status - 128; const char *str = strsignal(sig); if (str) LOG(ERROR, "%s [%ld]: died during startup due to fatal" " signal %s", ss->what, (unsigned long)pid, str); else LOG(ERROR, "%s [%ld]: died during startup due to unknown fatal" " signal number %d", ss->what, (unsigned long)pid, sig); } ss->rc = ERROR_FAIL; } spawn_cleanup(gc, ss); if (ss->rc && !ss->detaching) { ss->failure_cb(egc, ss, ss->rc); /* must be last */ return; } if (ss->rc && ss->detaching) LOG(WARN,"%s underlying machinery seemed to fail (%d)," " but its function seems to have been successful", ss->what, ss->rc); assert(ss->detaching); ss->detached_cb(egc, ss); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_arch.h0000664000175000017500000000624213256712137015213 0ustar smbsmb/* * Copyright (C) 2012 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef LIBXL_ARCH_H #define LIBXL_ARCH_H /* fill the arch specific configuration for the domain */ _hidden int libxl__arch_domain_prepare_config(libxl__gc *gc, libxl_domain_config *d_config, xc_domain_configuration_t *xc_config); /* save the arch specific configuration for the domain */ _hidden int libxl__arch_domain_save_config(libxl__gc *gc, libxl_domain_config *d_config, const xc_domain_configuration_t *xc_config); /* arch specific internal domain creation function */ _hidden int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid); /* setup arch specific hardware description, i.e. DTB on ARM */ _hidden int libxl__arch_domain_init_hw_description(libxl__gc *gc, libxl_domain_build_info *info, libxl__domain_build_state *state, struct xc_dom_image *dom); /* finalize arch specific hardware description. */ _hidden int libxl__arch_domain_finalise_hw_description(libxl__gc *gc, libxl_domain_build_info *info, struct xc_dom_image *dom); /* build vNUMA vmemrange with arch specific information */ _hidden int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *b_info, libxl__domain_build_state *state); /* arch specific irq map function */ _hidden int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq); /* arch specific to construct memory mapping function */ _hidden int libxl__arch_domain_construct_memmap(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid, struct xc_dom_image *dom); _hidden void libxl__arch_domain_build_info_acpi_setdefault( libxl_domain_build_info *b_info); _hidden int libxl__arch_extra_memory(libxl__gc *gc, const libxl_domain_build_info *info, uint64_t *out); #if defined(__i386__) || defined(__x86_64__) #define LAPIC_BASE_ADDRESS 0xfee00000 int libxl__dom_load_acpi(libxl__gc *gc, const libxl_domain_build_info *b_info, struct xc_dom_image *dom); #endif #endif xen-4.9.2/tools/libxl/libxl_no_convert_callout.c0000664000175000017500000000207613256712137020171 0ustar smbsmb/* * Copyright (C) 2015 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" void libxl__conversion_helper_init(libxl__conversion_helper_state *chs) { libxl__ev_child_init(&chs->child); } int libxl__convert_legacy_stream(libxl__egc *egc, libxl__conversion_helper_state *chs) { return ERROR_FAIL; } void libxl__conversion_helper_abort(libxl__egc *egc, libxl__conversion_helper_state *chs, int rc) { /* no op */ } xen-4.9.2/tools/libxl/libxl_tmem.c0000664000175000017500000000753313256712137015237 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long) { int r; char _buf[32768]; GC_INIT(ctx); r = xc_tmem_control(ctx->xch, -1, XEN_SYSCTL_TMEM_OP_LIST, domid, 32768, use_long, _buf); if (r < 0) { LOGED(ERROR, domid, "Can not get tmem list"); GC_FREE; return NULL; } GC_FREE; return strdup(_buf); } int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid) { int r, rc; GC_INIT(ctx); r = xc_tmem_control(ctx->xch, -1, XEN_SYSCTL_TMEM_OP_FREEZE, domid, 0, 0, NULL); if (r < 0) { LOGED(ERROR, domid, "Can not freeze tmem pools"); rc = ERROR_FAIL; goto out; } rc = 0; out: GC_FREE; return rc; } int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid) { int r, rc; GC_INIT(ctx); r = xc_tmem_control(ctx->xch, -1, XEN_SYSCTL_TMEM_OP_THAW, domid, 0, 0, NULL); if (r < 0) { LOGED(ERROR, domid, "Can not thaw tmem pools"); rc = ERROR_FAIL; goto out; } rc = 0; out: GC_FREE; return rc; } static int32_t tmem_setop_from_string(char *set_name, uint32_t val, xen_tmem_client_t *info) { if (!strcmp(set_name, "weight")) info->weight = val; else if (!strcmp(set_name, "compress")) info->flags.u.compress = val; else return -1; return 0; } int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name, uint32_t set) { int r, rc; xen_tmem_client_t info; GC_INIT(ctx); r = xc_tmem_control(ctx->xch, -1 /* pool_id */, XEN_SYSCTL_TMEM_OP_GET_CLIENT_INFO, domid, sizeof(info), 0 /* arg */, &info); if (r < 0) { LOGED(ERROR, domid, "Can not get tmem data!"); rc = ERROR_FAIL; goto out; } rc = tmem_setop_from_string(name, set, &info); if (rc == -1) { LOGEVD(ERROR, -1, domid, "Invalid set, valid sets are "); rc = ERROR_INVAL; goto out; } r = xc_tmem_control(ctx->xch, -1 /* pool_id */, XEN_SYSCTL_TMEM_OP_SET_CLIENT_INFO, domid, sizeof(info), 0 /* arg */, &info); if (r < 0) { LOGED(ERROR, domid, "Can not set tmem %s", name); rc = ERROR_FAIL; goto out; } rc = 0; out: GC_FREE; return rc; } int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid, char* uuid, int auth) { int r, rc; GC_INIT(ctx); r = xc_tmem_auth(ctx->xch, domid, uuid, auth); if (r < 0) { LOGED(ERROR, domid, "Can not set tmem shared auth"); rc = ERROR_FAIL; goto out; } rc = 0; out: GC_FREE; return rc; } int libxl_tmem_freeable(libxl_ctx *ctx) { int r, rc; GC_INIT(ctx); r = xc_tmem_control(ctx->xch, -1, XEN_SYSCTL_TMEM_OP_QUERY_FREEABLE_MB, -1, 0, 0, 0); if (r < 0) { LOGE(ERROR, "Can not get tmem freeable memory"); rc = ERROR_FAIL; goto out; } rc = 0; out: GC_FREE; return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/xlutil.pc.in0000664000175000017500000000035413256712137015203 0ustar smbsmbprefix=@@prefix@@ includedir=@@incdir@@ libdir=@@libdir@@ Name: Xlutil Description: The xl utility library for Xen hypervisor Version: @@version@@ Cflags: -I${includedir} Libs: @@libsflag@@${libdir} -lxlutil Requires.private: xenlight xen-4.9.2/tools/libxl/libxl_remus.c0000664000175000017500000003131513256712137015423 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * Yang Hongyang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" extern const libxl__checkpoint_device_instance_ops remus_device_nic; extern const libxl__checkpoint_device_instance_ops remus_device_drbd_disk; static const libxl__checkpoint_device_instance_ops *remus_ops[] = { &remus_device_nic, &remus_device_drbd_disk, NULL, }; /*----- helper functions -----*/ static int init_device_subkind(libxl__checkpoint_devices_state *cds) { /* init device subkind-specific state in the libxl ctx */ int rc; STATE_AO_GC(cds->ao); if (libxl__netbuffer_enabled(gc)) { rc = init_subkind_nic(cds); if (rc) goto out; } rc = init_subkind_drbd_disk(cds); if (rc) goto out; rc = 0; out: return rc; } static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds) { /* cleanup device subkind-specific state in the libxl ctx */ STATE_AO_GC(cds->ao); if (libxl__netbuffer_enabled(gc)) cleanup_subkind_nic(cds); cleanup_subkind_drbd_disk(cds); } /*-------------------- Remus setup and teardown ---------------------*/ static void remus_setup_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void remus_setup_failed(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void remus_checkpoint_stream_written( libxl__egc *egc, libxl__stream_write_state *sws, int rc); static void libxl__remus_domain_suspend_callback(void *data); static void libxl__remus_domain_resume_callback(void *data); static void libxl__remus_domain_save_checkpoint_callback(void *data); void libxl__remus_setup(libxl__egc *egc, libxl__remus_state *rs) { libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs); /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = &dss->cds; const libxl_domain_remus_info *const info = dss->remus; libxl__srm_save_autogen_callbacks *const callbacks = &dss->sws.shs.callbacks.save.a; STATE_AO_GC(dss->ao); if (libxl_defbool_val(info->netbuf)) { if (!libxl__netbuffer_enabled(gc)) { LOGD(ERROR, dss->domid, "Remus: No support for network buffering"); goto out; } cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VIF); } if (libxl_defbool_val(info->diskbuf)) cds->device_kind_flags |= (1 << LIBXL__DEVICE_KIND_VBD); cds->ao = ao; cds->domid = dss->domid; cds->callback = remus_setup_done; cds->ops = remus_ops; cds->concrete_data = rs; rs->interval = info->interval; if (init_device_subkind(cds)) { LOGD(ERROR, dss->domid, "Remus: failed to init device subkind"); goto out; } dss->sws.checkpoint_callback = remus_checkpoint_stream_written; callbacks->suspend = libxl__remus_domain_suspend_callback; callbacks->postcopy = libxl__remus_domain_resume_callback; callbacks->checkpoint = libxl__remus_domain_save_checkpoint_callback; libxl__checkpoint_devices_setup(egc, cds); return; out: dss->callback(egc, dss, ERROR_FAIL); } static void remus_setup_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds); STATE_AO_GC(dss->ao); if (!rc) { libxl__domain_save(egc, dss); return; } LOGD(ERROR, dss->domid, "Remus: failed to setup device, rc %d", rc); cds->callback = remus_setup_failed; libxl__checkpoint_devices_teardown(egc, cds); } static void remus_setup_failed(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds); STATE_AO_GC(dss->ao); if (rc) LOGD(ERROR, dss->domid, "Remus: failed to teardown device after setup failed, rc %d", rc); cleanup_device_subkind(cds); dss->callback(egc, dss, rc); } static void remus_teardown_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); void libxl__remus_teardown(libxl__egc *egc, libxl__remus_state *rs, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(rs, *dss, rs); /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = &dss->cds; EGC_GC; LOGD(WARN, dss->domid, "Remus: Domain suspend terminated with rc %d," " teardown Remus devices...", rc); cds->callback = remus_teardown_done; libxl__checkpoint_devices_teardown(egc, cds); } static void remus_teardown_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds); STATE_AO_GC(dss->ao); if (rc) LOGD(ERROR, dss->domid, "Remus: failed to teardown device," " rc %d", rc); cleanup_device_subkind(cds); dss->callback(egc, dss, rc); } /*---------------------- remus callbacks (save) -----------------------*/ static void remus_domain_suspend_callback_common_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int ok); static void remus_devices_postsuspend_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void remus_devices_preresume_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void libxl__remus_domain_suspend_callback(void *data) { libxl__save_helper_state *shs = data; libxl__egc *egc = shs->egc; libxl__domain_save_state *dss = shs->caller_state; libxl__domain_suspend_state *dsps = &dss->dsps; dsps->callback_common_done = remus_domain_suspend_callback_common_done; libxl__domain_suspend(egc, dsps); } static void remus_domain_suspend_callback_common_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps); if (rc) goto out; libxl__checkpoint_devices_state *const cds = &dss->cds; cds->callback = remus_devices_postsuspend_cb; libxl__checkpoint_devices_postsuspend(egc, cds); return; out: dss->rc = rc; libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc); } static void remus_devices_postsuspend_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds); if (rc) goto out; rc = 0; out: if (rc) dss->rc = rc; libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc); } static void libxl__remus_domain_resume_callback(void *data) { libxl__save_helper_state *shs = data; libxl__egc *egc = shs->egc; libxl__domain_save_state *dss = shs->caller_state; STATE_AO_GC(dss->ao); libxl__checkpoint_devices_state *const cds = &dss->cds; cds->callback = remus_devices_preresume_cb; libxl__checkpoint_devices_preresume(egc, cds); } static void remus_devices_preresume_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds); STATE_AO_GC(dss->ao); if (rc) goto out; /* Resumes the domain and the device model */ rc = libxl__domain_resume(gc, dss->domid, /* Fast Suspend */1); if (rc) goto out; rc = 0; out: if (rc) dss->rc = rc; libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc); } /*----- remus asynchronous checkpoint callback -----*/ static void remus_devices_commit_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc); static void libxl__remus_domain_save_checkpoint_callback(void *data) { libxl__save_helper_state *shs = data; libxl__domain_save_state *dss = shs->caller_state; libxl__egc *egc = shs->egc; STATE_AO_GC(dss->ao); libxl__stream_write_start_checkpoint(egc, &dss->sws); } static void remus_checkpoint_stream_written( libxl__egc *egc, libxl__stream_write_state *sws, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(sws, *dss, sws); /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = &dss->cds; STATE_AO_GC(dss->ao); if (rc) { LOGD(ERROR, dss->domid, "Failed to save device model." " Terminating Remus.."); goto out; } cds->callback = remus_devices_commit_cb; libxl__checkpoint_devices_commit(egc, cds); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0); } static void remus_devices_commit_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds); STATE_AO_GC(dss->ao); if (rc) { LOGD(ERROR, dss->domid, "Failed to do device commit op." " Terminating Remus.."); goto out; } /* * At this point, we have successfully checkpointed the guest and * committed it at the backup. We'll come back after the checkpoint * interval to checkpoint the guest again. Until then, let the guest * continue execution. */ /* Set checkpoint interval timeout */ rc = libxl__ev_time_register_rel(ao, &dss->rs.checkpoint_timeout, remus_next_checkpoint, dss->rs.interval); if (rc) goto out; return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0); } static void remus_next_checkpoint(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(ev, *dss, rs.checkpoint_timeout); STATE_AO_GC(dss->ao); if (rc == ERROR_TIMEDOUT) /* As intended */ rc = 0; /* * Time to checkpoint the guest again. We return 1 to libxc * (xc_domain_save.c). in order to continue executing the infinite loop * (suspend, checkpoint, resume) in xc_domain_save(). */ if (rc) dss->rc = rc; libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc); } /*---------------------- remus callbacks (restore) -----------------------*/ /*----- remus asynchronous checkpoint callback -----*/ static void remus_checkpoint_stream_done( libxl__egc *egc, libxl__stream_read_state *srs, int rc); static void libxl__remus_domain_restore_checkpoint_callback(void *data) { libxl__save_helper_state *shs = data; libxl__domain_create_state *dcs = shs->caller_state; libxl__egc *egc = shs->egc; STATE_AO_GC(dcs->ao); libxl__stream_read_start_checkpoint(egc, &dcs->srs); } static void remus_checkpoint_stream_done( libxl__egc *egc, libxl__stream_read_state *stream, int rc) { libxl__xc_domain_saverestore_async_callback_done(egc, &stream->shs, rc); } void libxl__remus_restore_setup(libxl__egc *egc, libxl__domain_create_state *dcs) { /* Convenience aliases */ libxl__srm_restore_autogen_callbacks *const callbacks = &dcs->srs.shs.callbacks.restore.a; callbacks->checkpoint = libxl__remus_domain_restore_checkpoint_callback; dcs->srs.checkpoint_callback = remus_checkpoint_stream_done; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/gentest.py0000664000175000017500000003173613256712137014764 0ustar smbsmb#!/usr/bin/python import os import sys import re import random import idl def randomize_char(c): if random.random() < 0.5: return str.lower(c) else: return str.upper(c) def randomize_case(s): r = [randomize_char(c) for c in s] return "".join(r) def randomize_enum(e): return random.choice([v.name for v in e.values]) handcoded = ["libxl_bitmap", "libxl_key_value_list", "libxl_cpuid_policy_list", "libxl_string_list"] def gen_rand_init(ty, v, indent = " ", parent = None): s = "" if isinstance(ty, idl.Enumeration): s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), randomize_enum(ty)) elif isinstance(ty, idl.Array): if parent is None: raise Exception("Array type must have a parent") s += "%s = test_rand(8);\n" % (parent + ty.lenvar.name) s += "%s = calloc(%s, sizeof(*%s));\n" % \ (v, parent + ty.lenvar.name, v) s += "assert(%s);\n" % (v, ) s += "{\n" s += " int i;\n" s += " for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name) s += gen_rand_init(ty.elem_type, v+"[i]", indent + " ", parent) s += "}\n" elif isinstance(ty, idl.KeyedUnion): if parent is None: raise Exception("KeyedUnion type must have a parent") s += gen_rand_init(ty.keyvar.type, parent + ty.keyvar.name, indent, parent) s += "switch (%s) {\n" % (parent + ty.keyvar.name) for f in ty.fields: (nparent,fexpr) = ty.member(v, f, parent is None) s += "case %s:\n" % f.enumname if f.type is not None: s += gen_rand_init(f.type, fexpr, indent + " ", nparent) s += " break;\n" s += "}\n" elif isinstance(ty, idl.Struct) \ and (parent is None or ty.json_gen_fn is None): for f in [f for f in ty.fields if not f.const]: (nparent,fexpr) = ty.member(v, f, parent is None) s += gen_rand_init(f.type, fexpr, "", nparent) elif hasattr(ty, "rand_init") and ty.rand_init is not None: s += "%s(%s);\n" % (ty.rand_init, ty.pass_arg(v, isref=parent is None, passby=idl.PASS_BY_REFERENCE)) elif ty.typename in ["libxl_uuid", "libxl_mac", "libxl_hwcap", "libxl_ms_vm_genid"]: s += "rand_bytes((uint8_t *)%s, sizeof(*%s));\n" % (v,v) elif ty.typename in ["libxl_domid", "libxl_devid"] or isinstance(ty, idl.Number): s += "%s = test_rand(sizeof(%s) * 8);\n" % \ (ty.pass_arg(v, parent is None), ty.pass_arg(v, parent is None)) elif ty.typename in ["bool"]: s += "%s = test_rand(2);\n" % v elif ty.typename in ["libxl_defbool"]: s += "libxl_defbool_set(%s, test_rand(2));\n" % v elif ty.typename in ["char *"]: s += "%s = rand_str();\n" % v elif ty.private: pass elif ty.typename in handcoded: raise Exception("Gen for handcoded %s" % ty.typename) else: raise Exception("Cannot randomly init %s" % ty.typename) if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) if __name__ == '__main__': if len(sys.argv) < 3: print >>sys.stderr, "Usage: gentest.py " sys.exit(1) random.seed(os.getenv('LIBXL_TESTIDL_SEED')) (builtins,types) = idl.parse(sys.argv[1]) impl = sys.argv[2] f = open(impl, "w") f.write(""" #include #include #include #include #include "libxl.h" #include "libxl_utils.h" static int test_rand(unsigned max) { /* We are not using rand() for its cryptographic properies. */ return rand() % max; } static char *rand_str(void) { int i, sz = test_rand(32); char *s = malloc(sz+1); assert(s); for (i=0; isize = test_rand(16); bitmap->map = calloc(bitmap->size, sizeof(*bitmap->map)); assert(bitmap->map); libxl_for_each_bit(i, *bitmap) { if (test_rand(2)) libxl_bitmap_set(bitmap, i); else libxl_bitmap_reset(bitmap, i); } } static void libxl_key_value_list_rand_init(libxl_key_value_list *pkvl) { int i, nr_kvp = test_rand(16); libxl_key_value_list kvl = calloc(nr_kvp+1, 2*sizeof(char *)); assert(kvl); for (i = 0; i<2*nr_kvp; i += 2) { kvl[i] = rand_str(); if (test_rand(8)) kvl[i+1] = rand_str(); else kvl[i+1] = NULL; } kvl[i] = NULL; kvl[i+1] = NULL; *pkvl = kvl; } static void libxl_cpuid_policy_list_rand_init(libxl_cpuid_policy_list *pp) { int i, nr_policies = test_rand(16); struct { const char *n; int w; } options[] = { /* A random selection from libxl_cpuid_parse_config */ {"maxleaf", 32}, {"family", 8}, {"model", 8}, {"stepping", 4}, {"localapicid", 8}, {"proccount", 8}, {"clflush", 8}, {"brandid", 8}, {"f16c", 1}, {"avx", 1}, {"osxsave", 1}, {"xsave", 1}, {"aes", 1}, {"popcnt", 1}, {"movbe", 1}, {"x2apic", 1}, {"sse4.2", 1}, {"sse4.1", 1}, {"dca", 1}, {"pdcm", 1}, {"procpkg", 6}, }; const int nr_options = sizeof(options)/sizeof(options[0]); char buf[64]; libxl_cpuid_policy_list p = NULL; for (i = 0; i < nr_policies; i++) { int opt = test_rand(nr_options); int val = test_rand(1< 0: f.write(" %s_init(%s_new);\n" % (ty.typename, \ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE))) iters -= 1 f.write(" s = %s_to_json(ctx, %s);\n" % \ (ty.typename, ty.pass_arg(arg, isref=False))) f.write(" printf(\"%%s: %%s\\n\", \"%s\", s);\n" % ty.typename) f.write(" if (s == NULL) abort();\n") f.write(" rc = %s_from_json(ctx, &%s_val_new, s);\n" % \ (ty.typename, ty.typename)) f.write(" if (rc) abort();\n") f.write(" new_s = %s_to_json(ctx, %s_new);\n" % \ (ty.typename, ty.pass_arg(arg, isref=False))) f.write(" if (new_s == NULL) abort();\n") f.write(" if (strcmp(s, new_s)) {\n") f.write(" printf(\"Huh? Regenerated string different from original string.\\n\");\n") f.write(" printf(\"Regenerated string: %s\\n\", new_s);\n") f.write(" abort();\n") f.write(" }\n") f.write(" free(s);\n") f.write(" free(new_s);\n") if ty.dispose_fn is not None: iters = random.randrange(1,10) f.write(" %s(&%s_val);\n" % (ty.dispose_fn, ty.typename)) while iters > 0: f.write(" %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename)) iters -= 1 f.write("\n") f.write(" printf(\"Testing TYPE_copy()\\n\");\n") f.write(" printf(\"----------------------\\n\");\n") f.write(" printf(\"\\n\");\n") for ty in [t for t in types if t.copy_fn is not None]: f.write(" printf(\"Testing %s_copy, \");\n" % ty.typename) arg = ty.typename + "_val" f.write(" %s_init(%s);\n" % (ty.typename, \ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE))) f.write(" %s_rand_init(%s);\n" % (ty.typename, \ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE))) f.write(" %s_init(%s_new);\n" % (ty.typename, \ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE))) f.write(" %s_copy(ctx, %s_new, %s);\n" % (ty.typename, \ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE), \ ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE))) f.write(" s = %s_to_json(ctx, %s);\n" % \ (ty.typename, ty.pass_arg(arg, isref=False))) f.write(" if (s == NULL) abort();\n") f.write(" new_s = %s_to_json(ctx, %s_new);\n" % \ (ty.typename, ty.pass_arg(arg, isref=False))) f.write(" if (new_s == NULL) abort();\n") f.write(" if (strcmp(s, new_s)) {\n") f.write(" printf(\"Huh? Deep copy for %s failed. Regenerated string different from original string.\\n\");\n" \ % ty.typename) f.write(" printf(\"Original string: %s\\n\", s);\n") f.write(" printf(\"Regenerated string: %s\\n\", new_s);\n") f.write(" abort();\n") f.write(" }\n") f.write(" free(s);\n") f.write(" free(new_s);\n") if ty.dispose_fn is not None: f.write(" %s(&%s_val);\n" % (ty.dispose_fn, ty.typename)) f.write(" %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename)) f.write(" printf(\"done\\n\");\n") f.write("\n") f.write(" printf(\"\\n\");\n") f.write(" printf(\"Testing Enumerations\\n\");\n") f.write(" printf(\"--------------------\\n\");\n") f.write(" printf(\"\\n\");\n") for ty in [t for t in types if isinstance(t,idl.Enumeration)]: f.write(" printf(\"%s -- to string:\\n\");\n" % (ty.typename)) for v in ty.values: f.write(" printf(\"\\t%s = %%d = \\\"%%s\\\"\\n\", " \ "%s, %s_to_string(%s));\n" % \ (v.valuename, v.name, ty.typename, v.name)) f.write("\n") f.write(" printf(\"%s -- to JSON:\\n\");\n" % (ty.typename)) for v in ty.values: f.write(" json_string = %s_to_json(ctx, %s);\n" % \ (ty.typename, v.name)) f.write(" printf(\"\\t%s = %%d = %%s\", " \ "%s, json_string);\n" %\ (v.valuename, v.name)) f.write(" free(json_string);\n"); f.write(" json_string = NULL;\n"); f.write("\n") f.write(" printf(\"%s -- from string:\\n\");\n" % (ty.typename)) for v in [v.valuename for v in ty.values] + ["AN INVALID VALUE"]: n = randomize_case(v) f.write(" %s_val = -1;\n" % (ty.typename)) f.write(" rc = %s_from_string(\"%s\", &%s_val);\n" %\ (ty.typename, n, ty.typename)) f.write(" printf(\"\\t%s = \\\"%%s\\\" = %%d (rc %%d)\\n\", " \ "\"%s\", %s_val, rc);\n" %\ (v, n, ty.typename)) f.write("\n") f.write(""" libxl_ctx_free(ctx); xtl_logger_destroy((xentoollog_logger*)logger); return 0; } """) xen-4.9.2/tools/libxl/libxl_dom_save.c0000664000175000017500000004044513256712137016071 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include /*========================= Domain save ============================*/ static void stream_done(libxl__egc *egc, libxl__stream_write_state *sws, int rc); static void domain_save_done(libxl__egc *egc, libxl__domain_save_state *dss, int rc); /*----- complicated callback, called by xc_domain_save -----*/ /* * We implement the other end of protocol for controlling qemu-dm's * logdirty. There is no documentation for this protocol, but our * counterparty's implementation is in * qemu-xen-traditional.git:xenstore.c in the function * xenstore_process_logdirty_event */ static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc); static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch*, const char *watch_path, const char *event_path); static void switch_logdirty_done(libxl__egc *egc, libxl__logdirty_switch *lds, int rc); void libxl__logdirty_init(libxl__logdirty_switch *lds) { lds->cmd_path = 0; libxl__ev_xswatch_init(&lds->watch); libxl__ev_time_init(&lds->timeout); } static void domain_suspend_switch_qemu_xen_traditional_logdirty (libxl__egc *egc, int domid, unsigned enable, libxl__logdirty_switch *lds) { STATE_AO_GC(lds->ao); int rc; xs_transaction_t t = 0; const char *got; if (!lds->cmd_path) { uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid); lds->cmd_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/logdirty/cmd"); lds->ret_path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/logdirty/ret"); } lds->cmd = enable ? "enable" : "disable"; rc = libxl__ev_xswatch_register(gc, &lds->watch, switch_logdirty_xswatch, lds->ret_path); if (rc) goto out; rc = libxl__ev_time_register_rel(ao, &lds->timeout, switch_logdirty_timeout, 10*1000); if (rc) goto out; for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; rc = libxl__xs_read_checked(gc, t, lds->cmd_path, &got); if (rc) goto out; if (got) { const char *got_ret; rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got_ret); if (rc) goto out; if (!got_ret || strcmp(got, got_ret)) { LOGD(ERROR, domid, "controlling logdirty: qemu was already sent" " command `%s' (xenstore path `%s') but result is `%s'", got, lds->cmd_path, got_ret ? got_ret : ""); rc = ERROR_FAIL; goto out; } rc = libxl__xs_rm_checked(gc, t, lds->cmd_path); if (rc) goto out; } rc = libxl__xs_rm_checked(gc, t, lds->ret_path); if (rc) goto out; rc = libxl__xs_write_checked(gc, t, lds->cmd_path, lds->cmd); if (rc) goto out; rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc<0) goto out; } /* OK, wait for some callback */ return; out: LOGD(ERROR, domid, "logdirty switch failed (rc=%d), abandoning suspend",rc); libxl__xs_transaction_abort(gc, &t); switch_logdirty_done(egc,lds,rc); } static void domain_suspend_switch_qemu_xen_logdirty (libxl__egc *egc, int domid, unsigned enable, libxl__logdirty_switch *lds) { STATE_AO_GC(lds->ao); int rc; rc = libxl__qmp_set_global_dirty_log(gc, domid, enable); if (rc) LOGD(ERROR, domid, "logdirty switch failed (rc=%d), abandoning suspend",rc); lds->callback(egc, lds, rc); } static void domain_suspend_switch_qemu_logdirty_done (libxl__egc *egc, libxl__logdirty_switch *lds, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(lds, *dss, logdirty); if (rc) { dss->rc = rc; libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, -1); } else libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0); } void libxl__domain_suspend_common_switch_qemu_logdirty (int domid, unsigned enable, void *user) { libxl__save_helper_state *shs = user; libxl__egc *egc = shs->egc; libxl__domain_save_state *dss = shs->caller_state; /* Convenience aliases. */ libxl__logdirty_switch *const lds = &dss->logdirty; lds->callback = domain_suspend_switch_qemu_logdirty_done; libxl__domain_common_switch_qemu_logdirty(egc, domid, enable, lds); } void libxl__domain_common_switch_qemu_logdirty(libxl__egc *egc, int domid, unsigned enable, libxl__logdirty_switch *lds) { STATE_AO_GC(lds->ao); switch (libxl__device_model_version_running(gc, domid)) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: domain_suspend_switch_qemu_xen_traditional_logdirty(egc, domid, enable, lds); break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: domain_suspend_switch_qemu_xen_logdirty(egc, domid, enable, lds); break; case LIBXL_DEVICE_MODEL_VERSION_NONE: lds->callback(egc, lds, 0); break; default: LOGD(ERROR, domid, "logdirty switch failed" ", no valid device model version found, abandoning suspend"); lds->callback(egc, lds, ERROR_FAIL); } } static void switch_logdirty_timeout(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc) { libxl__logdirty_switch *lds = CONTAINER_OF(ev, *lds, timeout); STATE_AO_GC(lds->ao); LOG(ERROR,"logdirty switch: wait for device model timed out"); switch_logdirty_done(egc,lds,ERROR_FAIL); } static void switch_logdirty_xswatch(libxl__egc *egc, libxl__ev_xswatch *watch, const char *watch_path, const char *event_path) { libxl__logdirty_switch *lds = CONTAINER_OF(watch, *lds, watch); STATE_AO_GC(lds->ao); const char *got; xs_transaction_t t = 0; int rc; for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; rc = libxl__xs_read_checked(gc, t, lds->ret_path, &got); if (rc) goto out; if (!got) { rc = +1; goto out; } if (strcmp(got, lds->cmd)) { LOG(ERROR,"logdirty switch: sent command `%s' but got reply `%s'" " (xenstore paths `%s' / `%s')", lds->cmd, got, lds->cmd_path, lds->ret_path); rc = ERROR_FAIL; goto out; } rc = libxl__xs_rm_checked(gc, t, lds->cmd_path); if (rc) goto out; rc = libxl__xs_rm_checked(gc, t, lds->ret_path); if (rc) goto out; rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc<0) goto out; } out: /* rc < 0: error * rc == 0: ok, we are done * rc == +1: need to keep waiting */ libxl__xs_transaction_abort(gc, &t); if (rc <= 0) { if (rc < 0) LOG(ERROR,"logdirty switch: failed (rc=%d)",rc); switch_logdirty_done(egc,lds,rc); } } static void switch_logdirty_done(libxl__egc *egc, libxl__logdirty_switch *lds, int rc) { STATE_AO_GC(lds->ao); libxl__ev_xswatch_deregister(gc, &lds->watch); libxl__ev_time_deregister(gc, &lds->timeout); lds->callback(egc, lds, rc); } /*----- callbacks, called by xc_domain_save -----*/ /* * Expand the buffer 'buf' of length 'len', to append 'str' including its NUL * terminator. */ static void append_string(libxl__gc *gc, char **buf, uint32_t *len, const char *str) { size_t extralen = strlen(str) + 1; char *new = libxl__realloc(gc, *buf, *len + extralen); *buf = new; memcpy(new + *len, str, extralen); *len += extralen; } int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss, char **callee_buf, uint32_t *callee_len) { STATE_AO_GC(dss->ao); const char *xs_root; char **entries, *buf = NULL; unsigned int nr_entries, i, j, len = 0; int rc; const uint32_t domid = dss->domid; const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid); xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, ""); entries = libxl__xs_directory(gc, 0, GCSPRINTF("%s/physmap", xs_root), &nr_entries); if (!entries || nr_entries == 0) { rc = 0; goto out; } for (i = 0; i < nr_entries; ++i) { static const char *const physmap_subkeys[] = { "start_addr", "size", "name" }; for (j = 0; j < ARRAY_SIZE(physmap_subkeys); ++j) { const char *key = GCSPRINTF("physmap/%s/%s", entries[i], physmap_subkeys[j]); const char *val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", xs_root, key)); if (!val) { rc = ERROR_FAIL; goto out; } append_string(gc, &buf, &len, key); append_string(gc, &buf, &len, val); } } rc = 0; out: if (!rc) { *callee_buf = buf; *callee_len = len; } return rc; } /*----- main code for saving, in order of execution -----*/ void libxl__domain_save(libxl__egc *egc, libxl__domain_save_state *dss) { STATE_AO_GC(dss->ao); int rc, ret; /* Convenience aliases */ const uint32_t domid = dss->domid; const libxl_domain_type type = dss->type; const int live = dss->live; const int debug = dss->debug; const libxl_domain_remus_info *const r_info = dss->remus; libxl__srm_save_autogen_callbacks *const callbacks = &dss->sws.shs.callbacks.save.a; unsigned int nr_vnodes = 0, nr_vmemranges = 0, nr_vcpus = 0; libxl__domain_suspend_state *dsps = &dss->dsps; if (dss->checkpointed_stream != LIBXL_CHECKPOINTED_STREAM_NONE && !r_info) { LOGD(ERROR, domid, "Migration stream is checkpointed, but there's no " "checkpoint info!"); rc = ERROR_INVAL; goto out; } dss->rc = 0; libxl__logdirty_init(&dss->logdirty); dss->logdirty.ao = ao; dsps->ao = ao; dsps->domid = domid; rc = libxl__domain_suspend_init(egc, dsps, type); if (rc) goto out; switch (type) { case LIBXL_DOMAIN_TYPE_HVM: { dss->hvm = 1; break; } case LIBXL_DOMAIN_TYPE_PV: dss->hvm = 0; break; default: abort(); } dss->xcflags = (live ? XCFLAGS_LIVE : 0) | (debug ? XCFLAGS_DEBUG : 0) | (dss->hvm ? XCFLAGS_HVM : 0); /* Disallow saving a guest with vNUMA configured because migration * stream does not preserve node information. * * Reject any domain which has vnuma enabled, even if the * configuration is empty. Only domains which have no vnuma * configuration at all are supported. */ ret = xc_domain_getvnuma(CTX->xch, domid, &nr_vnodes, &nr_vmemranges, &nr_vcpus, NULL, NULL, NULL); if (ret != -1 || errno != EOPNOTSUPP) { LOGD(ERROR, domid, "Cannot save a guest with vNUMA configured"); rc = ERROR_FAIL; goto out; } if (dss->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_REMUS) { if (libxl_defbool_val(r_info->compression)) dss->xcflags |= XCFLAGS_CHECKPOINT_COMPRESS; } if (dss->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_NONE) callbacks->suspend = libxl__domain_suspend_callback; callbacks->switch_qemu_logdirty = libxl__domain_suspend_common_switch_qemu_logdirty; dss->sws.ao = dss->ao; dss->sws.dss = dss; dss->sws.fd = dss->fd; dss->sws.back_channel = false; dss->sws.completion_callback = stream_done; libxl__stream_write_start(egc, &dss->sws); return; out: domain_save_done(egc, dss, rc); } static void stream_done(libxl__egc *egc, libxl__stream_write_state *sws, int rc) { domain_save_done(egc, sws->dss, rc); } static void domain_save_done(libxl__egc *egc, libxl__domain_save_state *dss, int rc) { STATE_AO_GC(dss->ao); /* Convenience aliases */ const uint32_t domid = dss->domid; libxl__domain_suspend_state *dsps = &dss->dsps; libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); if (dsps->guest_evtchn.port > 0) xc_suspend_evtchn_release(CTX->xch, CTX->xce, domid, dsps->guest_evtchn.port, &dsps->guest_evtchn_lockfd); if (dss->remus) { /* * With Remus/COLO, if we reach this point, it means either * backup died or some network error occurred preventing us * from sending checkpoints. Teardown the network buffers and * release netlink resources. This is an async op. */ if (libxl_defbool_val(dss->remus->colo)) libxl__colo_save_teardown(egc, &dss->css, rc); else libxl__remus_teardown(egc, &dss->rs, rc); return; } dss->callback(egc, dss, rc); } /*========================= Domain restore ============================*/ /* * Inspect the buffer between start and end, and return a pointer to the * character following the NUL terminator of start, or NULL if start is not * terminated before end. */ static const char *next_string(const char *start, const char *end) { if (start >= end) return NULL; size_t total_len = end - start; size_t len = strnlen(start, total_len); if (len == total_len) return NULL; else return start + len + 1; } int libxl__restore_emulator_xenstore_data(libxl__domain_create_state *dcs, const char *ptr, uint32_t size) { STATE_AO_GC(dcs->ao); const char *next = ptr, *end = ptr + size, *key, *val; int rc; const uint32_t domid = dcs->guest_domid; const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid); const char *xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, ""); while (next < end) { key = next; next = next_string(next, end); /* Sanitise 'key'. */ if (!next) { rc = ERROR_FAIL; LOGD(ERROR, domid, "Key in xenstore data not NUL terminated"); goto out; } if (key[0] == '\0') { rc = ERROR_FAIL; LOGD(ERROR, domid, "empty key found in xenstore data"); goto out; } if (key[0] == '/') { rc = ERROR_FAIL; LOGD(ERROR, domid, "Key in xenstore data not relative"); goto out; } val = next; next = next_string(next, end); /* Sanitise 'val'. */ if (!next) { rc = ERROR_FAIL; LOGD(ERROR, domid, "Val in xenstore data not NUL terminated"); goto out; } libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/%s", xs_root, key), "%s", val); } rc = 0; out: return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_vnuma.c0000664000175000017500000002432413256712137015420 0ustar smbsmb/* * Copyright (C) 2014 Citrix Ltd. * Author Wei Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include "libxl_arch.h" #include #include bool libxl__vnuma_configured(const libxl_domain_build_info *b_info) { return b_info->num_vnuma_nodes != 0; } /* Sort vmemranges in ascending order with "start" */ static int compare_vmemrange(const void *a, const void *b) { const xen_vmemrange_t *x = a, *y = b; if (x->start < y->start) return -1; if (x->start > y->start) return 1; return 0; } /* Check if a vcpu has an hard (or soft) affinity set in such * a way that it does not match the pnode to which the vcpu itself * is assigned to. */ static int check_vnuma_affinity(libxl__gc *gc, unsigned int vcpu, unsigned int pnode, unsigned int num_affinity, const libxl_bitmap *affinity, const char *kind) { libxl_bitmap nodemap; int rc = 0; libxl_bitmap_init(&nodemap); rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0); if (rc) { LOG(ERROR, "Can't allocate nodemap"); goto out; } rc = libxl_cpumap_to_nodemap(CTX, affinity, &nodemap); if (rc) { LOG(ERROR, "Can't convert Vcpu %d affinity to nodemap", vcpu); goto out; } if (libxl_bitmap_count_set(&nodemap) != 1 || !libxl_bitmap_test(&nodemap, pnode)) LOG(WARN, "Vcpu %d %s affinity and vnuma info mismatch", vcpu, kind); out: libxl_bitmap_dispose(&nodemap); return rc; } /* Check if vNUMA configuration is valid: * 1. all pnodes inside vnode_to_pnode array are valid * 2. each vcpu belongs to one and only one vnode * 3. each vmemrange is valid and doesn't overlap with any other * 4. local distance cannot be larger than remote distance * * Check also, if any hard or soft affinity is specified, whether * they match with the vNUMA related bits (namely vcpus to vnodes * mappings and vnodes to pnodes association). If that does not * hold, however, just print a warning, as that has "only" * performance implications. */ int libxl__vnuma_config_check(libxl__gc *gc, const libxl_domain_build_info *b_info, const libxl__domain_build_state *state) { int nr_nodes = 0, rc = ERROR_VNUMA_CONFIG_INVALID; unsigned int i, j; libxl_numainfo *ninfo = NULL; uint64_t total_memkb = 0; libxl_bitmap cpumap; libxl_vnode_info *v; libxl_bitmap_init(&cpumap); /* Check pnode specified is valid */ ninfo = libxl_get_numainfo(CTX, &nr_nodes); if (!ninfo) { LOG(ERROR, "libxl_get_numainfo failed"); goto out; } for (i = 0; i < b_info->num_vnuma_nodes; i++) { uint32_t pnode; v = &b_info->vnuma_nodes[i]; pnode = v->pnode; /* The pnode specified is not valid? */ if (pnode >= nr_nodes) { LOG(ERROR, "Invalid pnode %"PRIu32" specified", pnode); goto out; } total_memkb += v->memkb; } if (total_memkb != b_info->max_memkb) { LOG(ERROR, "Amount of memory mismatch (0x%"PRIx64" != 0x%"PRIx64")", total_memkb, b_info->max_memkb); goto out; } /* Check vcpu mapping */ libxl_cpu_bitmap_alloc(CTX, &cpumap, b_info->max_vcpus); for (i = 0; i < b_info->num_vnuma_nodes; i++) { v = &b_info->vnuma_nodes[i]; libxl_for_each_set_bit(j, v->vcpus) { if (!libxl_bitmap_test(&cpumap, j)) libxl_bitmap_set(&cpumap, j); else { LOG(ERROR, "Vcpu %d assigned more than once", j); goto out; } } } for (i = 0; i < b_info->max_vcpus; i++) { if (!libxl_bitmap_test(&cpumap, i)) { LOG(ERROR, "Vcpu %d is not assigned to any vnode", i); goto out; } } /* Check whether vcpu affinity (if any) matches vnuma configuration */ for (i = 0; i < b_info->num_vnuma_nodes; i++) { v = &b_info->vnuma_nodes[i]; libxl_for_each_set_bit(j, v->vcpus) { if (b_info->num_vcpu_hard_affinity > j) check_vnuma_affinity(gc, j, v->pnode, b_info->num_vcpu_hard_affinity, &b_info->vcpu_hard_affinity[j], "hard"); if (b_info->num_vcpu_soft_affinity > j) check_vnuma_affinity(gc, j, v->pnode, b_info->num_vcpu_soft_affinity, &b_info->vcpu_soft_affinity[j], "soft"); } } /* Check vmemranges */ qsort(state->vmemranges, state->num_vmemranges, sizeof(xen_vmemrange_t), compare_vmemrange); for (i = 0; i < state->num_vmemranges; i++) { if (state->vmemranges[i].end < state->vmemranges[i].start) { LOG(ERROR, "Vmemrange end < start"); goto out; } } for (i = 0; i < state->num_vmemranges - 1; i++) { if (state->vmemranges[i].end > state->vmemranges[i+1].start) { LOG(ERROR, "Vmemranges overlapped, 0x%"PRIx64"-0x%"PRIx64", 0x%"PRIx64"-0x%"PRIx64, state->vmemranges[i].start, state->vmemranges[i].end, state->vmemranges[i+1].start, state->vmemranges[i+1].end); goto out; } } /* Check vdistances */ for (i = 0; i < b_info->num_vnuma_nodes; i++) { uint32_t local_distance, remote_distance; v = &b_info->vnuma_nodes[i]; local_distance = v->distances[i]; for (j = 0; j < v->num_distances; j++) { if (i == j) continue; remote_distance = v->distances[j]; if (local_distance > remote_distance) { LOG(ERROR, "Distance from %u to %u smaller than %u's local distance", i, j, i); goto out; } } } rc = 0; out: libxl_numainfo_list_free(ninfo, nr_nodes); libxl_bitmap_dispose(&cpumap); return rc; } int libxl__vnuma_build_vmemrange_pv_generic(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *b_info, libxl__domain_build_state *state) { int i; uint64_t next; xen_vmemrange_t *v = NULL; /* Generate one vmemrange for each virtual node. */ GCREALLOC_ARRAY(v, b_info->num_vnuma_nodes); next = 0; for (i = 0; i < b_info->num_vnuma_nodes; i++) { libxl_vnode_info *p = &b_info->vnuma_nodes[i]; v[i].start = next; v[i].end = next + (p->memkb << 10); v[i].flags = 0; v[i].nid = i; next = v[i].end; } state->vmemranges = v; state->num_vmemranges = i; return 0; } /* Build vmemranges for PV guest */ int libxl__vnuma_build_vmemrange_pv(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *b_info, libxl__domain_build_state *state) { assert(state->vmemranges == NULL); return libxl__arch_vnuma_build_vmemrange(gc, domid, b_info, state); } /* Build vmemranges for HVM guest */ int libxl__vnuma_build_vmemrange_hvm(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *b_info, libxl__domain_build_state *state, struct xc_dom_image *dom) { uint64_t hole_start, hole_end, next; int nid, nr_vmemrange; xen_vmemrange_t *vmemranges; int rc; /* Derive vmemranges from vnode size and memory hole. * * Guest physical address space layout: * [0, hole_start) [hole_start, hole_end) [hole_end, highmem_end) */ hole_start = dom->lowmem_end < dom->mmio_start ? dom->lowmem_end : dom->mmio_start; hole_end = (dom->mmio_start + dom->mmio_size) > (1ULL << 32) ? (dom->mmio_start + dom->mmio_size) : (1ULL << 32); assert(state->vmemranges == NULL); next = 0; nr_vmemrange = 0; vmemranges = NULL; for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) { libxl_vnode_info *p = &b_info->vnuma_nodes[nid]; uint64_t remaining_bytes = p->memkb << 10; /* Consider video ram belongs to vnode 0 */ if (nid == 0) { if (p->memkb < b_info->video_memkb) { LOGD(ERROR, domid, "vnode 0 too small to contain video ram"); rc = ERROR_INVAL; goto out; } remaining_bytes -= (b_info->video_memkb << 10); } while (remaining_bytes > 0) { uint64_t count = remaining_bytes; if (next >= hole_start && next < hole_end) next = hole_end; if ((next < hole_start) && (next + remaining_bytes >= hole_start)) count = hole_start - next; GCREALLOC_ARRAY(vmemranges, nr_vmemrange+1); vmemranges[nr_vmemrange].start = next; vmemranges[nr_vmemrange].end = next + count; vmemranges[nr_vmemrange].flags = 0; vmemranges[nr_vmemrange].nid = nid; nr_vmemrange++; remaining_bytes -= count; next += count; } } state->vmemranges = vmemranges; state->num_vmemranges = nr_vmemrange; rc = 0; out: return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_cfg_y.h0000664000175000017500000000503213256712137015546 0ustar smbsmb/* A Bison parser, made by GNU Bison 3.0.2. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ #ifndef YY_XLU_CFG_YY_LIBXLU_CFG_Y_H_INCLUDED # define YY_XLU_CFG_YY_LIBXLU_CFG_Y_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int xlu__cfg_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { IDENT = 258, STRING = 259, NUMBER = 260, NEWLINE = 261 }; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE YYSTYPE; union YYSTYPE { #line 25 "libxlu_cfg_y.y" /* yacc.c:1909 */ char *string; XLU_ConfigValue *value; #line 66 "libxlu_cfg_y.h" /* yacc.c:1909 */ }; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif /* Location type. */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE YYLTYPE; struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; }; # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif int xlu__cfg_yyparse (CfgParseContext *ctx); #endif /* !YY_XLU_CFG_YY_LIBXLU_CFG_Y_H_INCLUDED */ xen-4.9.2/tools/libxl/libxl_checkpoint_device.c0000664000175000017500000002250013256712137017732 0ustar smbsmb/* * Copyright (C) 2014 FUJITSU LIMITED * Author: Yang Hongyang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" /*----- setup() and teardown() -----*/ /* callbacks */ static void all_devices_setup_cb(libxl__egc *egc, libxl__multidev *multidev, int rc); static void device_setup_iterate(libxl__egc *egc, libxl__ao_device *aodev); static void devices_teardown_cb(libxl__egc *egc, libxl__multidev *multidev, int rc); /* checkpoint device setup and teardown */ static libxl__checkpoint_device* checkpoint_device_init(libxl__egc *egc, libxl__checkpoint_devices_state *cds, libxl__device_kind kind, void *libxl_dev) { libxl__checkpoint_device *dev = NULL; STATE_AO_GC(cds->ao); GCNEW(dev); dev->backend_dev = libxl_dev; dev->kind = kind; dev->cds = cds; return dev; } static void checkpoint_devices_setup(libxl__egc *egc, libxl__checkpoint_devices_state *cds); void libxl__checkpoint_devices_setup(libxl__egc *egc, libxl__checkpoint_devices_state *cds) { int i; STATE_AO_GC(cds->ao); cds->num_devices = 0; cds->num_nics = 0; cds->num_disks = 0; if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VIF)) cds->nics = libxl_device_nic_list(CTX, cds->domid, &cds->num_nics); if (cds->device_kind_flags & (1 << LIBXL__DEVICE_KIND_VBD)) cds->disks = libxl_device_disk_list(CTX, cds->domid, &cds->num_disks); if (cds->num_nics == 0 && cds->num_disks == 0) goto out; GCNEW_ARRAY(cds->devs, cds->num_nics + cds->num_disks); for (i = 0; i < cds->num_nics; i++) { cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds, LIBXL__DEVICE_KIND_VIF, &cds->nics[i]); } for (i = 0; i < cds->num_disks; i++) { cds->devs[cds->num_devices++] = checkpoint_device_init(egc, cds, LIBXL__DEVICE_KIND_VBD, &cds->disks[i]); } checkpoint_devices_setup(egc, cds); return; out: cds->callback(egc, cds, 0); } static void checkpoint_devices_setup(libxl__egc *egc, libxl__checkpoint_devices_state *cds) { int i, rc; STATE_AO_GC(cds->ao); libxl__multidev_begin(ao, &cds->multidev); cds->multidev.callback = all_devices_setup_cb; for (i = 0; i < cds->num_devices; i++) { libxl__checkpoint_device *dev = cds->devs[i]; dev->ops_index = -1; libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev); dev->aodev.rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED; dev->aodev.callback = device_setup_iterate; device_setup_iterate(egc,&dev->aodev); } rc = 0; libxl__multidev_prepared(egc, &cds->multidev, rc); } static void device_setup_iterate(libxl__egc *egc, libxl__ao_device *aodev) { libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev); EGC_GC; if (aodev->rc != ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED && aodev->rc != ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH) /* might be success or disaster */ goto out; do { dev->ops = dev->cds->ops[++dev->ops_index]; if (!dev->ops) { libxl_device_nic * nic = NULL; libxl_device_disk * disk = NULL; uint32_t domid = INVALID_DOMID; int devid; if (dev->kind == LIBXL__DEVICE_KIND_VIF) { nic = (libxl_device_nic *)dev->backend_dev; domid = nic->backend_domid; devid = nic->devid; } else if (dev->kind == LIBXL__DEVICE_KIND_VBD) { disk = (libxl_device_disk *)dev->backend_dev; domid = disk->backend_domid; devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL); } else { LOGD(ERROR, domid, "device kind not handled by checkpoint: %s", libxl__device_kind_to_string(dev->kind)); aodev->rc = ERROR_FAIL; goto out; } LOGD(ERROR, domid, "device not handled by checkpoint" " (device=%s:%"PRId32"/%"PRId32")", libxl__device_kind_to_string(dev->kind), domid, devid); aodev->rc = ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED; goto out; } } while (dev->ops->kind != dev->kind); /* found the next ops_index to try */ assert(dev->aodev.callback == device_setup_iterate); dev->ops->setup(egc,dev); return; out: libxl__multidev_one_callback(egc,aodev); } static void all_devices_setup_cb(libxl__egc *egc, libxl__multidev *multidev, int rc) { STATE_AO_GC(multidev->ao); /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = CONTAINER_OF(multidev, *cds, multidev); cds->callback(egc, cds, rc); } void libxl__checkpoint_devices_teardown(libxl__egc *egc, libxl__checkpoint_devices_state *cds) { int i; libxl__checkpoint_device *dev; STATE_AO_GC(cds->ao); libxl__multidev_begin(ao, &cds->multidev); cds->multidev.callback = devices_teardown_cb; for (i = 0; i < cds->num_devices; i++) { dev = cds->devs[i]; if (!dev->ops || !dev->matched) continue; libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev); dev->ops->teardown(egc,dev); } libxl__multidev_prepared(egc, &cds->multidev, 0); } static void devices_teardown_cb(libxl__egc *egc, libxl__multidev *multidev, int rc) { int i; STATE_AO_GC(multidev->ao); /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = CONTAINER_OF(multidev, *cds, multidev); /* clean nic */ for (i = 0; i < cds->num_nics; i++) libxl_device_nic_dispose(&cds->nics[i]); free(cds->nics); cds->nics = NULL; cds->num_nics = 0; /* clean disk */ for (i = 0; i < cds->num_disks; i++) libxl_device_disk_dispose(&cds->disks[i]); free(cds->disks); cds->disks = NULL; cds->num_disks = 0; cds->callback(egc, cds, rc); } /*----- checkpointing APIs -----*/ /* callbacks */ static void devices_checkpoint_cb(libxl__egc *egc, libxl__multidev *multidev, int rc); /* API implementations */ #define define_checkpoint_api(api) \ void libxl__checkpoint_devices_##api(libxl__egc *egc, \ libxl__checkpoint_devices_state *cds) \ { \ int i; \ libxl__checkpoint_device *dev; \ \ STATE_AO_GC(cds->ao); \ \ libxl__multidev_begin(ao, &cds->multidev); \ cds->multidev.callback = devices_checkpoint_cb; \ for (i = 0; i < cds->num_devices; i++) { \ dev = cds->devs[i]; \ if (!dev->matched || !dev->ops->api) \ continue; \ libxl__multidev_prepare_with_aodev(&cds->multidev, &dev->aodev);\ dev->ops->api(egc,dev); \ } \ \ libxl__multidev_prepared(egc, &cds->multidev, 0); \ } define_checkpoint_api(postsuspend); define_checkpoint_api(preresume); define_checkpoint_api(commit); static void devices_checkpoint_cb(libxl__egc *egc, libxl__multidev *multidev, int rc) { STATE_AO_GC(multidev->ao); /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = CONTAINER_OF(multidev, *cds, multidev); cds->callback(egc, cds, rc); } xen-4.9.2/tools/libxl/libxl_create.c0000664000175000017500000020305313256712137015533 0ustar smbsmb/* * Copyright (C) 2010 Citrix Ltd. * Author Vincent Hanquez * Author Stefano Stabellini * Author Gianni Tedesco * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include "libxl_arch.h" #include #include #include #include #include int libxl__domain_create_info_setdefault(libxl__gc *gc, libxl_domain_create_info *c_info) { if (!c_info->type) { LOG(ERROR, "domain type unspecified"); return ERROR_INVAL; } if (c_info->type == LIBXL_DOMAIN_TYPE_HVM) { libxl_defbool_setdefault(&c_info->hap, true); libxl_defbool_setdefault(&c_info->oos, true); } libxl_defbool_setdefault(&c_info->run_hotplug_scripts, true); libxl_defbool_setdefault(&c_info->driver_domain, false); if (!c_info->ssidref) c_info->ssidref = SECINITSID_DOMU; return 0; } void libxl__rdm_setdefault(libxl__gc *gc, libxl_domain_build_info *b_info) { if (b_info->u.hvm.rdm.policy == LIBXL_RDM_RESERVE_POLICY_INVALID) b_info->u.hvm.rdm.policy = LIBXL_RDM_RESERVE_POLICY_RELAXED; if (b_info->u.hvm.rdm_mem_boundary_memkb == LIBXL_MEMKB_DEFAULT) b_info->u.hvm.rdm_mem_boundary_memkb = LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT; } int libxl__domain_build_info_setdefault(libxl__gc *gc, libxl_domain_build_info *b_info) { int i; if (b_info->type != LIBXL_DOMAIN_TYPE_HVM && b_info->type != LIBXL_DOMAIN_TYPE_PV) { LOG(ERROR, "invalid domain type"); return ERROR_INVAL; } libxl_defbool_setdefault(&b_info->device_model_stubdomain, false); if (libxl_defbool_val(b_info->device_model_stubdomain) && !b_info->device_model_ssidref) b_info->device_model_ssidref = SECINITSID_DOMDM; if (!b_info->device_model_version) { if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) { if (libxl_defbool_val(b_info->device_model_stubdomain)) { b_info->device_model_version = LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL; } else { b_info->device_model_version = libxl__default_device_model(gc); } } else { b_info->device_model_version = LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN; } if (b_info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { const char *dm; int rc; dm = libxl__domain_device_model(gc, b_info); rc = access(dm, X_OK); if (rc < 0) { /* qemu-xen unavailable, use qemu-xen-traditional */ if (errno == ENOENT) { LOGE(INFO, "qemu-xen is unavailable" ", using qemu-xen-traditional instead"); b_info->device_model_version = LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL; } else { LOGE(ERROR, "qemu-xen access error"); return ERROR_FAIL; } } } } if (b_info->blkdev_start == NULL) b_info->blkdev_start = libxl__strdup(NOGC, "xvda"); if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) { if (!b_info->u.hvm.bios) switch (b_info->device_model_version) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: b_info->u.hvm.bios = LIBXL_BIOS_TYPE_ROMBIOS; break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: b_info->u.hvm.bios = LIBXL_BIOS_TYPE_SEABIOS; break; case LIBXL_DEVICE_MODEL_VERSION_NONE: break; default: LOG(ERROR, "unknown device model version"); return ERROR_INVAL; } /* Enforce BIOS<->Device Model version relationship */ switch (b_info->device_model_version) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: if (b_info->u.hvm.bios != LIBXL_BIOS_TYPE_ROMBIOS) { LOG(ERROR, "qemu-xen-traditional requires bios=rombios."); return ERROR_INVAL; } break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: if (b_info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS) { LOG(ERROR, "qemu-xen does not support bios=rombios."); return ERROR_INVAL; } break; case LIBXL_DEVICE_MODEL_VERSION_NONE: break; default:abort(); } /* Check HVM direct boot parameters, we should honour ->ramdisk and * ->cmdline iff ->kernel is set. */ if (!b_info->kernel && (b_info->ramdisk || b_info->cmdline)) { LOG(ERROR, "direct boot parameters specified but kernel missing"); return ERROR_INVAL; } } if (b_info->type == LIBXL_DOMAIN_TYPE_HVM && b_info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL && libxl_defbool_val(b_info->device_model_stubdomain)) { LOG(ERROR, "device model stubdomains require \"qemu-xen-traditional\""); return ERROR_INVAL; } if (!b_info->max_vcpus) b_info->max_vcpus = 1; if (!b_info->avail_vcpus.size) { if (libxl_cpu_bitmap_alloc(CTX, &b_info->avail_vcpus, 1)) { LOG(ERROR, "unable to allocate avail_vcpus bitmap"); return ERROR_FAIL; } libxl_bitmap_set(&b_info->avail_vcpus, 0); } else if (b_info->avail_vcpus.size > HVM_MAX_VCPUS) { LOG(ERROR, "avail_vcpus bitmap contains too many VCPUS"); return ERROR_FAIL; } /* In libxl internals, we want to deal with vcpu_hard_affinity only! */ if (b_info->cpumap.size && !b_info->num_vcpu_hard_affinity) { b_info->vcpu_hard_affinity = libxl__calloc(gc, b_info->max_vcpus, sizeof(libxl_bitmap)); for (i = 0; i < b_info->max_vcpus; i++) { if (libxl_cpu_bitmap_alloc(CTX, &b_info->vcpu_hard_affinity[i], 0)) { LOG(ERROR, "failed to allocate vcpu hard affinity bitmap"); return ERROR_FAIL; } libxl_bitmap_copy(CTX, &b_info->vcpu_hard_affinity[i], &b_info->cpumap); } b_info->num_vcpu_hard_affinity = b_info->max_vcpus; } libxl_defbool_setdefault(&b_info->numa_placement, true); if (b_info->max_memkb == LIBXL_MEMKB_DEFAULT) b_info->max_memkb = 32 * 1024; if (b_info->target_memkb == LIBXL_MEMKB_DEFAULT) b_info->target_memkb = b_info->max_memkb; libxl_defbool_setdefault(&b_info->claim_mode, false); libxl_defbool_setdefault(&b_info->localtime, false); libxl_defbool_setdefault(&b_info->disable_migrate, false); for (i = 0 ; i < b_info->num_iomem; i++) if (b_info->iomem[i].gfn == LIBXL_INVALID_GFN) b_info->iomem[i].gfn = b_info->iomem[i].start; if (!b_info->event_channels) b_info->event_channels = 1023; libxl__arch_domain_build_info_acpi_setdefault(b_info); switch (b_info->type) { case LIBXL_DOMAIN_TYPE_HVM: if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT) b_info->shadow_memkb = 0; if (b_info->u.hvm.mmio_hole_memkb == LIBXL_MEMKB_DEFAULT) b_info->u.hvm.mmio_hole_memkb = 0; if (b_info->u.hvm.vga.kind == LIBXL_VGA_INTERFACE_TYPE_UNKNOWN) { if (b_info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_NONE; else b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_CIRRUS; } if (!b_info->u.hvm.hdtype) b_info->u.hvm.hdtype = LIBXL_HDTYPE_IDE; switch (b_info->device_model_version) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: switch (b_info->u.hvm.vga.kind) { case LIBXL_VGA_INTERFACE_TYPE_NONE: if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) b_info->video_memkb = 0; break; case LIBXL_VGA_INTERFACE_TYPE_QXL: LOG(ERROR,"qemu upstream required for qxl vga"); return ERROR_INVAL; break; case LIBXL_VGA_INTERFACE_TYPE_STD: if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) b_info->video_memkb = 8 * 1024; if (b_info->video_memkb < 8 * 1024) { LOG(ERROR, "videoram must be at least 8 MB for STDVGA on QEMU_XEN_TRADITIONAL"); return ERROR_INVAL; } break; case LIBXL_VGA_INTERFACE_TYPE_CIRRUS: default: if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) b_info->video_memkb = 4 * 1024; if (b_info->video_memkb != 4 * 1024) LOG(WARN, "ignoring videoram other than 4 MB for CIRRUS on QEMU_XEN_TRADITIONAL"); break; } break; case LIBXL_DEVICE_MODEL_VERSION_NONE: if (b_info->u.hvm.vga.kind != LIBXL_VGA_INTERFACE_TYPE_NONE) { LOG(ERROR, "guests without a device model cannot have an emulated video card"); return ERROR_INVAL; } b_info->video_memkb = 0; break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: default: switch (b_info->u.hvm.vga.kind) { case LIBXL_VGA_INTERFACE_TYPE_NONE: if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) b_info->video_memkb = 0; break; case LIBXL_VGA_INTERFACE_TYPE_QXL: if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) { b_info->video_memkb = (128 * 1024); } else if (b_info->video_memkb < (128 * 1024)) { LOG(ERROR, "128 Mib videoram is the minimum for qxl default"); return ERROR_INVAL; } break; case LIBXL_VGA_INTERFACE_TYPE_STD: if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) b_info->video_memkb = 16 * 1024; if (b_info->video_memkb < 16 * 1024) { LOG(ERROR, "videoram must be at least 16 MB for STDVGA on QEMU_XEN"); return ERROR_INVAL; } break; case LIBXL_VGA_INTERFACE_TYPE_CIRRUS: default: if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) b_info->video_memkb = 8 * 1024; if (b_info->video_memkb < 8 * 1024) { LOG(ERROR, "videoram must be at least 8 MB for CIRRUS on QEMU_XEN"); return ERROR_INVAL; } break; } break; } if (b_info->u.hvm.timer_mode == LIBXL_TIMER_MODE_DEFAULT) b_info->u.hvm.timer_mode = LIBXL_TIMER_MODE_NO_DELAY_FOR_MISSED_TICKS; libxl_defbool_setdefault(&b_info->u.hvm.pae, true); libxl_defbool_setdefault(&b_info->u.hvm.apic, true); libxl_defbool_setdefault(&b_info->u.hvm.acpi, true); libxl_defbool_setdefault(&b_info->u.hvm.acpi_s3, true); libxl_defbool_setdefault(&b_info->u.hvm.acpi_s4, true); libxl_defbool_setdefault(&b_info->u.hvm.acpi_laptop_slate, false); libxl_defbool_setdefault(&b_info->u.hvm.nx, true); libxl_defbool_setdefault(&b_info->u.hvm.viridian, false); libxl_defbool_setdefault(&b_info->u.hvm.hpet, true); libxl_defbool_setdefault(&b_info->u.hvm.vpt_align, true); libxl_defbool_setdefault(&b_info->u.hvm.nested_hvm, false); libxl_defbool_setdefault(&b_info->u.hvm.altp2m, false); libxl_defbool_setdefault(&b_info->u.hvm.usb, false); libxl_defbool_setdefault(&b_info->u.hvm.xen_platform_pci, true); libxl_defbool_setdefault(&b_info->u.hvm.spice.enable, false); if (!libxl_defbool_val(b_info->u.hvm.spice.enable) && (b_info->u.hvm.spice.usbredirection > 0) ){ b_info->u.hvm.spice.usbredirection = 0; LOG(WARN, "spice disabled, disabling usbredirection"); } if (!b_info->u.hvm.usbversion && (b_info->u.hvm.spice.usbredirection > 0) ) b_info->u.hvm.usbversion = 2; if ((b_info->u.hvm.usbversion || b_info->u.hvm.spice.usbredirection) && ( libxl_defbool_val(b_info->u.hvm.usb) || b_info->u.hvm.usbdevice_list || b_info->u.hvm.usbdevice) ){ LOG(ERROR,"usbversion and/or usbredirection cannot be " "enabled with usb and/or usbdevice parameters."); return ERROR_INVAL; } if (!b_info->u.hvm.boot) b_info->u.hvm.boot = libxl__strdup(NOGC, "cda"); libxl_defbool_setdefault(&b_info->u.hvm.vnc.enable, true); if (libxl_defbool_val(b_info->u.hvm.vnc.enable)) { libxl_defbool_setdefault(&b_info->u.hvm.vnc.findunused, true); if (!b_info->u.hvm.vnc.listen) b_info->u.hvm.vnc.listen = libxl__strdup(NOGC, "127.0.0.1"); } libxl_defbool_setdefault(&b_info->u.hvm.sdl.enable, false); if (libxl_defbool_val(b_info->u.hvm.sdl.enable)) { libxl_defbool_setdefault(&b_info->u.hvm.sdl.opengl, false); } if (libxl_defbool_val(b_info->u.hvm.spice.enable)) { libxl_defbool_setdefault(&b_info->u.hvm.spice.disable_ticketing, false); libxl_defbool_setdefault(&b_info->u.hvm.spice.agent_mouse, true); libxl_defbool_setdefault(&b_info->u.hvm.spice.vdagent, false); libxl_defbool_setdefault(&b_info->u.hvm.spice.clipboard_sharing, false); } libxl_defbool_setdefault(&b_info->u.hvm.nographic, false); libxl_defbool_setdefault(&b_info->u.hvm.gfx_passthru, false); libxl__rdm_setdefault(gc, b_info); break; case LIBXL_DOMAIN_TYPE_PV: libxl_defbool_setdefault(&b_info->u.pv.e820_host, false); if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) b_info->video_memkb = 0; if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT) b_info->shadow_memkb = 0; if (b_info->u.pv.slack_memkb == LIBXL_MEMKB_DEFAULT) b_info->u.pv.slack_memkb = 0; /* For compatibility, fill in b_info->kernel|ramdisk|cmdline * with the value in u.pv, later processing will use * b_info->kernel|ramdisk|cmdline only. * User with old APIs that passes u.pv.kernel|ramdisk|cmdline * is not affected. */ if (!b_info->kernel && b_info->u.pv.kernel) { b_info->kernel = b_info->u.pv.kernel; b_info->u.pv.kernel = NULL; } if (!b_info->ramdisk && b_info->u.pv.ramdisk) { b_info->ramdisk = b_info->u.pv.ramdisk; b_info->u.pv.ramdisk = NULL; } if (!b_info->cmdline && b_info->u.pv.cmdline) { b_info->cmdline = b_info->u.pv.cmdline; b_info->u.pv.cmdline = NULL; } break; default: LOG(ERROR, "invalid domain type %s in create info", libxl_domain_type_to_string(b_info->type)); return ERROR_INVAL; } return 0; } static void init_console_info(libxl__gc *gc, libxl__device_console *console, int dev_num) { libxl__device_console_init(console); console->devid = dev_num; console->consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED; console->output = libxl__strdup(NOGC, "pty"); /* console->{name,connection,path} are NULL on normal consoles. Only 'channels' when mapped to consoles have a string name. */ } int libxl__domain_build(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid, libxl__domain_build_state *state) { libxl_domain_build_info *const info = &d_config->b_info; char **vments = NULL, **localents = NULL; struct timeval start_time; int i, ret; ret = libxl__build_pre(gc, domid, d_config, state); if (ret) goto out; gettimeofday(&start_time, NULL); switch (info->type) { case LIBXL_DOMAIN_TYPE_HVM: ret = libxl__build_hvm(gc, domid, d_config, state); if (ret) goto out; vments = libxl__calloc(gc, 7, sizeof(char *)); vments[0] = "rtc/timeoffset"; vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : ""; vments[2] = "image/ostype"; vments[3] = "hvm"; vments[4] = "start_time"; vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000); localents = libxl__calloc(gc, 11, sizeof(char *)); i = 0; localents[i++] = "platform/acpi"; localents[i++] = libxl__acpi_defbool_val(info) ? "1" : "0"; localents[i++] = "platform/acpi_s3"; localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s3) ? "1" : "0"; localents[i++] = "platform/acpi_s4"; localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s4) ? "1" : "0"; localents[i++] = "platform/acpi_laptop_slate"; localents[i++] = libxl_defbool_val(info->u.hvm.acpi_laptop_slate) ? "1" : "0"; if (info->u.hvm.mmio_hole_memkb) { uint64_t max_ram_below_4g = (1ULL << 32) - (info->u.hvm.mmio_hole_memkb << 10); if (max_ram_below_4g <= HVM_BELOW_4G_MMIO_START) { localents[i++] = "platform/mmio_hole_size"; localents[i++] = GCSPRINTF("%"PRIu64, info->u.hvm.mmio_hole_memkb << 10); } } break; case LIBXL_DOMAIN_TYPE_PV: ret = libxl__build_pv(gc, domid, info, state); if (ret) goto out; vments = libxl__calloc(gc, 11, sizeof(char *)); i = 0; vments[i++] = "image/ostype"; vments[i++] = "linux"; vments[i++] = "image/kernel"; vments[i++] = (char *) state->pv_kernel.path; vments[i++] = "start_time"; vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000); if (state->pv_ramdisk.path) { vments[i++] = "image/ramdisk"; vments[i++] = (char *) state->pv_ramdisk.path; } if (state->pv_cmdline) { vments[i++] = "image/cmdline"; vments[i++] = (char *) state->pv_cmdline; } break; default: ret = ERROR_INVAL; goto out; } ret = libxl__build_post(gc, domid, info, state, vments, localents); out: return ret; } int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config, uint32_t *domid, xc_domain_configuration_t *xc_config) { libxl_ctx *ctx = libxl__gc_owner(gc); int flags, ret, rc, nb_vm; char *uuid_string; char *dom_path, *vm_path, *libxl_path; struct xs_permissions roperm[2]; struct xs_permissions rwperm[1]; struct xs_permissions noperm[1]; xs_transaction_t t = 0; xen_domain_handle_t handle; libxl_vminfo *vm_list; /* convenience aliases */ libxl_domain_create_info *info = &d_config->c_info; uuid_string = libxl__uuid2string(gc, info->uuid); if (!uuid_string) { rc = ERROR_NOMEM; goto out; } flags = 0; if (info->type == LIBXL_DOMAIN_TYPE_HVM) { flags |= XEN_DOMCTL_CDF_hvm_guest; flags |= libxl_defbool_val(info->hap) ? XEN_DOMCTL_CDF_hap : 0; flags |= libxl_defbool_val(info->oos) ? 0 : XEN_DOMCTL_CDF_oos_off; } /* Ultimately, handle is an array of 16 uint8_t, same as uuid */ libxl_uuid_copy(ctx, (libxl_uuid *)handle, &info->uuid); ret = libxl__arch_domain_prepare_config(gc, d_config, xc_config); if (ret < 0) { LOGED(ERROR, *domid, "fail to get domain config"); rc = ERROR_FAIL; goto out; } /* Valid domid here means we're soft resetting. */ if (!libxl_domid_valid_guest(*domid)) { ret = xc_domain_create(ctx->xch, info->ssidref, handle, flags, domid, xc_config); if (ret < 0) { LOGED(ERROR, *domid, "domain creation fail"); rc = ERROR_FAIL; goto out; } } rc = libxl__arch_domain_save_config(gc, d_config, xc_config); if (rc < 0) goto out; ret = xc_cpupool_movedomain(ctx->xch, info->poolid, *domid); if (ret < 0) { LOGED(ERROR, *domid, "domain move fail"); rc = ERROR_FAIL; goto out; } dom_path = libxl__xs_get_dompath(gc, *domid); if (!dom_path) { rc = ERROR_FAIL; goto out; } vm_path = GCSPRINTF("/vm/%s", uuid_string); if (!vm_path) { LOGD(ERROR, *domid, "cannot allocate create paths"); rc = ERROR_FAIL; goto out; } libxl_path = libxl__xs_libxl_path(gc, *domid); if (!libxl_path) { rc = ERROR_FAIL; goto out; } noperm[0].id = 0; noperm[0].perms = XS_PERM_NONE; roperm[0].id = 0; roperm[0].perms = XS_PERM_NONE; roperm[1].id = *domid; roperm[1].perms = XS_PERM_READ; rwperm[0].id = *domid; rwperm[0].perms = XS_PERM_NONE; retry_transaction: t = xs_transaction_start(ctx->xsh); xs_rm(ctx->xsh, t, dom_path); libxl__xs_mknod(gc, t, dom_path, roperm, ARRAY_SIZE(roperm)); xs_rm(ctx->xsh, t, vm_path); libxl__xs_mknod(gc, t, vm_path, roperm, ARRAY_SIZE(roperm)); xs_rm(ctx->xsh, t, libxl_path); libxl__xs_mknod(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/device", libxl_path), noperm, ARRAY_SIZE(noperm)); xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path)); rc = libxl__domain_rename(gc, *domid, 0, info->name, t); if (rc) goto out; libxl__xs_mknod(gc, t, GCSPRINTF("%s/cpu", dom_path), roperm, ARRAY_SIZE(roperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/memory", dom_path), roperm, ARRAY_SIZE(roperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/device", dom_path), roperm, ARRAY_SIZE(roperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/control", dom_path), roperm, ARRAY_SIZE(roperm)); if (info->type == LIBXL_DOMAIN_TYPE_HVM) libxl__xs_mknod(gc, t, GCSPRINTF("%s/hvmloader", dom_path), roperm, ARRAY_SIZE(roperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/control/shutdown", dom_path), rwperm, ARRAY_SIZE(rwperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/control/feature-poweroff", dom_path), rwperm, ARRAY_SIZE(rwperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/control/feature-reboot", dom_path), rwperm, ARRAY_SIZE(rwperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/control/feature-suspend", dom_path), rwperm, ARRAY_SIZE(rwperm)); if (info->type == LIBXL_DOMAIN_TYPE_HVM) { libxl__xs_mknod(gc, t, GCSPRINTF("%s/control/feature-s3", dom_path), rwperm, ARRAY_SIZE(rwperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/control/feature-s4", dom_path), rwperm, ARRAY_SIZE(rwperm)); } libxl__xs_mknod(gc, t, GCSPRINTF("%s/device/suspend/event-channel", dom_path), rwperm, ARRAY_SIZE(rwperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/data", dom_path), rwperm, ARRAY_SIZE(rwperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/drivers", dom_path), rwperm, ARRAY_SIZE(rwperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/feature", dom_path), rwperm, ARRAY_SIZE(rwperm)); libxl__xs_mknod(gc, t, GCSPRINTF("%s/attr", dom_path), rwperm, ARRAY_SIZE(rwperm)); if (libxl_defbool_val(info->driver_domain)) { /* * Create a local "libxl" directory for each guest, since we might want * to use libxl from inside the guest */ libxl__xs_mknod(gc, t, GCSPRINTF("%s/libxl", dom_path), rwperm, ARRAY_SIZE(rwperm)); /* * Create a local "device-model" directory for each guest, since we * might want to use Qemu from inside the guest */ libxl__xs_mknod(gc, t, GCSPRINTF("%s/device-model", dom_path), rwperm, ARRAY_SIZE(rwperm)); } vm_list = libxl_list_vm(ctx, &nb_vm); if (!vm_list) { LOGD(ERROR, *domid, "cannot get number of running guests"); rc = ERROR_FAIL; goto out; } libxl_vminfo_list_free(vm_list, nb_vm); xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string)); xs_write(ctx->xsh, t, GCSPRINTF("%s/name", vm_path), info->name, strlen(info->name)); libxl__xs_writev(gc, t, dom_path, info->xsdata); libxl__xs_writev(gc, t, GCSPRINTF("%s/platform", dom_path), info->platformdata); xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-multiprocessor-suspend", dom_path), "1", 1); xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-xs_reset_watches", dom_path), "1", 1); if (!xs_transaction_end(ctx->xsh, t, 0)) { if (errno == EAGAIN) { t = 0; goto retry_transaction; } LOGED(ERROR, *domid, "domain creation ""xenstore transaction commit failed"); rc = ERROR_FAIL; goto out; } t = 0; rc = 0; out: if (t) xs_transaction_end(ctx->xsh, t, 1); return rc; } static int store_libxl_entry(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *b_info) { char *path = NULL; path = libxl__xs_libxl_path(gc, domid); path = GCSPRINTF("%s/dm-version", path); return libxl__xs_printf(gc, XBT_NULL, path, "%s", libxl_device_model_version_to_string(b_info->device_model_version)); } /*----- main domain creation -----*/ /* We have a linear control flow; only one event callback is * outstanding at any time. Each initiation and callback function * arranges for the next to be called, as the very last thing it * does. (If that particular sub-operation is not needed, a * function will call the next event callback directly.) */ /* Event callbacks, in this order: */ static void domcreate_devmodel_started(libxl__egc *egc, libxl__dm_spawn_state *dmss, int rc); static void domcreate_bootloader_console_available(libxl__egc *egc, libxl__bootloader_state *bl); static void domcreate_bootloader_done(libxl__egc *egc, libxl__bootloader_state *bl, int rc); static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *aodevs, int ret); static void domcreate_console_available(libxl__egc *egc, libxl__domain_create_state *dcs); static void domcreate_stream_done(libxl__egc *egc, libxl__stream_read_state *srs, int ret); static void domcreate_rebuild_done(libxl__egc *egc, libxl__domain_create_state *dcs, int ret); /* Our own function to clean up and call the user's callback. * The final call in the sequence. */ static void domcreate_complete(libxl__egc *egc, libxl__domain_create_state *dcs, int rc); /* If creation is not successful, this callback will be executed * when domain destruction is finished */ static void domcreate_destruction_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, int rc); static void initiate_domain_create(libxl__egc *egc, libxl__domain_create_state *dcs) { STATE_AO_GC(dcs->ao); libxl_ctx *ctx = libxl__gc_owner(gc); uint32_t domid; int i, ret; bool pod_enabled = false; /* convenience aliases */ libxl_domain_config *const d_config = dcs->guest_config; libxl__domain_build_state *const state = &dcs->build_state; const int restore_fd = dcs->restore_fd; domid = dcs->domid_soft_reset; if (d_config->c_info.ssid_label) { char *s = d_config->c_info.ssid_label; ret = libxl_flask_context_to_sid(ctx, s, strlen(s), &d_config->c_info.ssidref); if (ret) { if (errno == ENOSYS) { LOGD(WARN, domid, "XSM Disabled: init_seclabel not supported"); ret = 0; } else { LOGD(ERROR, domid, "Invalid init_seclabel: %s", s); goto error_out; } } } if (d_config->b_info.exec_ssid_label) { char *s = d_config->b_info.exec_ssid_label; ret = libxl_flask_context_to_sid(ctx, s, strlen(s), &d_config->b_info.exec_ssidref); if (ret) { if (errno == ENOSYS) { LOGD(WARN, domid, "XSM Disabled: seclabel not supported"); ret = 0; } else { LOGD(ERROR, domid, "Invalid seclabel: %s", s); goto error_out; } } } if (d_config->b_info.device_model_ssid_label) { char *s = d_config->b_info.device_model_ssid_label; ret = libxl_flask_context_to_sid(ctx, s, strlen(s), &d_config->b_info.device_model_ssidref); if (ret) { if (errno == ENOSYS) { LOGD(WARN, domid, "XSM Disabled: device_model_stubdomain_seclabel not supported"); ret = 0; } else { LOGD(ERROR, domid, "Invalid device_model_stubdomain_seclabel: %s", s); goto error_out; } } } if (d_config->c_info.pool_name) { d_config->c_info.poolid = -1; libxl_cpupool_qualifier_to_cpupoolid(ctx, d_config->c_info.pool_name, &d_config->c_info.poolid, NULL); } if (!libxl_cpupoolid_is_valid(ctx, d_config->c_info.poolid)) { LOGD(ERROR, domid, "Illegal pool specified: %s", d_config->c_info.pool_name); ret = ERROR_INVAL; goto error_out; } /* If target_memkb is smaller than max_memkb, the subsequent call * to libxc when building HVM domain will enable PoD mode. */ pod_enabled = (d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM) && (d_config->b_info.target_memkb < d_config->b_info.max_memkb); /* We cannot have PoD and PCI device assignment at the same time * for HVM guest. It was reported that IOMMU cannot work with PoD * enabled because it needs to populated entire page table for * guest. To stay on the safe side, we disable PCI device * assignment when PoD is enabled. */ if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM && d_config->num_pcidevs && pod_enabled) { ret = ERROR_INVAL; LOGD(ERROR, domid, "PCI device assignment for HVM guest failed due to PoD enabled"); goto error_out; } /* Disallow PoD and vNUMA to be enabled at the same time because PoD * pool is not vNUMA-aware yet. */ if (pod_enabled && d_config->b_info.num_vnuma_nodes) { ret = ERROR_INVAL; LOGD(ERROR, domid, "Cannot enable PoD and vNUMA at the same time"); goto error_out; } /* PV vNUMA is not yet supported because there is an issue with * cpuid handling. */ if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV && d_config->b_info.num_vnuma_nodes) { ret = ERROR_INVAL; LOGD(ERROR, domid, "PV vNUMA is not yet supported"); goto error_out; } ret = libxl__domain_create_info_setdefault(gc, &d_config->c_info); if (ret) { LOGD(ERROR, domid, "Unable to set domain create info defaults"); goto error_out; } ret = libxl__domain_build_info_setdefault(gc, &d_config->b_info); if (ret) { LOGD(ERROR, domid, "Unable to set domain build info defaults"); goto error_out; } if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM && (libxl_defbool_val(d_config->b_info.u.hvm.nested_hvm) && (libxl_defbool_val(d_config->b_info.u.hvm.altp2m) || (d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)))) { ret = ERROR_INVAL; LOGD(ERROR, domid, "nestedhvm and altp2mhvm cannot be used together"); goto error_out; } if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM && (libxl_defbool_val(d_config->b_info.u.hvm.altp2m) || (d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)) && pod_enabled) { ret = ERROR_INVAL; LOGD(ERROR, domid, "Cannot enable PoD and ALTP2M at the same time"); goto error_out; } ret = libxl__domain_make(gc, d_config, &domid, &state->config); if (ret) { LOGD(ERROR, domid, "cannot make domain: %d", ret); dcs->guest_domid = domid; ret = ERROR_FAIL; goto error_out; } dcs->guest_domid = domid; dcs->sdss.dm.guest_domid = 0; /* means we haven't spawned */ /* * Set the dm version quite early so that libxl doesn't have to pass the * build info around just to know if the domain has a device model or not. */ store_libxl_entry(gc, domid, &d_config->b_info); for (i = 0; i < d_config->num_disks; i++) { ret = libxl__device_disk_setdefault(gc, &d_config->disks[i], domid); if (ret) { LOGD(ERROR, domid, "Unable to set disk defaults for disk %d", i); goto error_out; } } dcs->bl.ao = ao; libxl_device_disk *bootdisk = d_config->num_disks > 0 ? &d_config->disks[0] : NULL; /* * The devid has to be set before launching the device model. For the * hotplug case this is done in libxl_device_nic_add but on domain * creation this is called too late. * Make two runs over configured NICs in order to avoid duplicate IDs * in case the caller partially assigned IDs. */ ret = libxl__device_nic_set_devids(gc, d_config, domid); if (ret) goto error_out; if (restore_fd >= 0 || dcs->domid_soft_reset != INVALID_DOMID) { LOGD(DEBUG, domid, "restoring, not running bootloader"); domcreate_bootloader_done(egc, &dcs->bl, 0); } else { LOGD(DEBUG, domid, "running bootloader"); dcs->bl.callback = domcreate_bootloader_done; dcs->bl.console_available = domcreate_bootloader_console_available; dcs->bl.info = &d_config->b_info; dcs->bl.disk = bootdisk; dcs->bl.domid = dcs->guest_domid; dcs->bl.kernel = &dcs->build_state.pv_kernel; dcs->bl.ramdisk = &dcs->build_state.pv_ramdisk; libxl__bootloader_run(egc, &dcs->bl); } return; error_out: assert(ret); domcreate_complete(egc, dcs, ret); } static void domcreate_bootloader_console_available(libxl__egc *egc, libxl__bootloader_state *bl) { libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl); STATE_AO_GC(bl->ao); domcreate_console_available(egc, dcs); } static void domcreate_console_available(libxl__egc *egc, libxl__domain_create_state *dcs) { libxl__ao_progress_report(egc, dcs->ao, &dcs->aop_console_how, NEW_EVENT(egc, DOMAIN_CREATE_CONSOLE_AVAILABLE, dcs->guest_domid, dcs->aop_console_how.for_event)); } static void libxl__colo_restore_setup_done(libxl__egc *egc, libxl__colo_restore_state *crs, int rc) { libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); EGC_GC; if (rc) { LOGD(ERROR, dcs->guest_domid, "colo restore setup fails: %d", rc); domcreate_stream_done(egc, &dcs->srs, rc); return; } libxl__stream_read_start(egc, &dcs->srs); } static void domcreate_bootloader_done(libxl__egc *egc, libxl__bootloader_state *bl, int rc) { libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl); STATE_AO_GC(bl->ao); /* convenience aliases */ const uint32_t domid = dcs->guest_domid; libxl_domain_config *const d_config = dcs->guest_config; const int restore_fd = dcs->restore_fd; libxl__domain_build_state *const state = &dcs->build_state; const int checkpointed_stream = dcs->restore_params.checkpointed_stream; libxl__colo_restore_state *const crs = &dcs->crs; libxl_domain_build_info *const info = &d_config->b_info; libxl__srm_restore_autogen_callbacks *const callbacks = &dcs->srs.shs.callbacks.restore.a; if (rc) { domcreate_rebuild_done(egc, dcs, rc); return; } /* consume bootloader outputs. state->pv_{kernel,ramdisk} have * been initialised by the bootloader already. */ state->pv_cmdline = bl->cmdline; /* We might be going to call libxl__spawn_local_dm, or _spawn_stub_dm. * Fill in any field required by either, including both relevant * callbacks (_spawn_stub_dm will overwrite our trespass if needed). */ dcs->sdss.dm.spawn.ao = ao; dcs->sdss.dm.guest_config = dcs->guest_config; dcs->sdss.dm.build_state = &dcs->build_state; dcs->sdss.dm.callback = domcreate_devmodel_started; dcs->sdss.callback = domcreate_devmodel_started; if (restore_fd < 0 && dcs->domid_soft_reset == INVALID_DOMID) { rc = libxl__domain_build(gc, d_config, domid, state); domcreate_rebuild_done(egc, dcs, rc); return; } /* Restore */ callbacks->restore_results = libxl__srm_callout_callback_restore_results; /* COLO only supports HVM now because it does not work very * well with pv drivers: * 1. We need to resume vm in the slow path. In this case we * need to disconnect/reconnect backend and frontend. It * will take too much time and the performance is very slow. * 2. PV disk cannot reuse block replication that is implemented * in QEMU. */ if (info->type != LIBXL_DOMAIN_TYPE_HVM && checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) { LOGD(ERROR, domid, "COLO only supports HVM, unable to restore domain"); rc = ERROR_FAIL; goto out; } rc = libxl__build_pre(gc, domid, d_config, state); if (rc) goto out; dcs->srs.ao = ao; dcs->srs.dcs = dcs; dcs->srs.fd = restore_fd; dcs->srs.legacy = (dcs->restore_params.stream_version == 1); dcs->srs.back_channel = false; dcs->srs.completion_callback = domcreate_stream_done; if (restore_fd >= 0) { switch (checkpointed_stream) { case LIBXL_CHECKPOINTED_STREAM_COLO: /* colo restore setup */ crs->ao = ao; crs->domid = domid; crs->send_back_fd = dcs->send_back_fd; crs->recv_fd = restore_fd; crs->hvm = (info->type == LIBXL_DOMAIN_TYPE_HVM); crs->callback = libxl__colo_restore_setup_done; libxl__colo_restore_setup(egc, crs); break; case LIBXL_CHECKPOINTED_STREAM_REMUS: libxl__remus_restore_setup(egc, dcs); /* fall through */ case LIBXL_CHECKPOINTED_STREAM_NONE: libxl__stream_read_start(egc, &dcs->srs); } return; } out: domcreate_stream_done(egc, &dcs->srs, rc); } void libxl__srm_callout_callback_restore_results(xen_pfn_t store_mfn, xen_pfn_t console_mfn, void *user) { libxl__save_helper_state *shs = user; libxl__domain_create_state *dcs = shs->caller_state; STATE_AO_GC(dcs->ao); libxl__domain_build_state *const state = &dcs->build_state; state->store_mfn = store_mfn; state->console_mfn = console_mfn; shs->need_results = 0; } static void domcreate_stream_done(libxl__egc *egc, libxl__stream_read_state *srs, int ret) { libxl__domain_create_state *dcs = srs->dcs; STATE_AO_GC(dcs->ao); libxl_ctx *ctx = libxl__gc_owner(gc); char **vments = NULL, **localents = NULL; struct timeval start_time; int i, esave; /* convenience aliases */ const uint32_t domid = dcs->guest_domid; libxl_domain_config *const d_config = dcs->guest_config; libxl_domain_build_info *const info = &d_config->b_info; libxl__domain_build_state *const state = &dcs->build_state; const int fd = dcs->restore_fd; if (ret) goto out; gettimeofday(&start_time, NULL); switch (info->type) { case LIBXL_DOMAIN_TYPE_HVM: vments = libxl__calloc(gc, 7, sizeof(char *)); vments[0] = "rtc/timeoffset"; vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : ""; vments[2] = "image/ostype"; vments[3] = "hvm"; vments[4] = "start_time"; vments[5] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000); break; case LIBXL_DOMAIN_TYPE_PV: vments = libxl__calloc(gc, 11, sizeof(char *)); i = 0; vments[i++] = "image/ostype"; vments[i++] = "linux"; vments[i++] = "image/kernel"; vments[i++] = (char *) state->pv_kernel.path; vments[i++] = "start_time"; vments[i++] = GCSPRINTF("%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000); if (state->pv_ramdisk.path) { vments[i++] = "image/ramdisk"; vments[i++] = (char *) state->pv_ramdisk.path; } if (state->pv_cmdline) { vments[i++] = "image/cmdline"; vments[i++] = (char *) state->pv_cmdline; } break; default: ret = ERROR_INVAL; goto out; } ret = libxl__build_post(gc, domid, info, state, vments, localents); if (ret) goto out; if (info->type == LIBXL_DOMAIN_TYPE_HVM) { state->saved_state = GCSPRINTF( LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid); } out: if (info->type == LIBXL_DOMAIN_TYPE_PV) { libxl__file_reference_unmap(&state->pv_kernel); libxl__file_reference_unmap(&state->pv_ramdisk); } /* fd == -1 here means we're doing soft reset. */ if (fd != -1) { esave = errno; libxl_fd_set_nonblock(ctx, fd, 0); errno = esave; } domcreate_rebuild_done(egc, dcs, ret); } static void domcreate_rebuild_done(libxl__egc *egc, libxl__domain_create_state *dcs, int ret) { STATE_AO_GC(dcs->ao); /* convenience aliases */ const uint32_t domid = dcs->guest_domid; libxl_domain_config *const d_config = dcs->guest_config; if (ret) { LOGD(ERROR, domid, "cannot (re-)build domain: %d", ret); ret = ERROR_FAIL; goto error_out; } store_libxl_entry(gc, domid, &d_config->b_info); libxl__multidev_begin(ao, &dcs->multidev); dcs->multidev.callback = domcreate_launch_dm; libxl__add_disks(egc, ao, domid, d_config, &dcs->multidev); libxl__multidev_prepared(egc, &dcs->multidev, 0); return; error_out: assert(ret); domcreate_complete(egc, dcs, ret); } static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *multidev, int ret) { libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev); STATE_AO_GC(dcs->ao); int i; /* convenience aliases */ const uint32_t domid = dcs->guest_domid; libxl_domain_config *const d_config = dcs->guest_config; libxl__domain_build_state *const state = &dcs->build_state; if (ret) { LOGD(ERROR, domid, "unable to add disk devices"); goto error_out; } for (i = 0; i < d_config->b_info.num_ioports; i++) { libxl_ioport_range *io = &d_config->b_info.ioports[i]; LOGD(DEBUG, domid, "ioports %"PRIx32"-%"PRIx32, io->first, io->first + io->number - 1); ret = xc_domain_ioport_permission(CTX->xch, domid, io->first, io->number, 1); if (ret < 0) { LOGED(ERROR, domid, "failed give domain access to ioports %"PRIx32"-%"PRIx32, io->first, io->first + io->number - 1); ret = ERROR_FAIL; goto error_out; } } for (i = 0; i < d_config->b_info.num_irqs; i++) { int irq = d_config->b_info.irqs[i]; LOGD(DEBUG, domid, "irq %d", irq); ret = irq >= 0 ? libxl__arch_domain_map_irq(gc, domid, irq) : -EOVERFLOW; if (ret) { LOGED(ERROR, domid, "failed give domain access to irq %d", irq); ret = ERROR_FAIL; goto error_out; } } for (i = 0; i < d_config->b_info.num_iomem; i++) { libxl_iomem_range *io = &d_config->b_info.iomem[i]; LOGD(DEBUG, domid, "iomem %"PRIx64"-%"PRIx64, io->start, io->start + io->number - 1); ret = xc_domain_iomem_permission(CTX->xch, domid, io->start, io->number, 1); if (ret < 0) { LOGED(ERROR, domid, "failed give domain access to iomem range %"PRIx64"-%"PRIx64, io->start, io->start + io->number - 1); ret = ERROR_FAIL; goto error_out; } ret = xc_domain_memory_mapping(CTX->xch, domid, io->gfn, io->start, io->number, 1); if (ret < 0) { LOGED(ERROR, domid, "failed to map to domain iomem range %"PRIx64"-%"PRIx64 " to guest address %"PRIx64, io->start, io->start + io->number - 1, io->gfn); ret = ERROR_FAIL; goto error_out; } } /* For both HVM and PV the 0th console is a regular console. We map channels to IOEMU consoles starting at 1 */ for (i = 0; i < d_config->num_channels; i++) { libxl__device_console console; libxl__device device; ret = libxl__init_console_from_channel(gc, &console, i + 1, &d_config->channels[i]); if ( ret ) { libxl__device_console_dispose(&console); goto error_out; } libxl__device_console_add(gc, domid, &console, NULL, &device); libxl__device_console_dispose(&console); } for (i = 0; i < d_config->num_p9s; i++) libxl__device_p9_add(gc, domid, &d_config->p9[i]); switch (d_config->c_info.type) { case LIBXL_DOMAIN_TYPE_HVM: { libxl__device_console console; libxl__device device; libxl_device_vkb vkb; init_console_info(gc, &console, 0); console.backend_domid = state->console_domid; libxl__device_console_add(gc, domid, &console, state, &device); libxl__device_console_dispose(&console); if (d_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) { domcreate_devmodel_started(egc, &dcs->sdss.dm, 0); return; } libxl_device_vkb_init(&vkb); libxl__device_vkb_add(gc, domid, &vkb); libxl_device_vkb_dispose(&vkb); dcs->sdss.dm.guest_domid = domid; if (libxl_defbool_val(d_config->b_info.device_model_stubdomain)) libxl__spawn_stub_dm(egc, &dcs->sdss); else libxl__spawn_local_dm(egc, &dcs->sdss.dm); /* * Handle the domain's (and the related stubdomain's) access to * the VGA framebuffer. */ ret = libxl__grant_vga_iomem_permission(gc, domid, d_config); if ( ret ) goto error_out; return; } case LIBXL_DOMAIN_TYPE_PV: { libxl__device_console console; libxl__device device; for (i = 0; i < d_config->num_vfbs; i++) { libxl__device_vfb_add(gc, domid, &d_config->vfbs[i]); libxl__device_vkb_add(gc, domid, &d_config->vkbs[i]); } init_console_info(gc, &console, 0); console.backend_domid = state->console_domid; libxl__device_console_add(gc, domid, &console, state, &device); libxl__device_console_dispose(&console); ret = libxl__need_xenpv_qemu(gc, d_config); if (ret < 0) goto error_out; if (ret) { dcs->sdss.dm.guest_domid = domid; libxl__spawn_local_dm(egc, &dcs->sdss.dm); return; } else { assert(!dcs->sdss.dm.guest_domid); domcreate_devmodel_started(egc, &dcs->sdss.dm, 0); return; } } default: ret = ERROR_INVAL; goto error_out; } abort(); /* not reached */ error_out: assert(ret); domcreate_complete(egc, dcs, ret); } static void libxl__add_dtdevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid, libxl_domain_config *d_config, libxl__multidev *multidev) { AO_GC; libxl__ao_device *aodev = libxl__multidev_prepare(multidev); int i, rc = 0; for (i = 0; i < d_config->num_dtdevs; i++) { const libxl_device_dtdev *dtdev = &d_config->dtdevs[i]; LOGD(DEBUG, domid, "Assign device \"%s\" to domain", dtdev->path); rc = xc_assign_dt_device(CTX->xch, domid, dtdev->path); if (rc < 0) { LOGD(ERROR, domid, "xc_assign_dtdevice failed: %d", rc); goto out; } } out: aodev->rc = rc; aodev->callback(egc, aodev); } #define libxl_device_dtdev_list NULL #define libxl_device_dtdev_compare NULL static DEFINE_DEVICE_TYPE_STRUCT(dtdev); const struct libxl_device_type *device_type_tbl[] = { &libxl__disk_devtype, &libxl__nic_devtype, &libxl__vtpm_devtype, &libxl__usbctrl_devtype, &libxl__usbdev_devtype, &libxl__pcidev_devtype, &libxl__dtdev_devtype, NULL }; static void domcreate_attach_devices(libxl__egc *egc, libxl__multidev *multidev, int ret) { libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev); STATE_AO_GC(dcs->ao); int domid = dcs->guest_domid; libxl_domain_config *const d_config = dcs->guest_config; const struct libxl_device_type *dt; if (ret) { LOGD(ERROR, domid, "unable to add %s devices", device_type_tbl[dcs->device_type_idx]->type); goto error_out; } dcs->device_type_idx++; dt = device_type_tbl[dcs->device_type_idx]; if (dt) { if (*libxl__device_type_get_num(dt, d_config) > 0 && !dt->skip_attach) { /* Attach devices */ libxl__multidev_begin(ao, &dcs->multidev); dcs->multidev.callback = domcreate_attach_devices; dt->add(egc, ao, domid, d_config, &dcs->multidev); libxl__multidev_prepared(egc, &dcs->multidev, 0); return; } domcreate_attach_devices(egc, &dcs->multidev, 0); return; } domcreate_console_available(egc, dcs); domcreate_complete(egc, dcs, 0); return; error_out: assert(ret); domcreate_complete(egc, dcs, ret); } static void domcreate_devmodel_started(libxl__egc *egc, libxl__dm_spawn_state *dmss, int ret) { libxl__domain_create_state *dcs = CONTAINER_OF(dmss, *dcs, sdss.dm); STATE_AO_GC(dmss->spawn.ao); int domid = dcs->guest_domid; /* convenience aliases */ libxl_domain_config *const d_config = dcs->guest_config; if (ret) { LOGD(ERROR, domid, "device model did not start: %d", ret); goto error_out; } if (dcs->sdss.dm.guest_domid) { if (d_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { libxl__qmp_initializations(gc, domid, d_config); } } dcs->device_type_idx = -1; domcreate_attach_devices(egc, &dcs->multidev, 0); return; error_out: assert(ret); domcreate_complete(egc, dcs, ret); } static void domcreate_complete(libxl__egc *egc, libxl__domain_create_state *dcs, int rc) { STATE_AO_GC(dcs->ao); libxl_domain_config *const d_config = dcs->guest_config; libxl_domain_config *d_config_saved = &dcs->guest_config_saved; libxl__file_reference_unmap(&dcs->build_state.pv_kernel); libxl__file_reference_unmap(&dcs->build_state.pv_ramdisk); if (!rc && d_config->b_info.exec_ssidref) rc = xc_flask_relabel_domain(CTX->xch, dcs->guest_domid, d_config->b_info.exec_ssidref); bool retain_domain = !rc || rc == ERROR_ABORTED; if (retain_domain) { libxl__domain_userdata_lock *lock; /* Note that we hold CTX lock at this point so only need to * take data store lock */ lock = libxl__lock_domain_userdata(gc, dcs->guest_domid); if (!lock) { rc = ERROR_LOCK_FAIL; } else { libxl__update_domain_configuration(gc, d_config_saved, d_config); int cfg_rc = libxl__set_domain_configuration (gc, dcs->guest_domid, d_config_saved); if (!rc) rc = cfg_rc; libxl__unlock_domain_userdata(lock); } } libxl_domain_config_dispose(d_config_saved); if (!retain_domain) { if (dcs->guest_domid > 0) { dcs->dds.ao = ao; dcs->dds.domid = dcs->guest_domid; dcs->dds.callback = domcreate_destruction_cb; libxl__domain_destroy(egc, &dcs->dds); return; } dcs->guest_domid = -1; } dcs->callback(egc, dcs, rc, dcs->guest_domid); } static void domcreate_destruction_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, int rc) { STATE_AO_GC(dds->ao); libxl__domain_create_state *dcs = CONTAINER_OF(dds, *dcs, dds); if (rc) LOGD(ERROR, dds->domid, "unable to destroy domain following failed creation"); dcs->callback(egc, dcs, ERROR_FAIL, dcs->guest_domid); } /*----- application-facing domain creation interface -----*/ typedef struct { libxl__domain_create_state dcs; uint32_t *domid_out; } libxl__app_domain_create_state; typedef struct { libxl__app_domain_create_state cdcs; libxl__domain_destroy_state dds; libxl__domain_save_state dss; char *toolstack_buf; uint32_t toolstack_len; } libxl__domain_soft_reset_state; static void domain_create_cb(libxl__egc *egc, libxl__domain_create_state *dcs, int rc, uint32_t domid); static int do_domain_create(libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t *domid, int restore_fd, int send_back_fd, const libxl_domain_restore_params *params, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) { AO_CREATE(ctx, 0, ao_how); libxl__app_domain_create_state *cdcs; int rc; GCNEW(cdcs); cdcs->dcs.ao = ao; cdcs->dcs.guest_config = d_config; libxl_domain_config_init(&cdcs->dcs.guest_config_saved); libxl_domain_config_copy(ctx, &cdcs->dcs.guest_config_saved, d_config); cdcs->dcs.restore_fd = cdcs->dcs.libxc_fd = restore_fd; cdcs->dcs.send_back_fd = send_back_fd; if (restore_fd > -1) { cdcs->dcs.restore_params = *params; rc = libxl__fd_flags_modify_save(gc, cdcs->dcs.restore_fd, ~(O_NONBLOCK|O_NDELAY), 0, &cdcs->dcs.restore_fdfl); if (rc < 0) goto out_err; } cdcs->dcs.callback = domain_create_cb; cdcs->dcs.domid_soft_reset = INVALID_DOMID; if (cdcs->dcs.restore_params.checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) { cdcs->dcs.colo_proxy_script = cdcs->dcs.restore_params.colo_proxy_script; cdcs->dcs.crs.cps.is_userspace_proxy = libxl_defbool_val(cdcs->dcs.restore_params.userspace_colo_proxy); } else { cdcs->dcs.colo_proxy_script = NULL; cdcs->dcs.crs.cps.is_userspace_proxy = false; } libxl__ao_progress_gethow(&cdcs->dcs.aop_console_how, aop_console_how); cdcs->domid_out = domid; initiate_domain_create(egc, &cdcs->dcs); return AO_INPROGRESS; out_err: return AO_CREATE_FAIL(rc); } static void domain_soft_reset_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, int rc) { STATE_AO_GC(dds->ao); libxl__domain_soft_reset_state *srs = CONTAINER_OF(dds, *srs, dds); libxl__app_domain_create_state *cdcs = &srs->cdcs; char *savefile, *restorefile; if (rc) { LOGD(ERROR, dds->domid, "destruction of domain failed."); goto error; } cdcs->dcs.guest_domid = dds->domid; rc = libxl__restore_emulator_xenstore_data(&cdcs->dcs, srs->toolstack_buf, srs->toolstack_len); if (rc) { LOGD(ERROR, dds->domid, "failed to restore toolstack record."); goto error; } if (cdcs->dcs.guest_config->b_info.device_model_version != LIBXL_DEVICE_MODEL_VERSION_NONE) { savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", dds->domid); restorefile = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", dds->domid); rc = rename(savefile, restorefile); if (rc) { LOGD(ERROR, dds->domid, "failed to rename dm save file."); goto error; } } initiate_domain_create(egc, &cdcs->dcs); return; error: domcreate_complete(egc, &cdcs->dcs, rc); } static int do_domain_soft_reset(libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t domid_soft_reset, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) { AO_CREATE(ctx, 0, ao_how); libxl__domain_soft_reset_state *srs; libxl__app_domain_create_state *cdcs; libxl__domain_create_state *dcs; libxl__domain_build_state *state; libxl__domain_save_state *dss; const char *console_tty, *xs_store_mfn, *xs_console_mfn; char *dom_path; uint32_t domid_out; int rc; GCNEW(srs); cdcs = &srs->cdcs; dcs = &cdcs->dcs; state = &dcs->build_state; dss = &srs->dss; srs->cdcs.dcs.ao = ao; srs->cdcs.dcs.guest_config = d_config; libxl_domain_config_init(&srs->cdcs.dcs.guest_config_saved); libxl_domain_config_copy(ctx, &srs->cdcs.dcs.guest_config_saved, d_config); cdcs->dcs.restore_fd = -1; cdcs->dcs.domid_soft_reset = domid_soft_reset; cdcs->dcs.callback = domain_create_cb; libxl__ao_progress_gethow(&srs->cdcs.dcs.aop_console_how, aop_console_how); cdcs->domid_out = &domid_out; dom_path = libxl__xs_get_dompath(gc, domid_soft_reset); if (!dom_path) { LOGD(ERROR, domid_soft_reset, "failed to read domain path"); rc = ERROR_FAIL; goto out; } rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/store/ring-ref", dom_path), &xs_store_mfn); if (rc) { LOGD(ERROR, domid_soft_reset, "failed to read store/ring-ref."); goto out; } state->store_mfn = xs_store_mfn ? atol(xs_store_mfn): 0; rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/console/ring-ref", dom_path), &xs_console_mfn); if (rc) { LOGD(ERROR, domid_soft_reset, "failed to read console/ring-ref."); goto out; } state->console_mfn = xs_console_mfn ? atol(xs_console_mfn): 0; rc = libxl__xs_read_mandatory(gc, XBT_NULL, GCSPRINTF("%s/console/tty", dom_path), &console_tty); if (rc) { LOGD(ERROR, domid_soft_reset, "failed to read console/tty."); goto out; } state->console_tty = libxl__strdup(gc, console_tty); dss->ao = ao; dss->domid = dss->dsps.domid = domid_soft_reset; dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", domid_soft_reset); rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf, &srs->toolstack_len); if (rc) { LOGD(ERROR, domid_soft_reset, "failed to save toolstack record."); goto out; } rc = libxl__domain_suspend_device_model(gc, &dss->dsps); if (rc) { LOGD(ERROR, domid_soft_reset, "failed to suspend device model."); goto out; } /* * Ask all backends to disconnect by removing the domain from * xenstore. On the creation path the domain will be introduced to * xenstore again with probably different store/console/... * channels. */ xs_release_domain(ctx->xsh, cdcs->dcs.domid_soft_reset); srs->dds.ao = ao; srs->dds.domid = domid_soft_reset; srs->dds.callback = domain_soft_reset_cb; srs->dds.soft_reset = true; libxl__domain_destroy(egc, &srs->dds); return AO_INPROGRESS; out: return AO_CREATE_FAIL(rc); } static void domain_create_cb(libxl__egc *egc, libxl__domain_create_state *dcs, int rc, uint32_t domid) { libxl__app_domain_create_state *cdcs = CONTAINER_OF(dcs, *cdcs, dcs); int flrc; STATE_AO_GC(cdcs->dcs.ao); *cdcs->domid_out = domid; if (dcs->restore_fd > -1) { flrc = libxl__fd_flags_restore(gc, dcs->restore_fd, dcs->restore_fdfl); /* * If restore has failed already then report that error not * this one. */ if (flrc && !rc) rc = flrc; } libxl__ao_complete(egc, ao, rc); } static void set_disk_colo_restore(libxl_domain_config *d_config) { int i; for (i = 0; i < d_config->num_disks; i++) libxl_defbool_set(&d_config->disks[i].colo_restore_enable, true); } static void unset_disk_colo_restore(libxl_domain_config *d_config) { int i; for (i = 0; i < d_config->num_disks; i++) libxl_defbool_set(&d_config->disks[i].colo_restore_enable, false); } int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t *domid, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) { unset_disk_colo_restore(d_config); return do_domain_create(ctx, d_config, domid, -1, -1, NULL, ao_how, aop_console_how); } int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t *domid, int restore_fd, int send_back_fd, const libxl_domain_restore_params *params, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) { if (params->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) { set_disk_colo_restore(d_config); } else { unset_disk_colo_restore(d_config); } return do_domain_create(ctx, d_config, domid, restore_fd, send_back_fd, params, ao_how, aop_console_how); } int libxl_domain_soft_reset(libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t domid, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) { libxl_domain_build_info *const info = &d_config->b_info; if (info->type != LIBXL_DOMAIN_TYPE_HVM) return ERROR_INVAL; return do_domain_soft_reset(ctx, d_config, domid, ao_how, aop_console_how); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/check-libxl-api-rules0000775000175000017500000000102413256712137016736 0ustar smbsmb#!/usr/bin/perl -w use strict; our $needed=0; our $speclineoffset=0; our $specfile; while (<>) { if (m/^\# (\d+) \"(.*)\"$/) { $speclineoffset = $1 - $. -1; $specfile = $2; } my $file = defined($specfile) ? $specfile : $ARGV; my $line = $speclineoffset + $.; if (m/libxl_asyncop_how[^;]/) { $needed=1; } if (m/LIBXL_EXTERNAL_CALLERS_ONLY/) { $needed=0; } next unless $needed; if (m/\;/) { die "$file:$line:missing LIBXL_EXTERNAL_CALLERS_ONLY"; } } xen-4.9.2/tools/libxl/libxl_test_timedereg.h0000664000175000017500000000031313256712137017273 0ustar smbsmb#ifndef TEST_TIMEDEREG_H #define TEST_TIMEDEREG_H #include int libxl_test_timedereg(libxl_ctx *ctx, libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; #endif /*TEST_TIMEDEREG_H*/ xen-4.9.2/tools/libxl/libxl_colo_proxy.c0000664000175000017500000002554313256712137016473 0ustar smbsmb/* * Copyright (C) 2016 FUJITSU LIMITED * Author: Yang Hongyang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include #include #include #include /* Consistent with the new COLO netlink channel in kernel side */ #define NETLINK_COLO 28 #define COLO_DEFAULT_WAIT_TIME 500000 enum colo_netlink_op { COLO_QUERY_CHECKPOINT = (NLMSG_MIN_TYPE + 1), COLO_CHECKPOINT, COLO_FAILOVER, COLO_PROXY_INIT, COLO_PROXY_RESET, /* UNUSED, will be used for continuous FT */ }; /* ========= colo-proxy: helper functions ========== */ static int colo_proxy_send(libxl__colo_proxy_state *cps, uint8_t *buff, uint64_t size, int type) { struct sockaddr_nl sa; struct nlmsghdr msg; struct iovec iov; struct msghdr mh; int ret; STATE_AO_GC(cps->ao); memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; sa.nl_pid = 0; sa.nl_groups = 0; msg.nlmsg_len = NLMSG_SPACE(0); msg.nlmsg_flags = NLM_F_REQUEST; if (type == COLO_PROXY_INIT) msg.nlmsg_flags |= NLM_F_ACK; msg.nlmsg_seq = 0; msg.nlmsg_pid = cps->index; msg.nlmsg_type = type; iov.iov_base = &msg; iov.iov_len = msg.nlmsg_len; mh.msg_name = &sa; mh.msg_namelen = sizeof(sa); mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = NULL; mh.msg_controllen = 0; mh.msg_flags = 0; ret = sendmsg(cps->sock_fd, &mh, 0); if (ret <= 0) { LOGD(ERROR, ao->domid, "can't send msg to kernel by netlink: %s", strerror(errno)); } return ret; } static int colo_userspace_proxy_send(libxl__colo_proxy_state *cps, uint8_t *buff, uint32_t size) { int ret = 0; uint32_t len = 0; len = htonl(size); ret = send(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0); if (ret != sizeof(len)) { goto err; } ret = send(cps->sock_fd, (uint8_t *)buff, size, 0); if (ret != size) { goto err; } err: return ret; } static int colo_userspace_proxy_recv(libxl__colo_proxy_state *cps, char *buff, unsigned int timeout_us) { struct timeval tv; int ret; uint32_t len = 0; uint32_t size = 0; STATE_AO_GC(cps->ao); if (timeout_us) { tv.tv_sec = timeout_us / 1000000; tv.tv_usec = timeout_us % 1000000; ret = setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); if (ret < 0) { LOGD(ERROR, ao->domid, "colo_userspace_proxy_recv setsockopt error: %s", strerror(errno)); } } ret = recv(cps->sock_fd, (uint8_t *)&len, sizeof(len), 0); if (ret < 0) { goto err; } size = ntohl(len); ret = recv(cps->sock_fd, buff, size, 0); err: return ret; } /* error: return -1, otherwise return 0 */ static int64_t colo_proxy_recv(libxl__colo_proxy_state *cps, uint8_t **buff, unsigned int timeout_us) { struct sockaddr_nl sa; struct iovec iov; struct msghdr mh = { .msg_name = &sa, .msg_namelen = sizeof(sa), .msg_iov = &iov, .msg_iovlen = 1, }; struct timeval tv; uint32_t size = 16384; int64_t len = 0; int ret; STATE_AO_GC(cps->ao); uint8_t *tmp = libxl__malloc(NOGC, size); if (timeout_us) { tv.tv_sec = timeout_us / 1000000; tv.tv_usec = timeout_us % 1000000; setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); } iov.iov_base = tmp; iov.iov_len = size; next: ret = recvmsg(cps->sock_fd, &mh, 0); if (ret <= 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) LOGED(ERROR, ao->domid, "can't recv msg from kernel by netlink"); goto err; } len += ret; if (mh.msg_flags & MSG_TRUNC) { size += 16384; tmp = libxl__realloc(NOGC, tmp, size); iov.iov_base = tmp + len; iov.iov_len = size - len; goto next; } *buff = tmp; ret = len; goto out; err: free(tmp); *buff = NULL; out: if (timeout_us) { tv.tv_sec = 0; tv.tv_usec = 0; setsockopt(cps->sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); } return ret; } /* ========= colo-proxy: setup and teardown ========== */ int colo_proxy_setup(libxl__colo_proxy_state *cps) { int skfd = 0; struct sockaddr_nl sa; struct nlmsghdr *h; int i = 1; int ret = ERROR_FAIL; uint8_t *buff = NULL; int64_t size; STATE_AO_GC(cps->ao); /* If enable userspace proxy mode, we don't need setup kernel proxy */ if (cps->is_userspace_proxy) { struct sockaddr_in addr; int port; char recvbuff[1024]; const char sendbuf[] = "COLO_USERSPACE_PROXY_INIT"; memset(&addr, 0, sizeof(addr)); port = atoi(cps->checkpoint_port); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(cps->checkpoint_host); skfd = socket(AF_INET, SOCK_STREAM, 0); if (skfd < 0) { LOGD(ERROR, ao->domid, "can not create a TCP socket: %s", strerror(errno)); goto out; } cps->sock_fd = skfd; if (connect(skfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { LOGD(ERROR, ao->domid, "connect error"); goto out; } ret = colo_userspace_proxy_send(cps, (uint8_t *)sendbuf, strlen(sendbuf)); if (ret < 0) goto out; ret = colo_userspace_proxy_recv(cps, recvbuff, COLO_DEFAULT_WAIT_TIME); if (ret < 0) { LOGD(ERROR, ao->domid, "Can't recv msg from qemu colo-compare: %s", strerror(errno)); goto out; } return 0; } skfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_COLO); if (skfd < 0) { LOGD(ERROR, ao->domid, "can not create a netlink socket: %s", strerror(errno)); goto out; } cps->sock_fd = skfd; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; sa.nl_groups = 0; retry: sa.nl_pid = i++; if (i > 10) { LOGD(ERROR, ao->domid, "netlink bind error"); goto out; } ret = bind(skfd, (struct sockaddr *)&sa, sizeof(sa)); if (ret < 0 && errno == EADDRINUSE) { LOGD(ERROR, ao->domid, "colo index %d has already in used", sa.nl_pid); goto retry; } else if (ret < 0) { LOGD(ERROR, ao->domid, "netlink bind error"); goto out; } cps->index = sa.nl_pid; ret = colo_proxy_send(cps, NULL, 0, COLO_PROXY_INIT); if (ret < 0) goto out; /* receive ack */ size = colo_proxy_recv(cps, &buff, 500000); if (size < 0) { LOGD(ERROR, ao->domid, "Can't recv msg from kernel by netlink: %s", strerror(errno)); goto out; } if (size) { h = (struct nlmsghdr *)buff; if (h->nlmsg_type == NLMSG_ERROR) { /* ack's type is NLMSG_ERROR */ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); if (size - sizeof(*h) < sizeof(*err)) { LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short"); goto out; } if (err->error) { LOGD(ERROR, ao->domid, "NLMSG_ERROR contains error %d", err->error); goto out; } } } ret = 0; out: free(buff); if (ret) { close(cps->sock_fd); cps->sock_fd = -1; } return ret; } void colo_proxy_teardown(libxl__colo_proxy_state *cps) { /* * If enable userspace proxy mode, * we don't need teardown kernel proxy */ if (cps->is_userspace_proxy) return; if (cps->sock_fd >= 0) { close(cps->sock_fd); cps->sock_fd = -1; } } /* ========= colo-proxy: preresume, postresume and checkpoint ========== */ void colo_proxy_preresume(libxl__colo_proxy_state *cps) { /* * If enable userspace proxy mode, * we don't need preresume kernel proxy */ if (cps->is_userspace_proxy) { const char sendbuf[] = "COLO_CHECKPOINT"; colo_userspace_proxy_send(cps, (uint8_t *)sendbuf, strlen(sendbuf)); return; } colo_proxy_send(cps, NULL, 0, COLO_CHECKPOINT); /* TODO: need to handle if the call fails... */ } void colo_proxy_postresume(libxl__colo_proxy_state *cps) { /* nothing to do... */ } typedef struct colo_msg { bool is_checkpoint; } colo_msg; /* * Return value: * -1: error * 0: no checkpoint event is received before timeout * 1: do checkpoint */ int colo_proxy_checkpoint(libxl__colo_proxy_state *cps, unsigned int timeout_us) { uint8_t *buff = NULL; int64_t size; struct nlmsghdr *h; struct colo_msg *m; int ret = -1; char recvbuff[1024]; STATE_AO_GC(cps->ao); /* * Enable userspace proxy to periodical checkpoint mode, * sleeping temporarily for colo userspace proxy mode. * then we will use socket recv instead of this usleep. * In other words, we use socket communicate with Qemu * Proxy part(colo-compare), for example, notify checkpoint * event. */ if (cps->is_userspace_proxy) { ret = colo_userspace_proxy_recv(cps, recvbuff, timeout_us); if (ret <= 0) { ret = 0; goto out; } if (!strcmp(recvbuff, "DO_CHECKPOINT")) { ret = 1; } else { LOGD(ERROR, ao->domid, "receive qemu colo-compare checkpoint error"); ret = 0; } goto out; } size = colo_proxy_recv(cps, &buff, timeout_us); /* timeout, return no checkpoint message. */ if (size <= 0) { ret = 0; goto out; } h = (struct nlmsghdr *) buff; if (h->nlmsg_type == NLMSG_ERROR) { LOGD(ERROR, ao->domid, "receive NLMSG_ERROR"); goto out; } if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*m))) { LOGD(ERROR, ao->domid, "NLMSG_LENGTH is too short"); goto out; } m = NLMSG_DATA(h); ret = m->is_checkpoint ? 1 : 0; out: free(buff); return ret; } xen-4.9.2/tools/libxl/libxl_libfdt_compat.c0000664000175000017500000000705213256712137017100 0ustar smbsmb/* * Copyright (C) 2006 David Gibson, IBM Corporation. * * This file is part of libxl, and was originally taken from libfdt. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Additionally, this particular file is dual licensed. That is, * alternatively, at your option: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Note that this applies only to this file, and other files with a * similar notice. Also, note that when the same code is distributed * along with the rest of libxl, you must comply with the terms of the * LGPLv2.1 for the whole of libxl including this file. * * The intent is to permit, in particular, upstream libfdt to * incorporate improvements to this file within upstream libfdt. At * the time of writing, upstream libfdt is dual licensed: 2-clause BSD * (as above) and GPLv2-or-later. The 2-clause BSD licence is * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits * copying in both directions, and the optional licence upgrade to a * copyleft licence by libdft upstream or the Xen Project, * respectively. */ #include #include "libxl_libfdt_compat.h" #ifndef HAVE_FDT_FIRST_SUBNODE _hidden int fdt_first_subnode(const void *fdt, int offset) { int depth = 0; offset = fdt_next_node(fdt, offset, &depth); if (offset < 0 || depth != 1) return -FDT_ERR_NOTFOUND; return offset; } #endif #ifndef HAVE_FDT_NEXT_SUBNODE _hidden int fdt_next_subnode(const void *fdt, int offset) { int depth = 1; /* * With respect to the parent, the depth of the next subnode will be * the same as the last. */ do { offset = fdt_next_node(fdt, offset, &depth); if (offset < 0 || depth < 1) return -FDT_ERR_NOTFOUND; } while (depth > 1); return offset; } #endif xen-4.9.2/tools/libxl/libxl_nonetbuffer.c0000664000175000017500000000246313256712137016607 0ustar smbsmb/* * Copyright (C) 2014 * Author Shriram Rajagopalan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" int libxl__netbuffer_enabled(libxl__gc *gc) { return 0; } int init_subkind_nic(libxl__checkpoint_devices_state *cds) { return 0; } void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds) { return; } static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev) { STATE_AO_GC(dev->cds->ao); dev->aodev.rc = ERROR_FAIL; dev->aodev.callback(egc, &dev->aodev); } const libxl__checkpoint_device_instance_ops remus_device_nic = { .kind = LIBXL__DEVICE_KIND_VIF, .setup = nic_setup, }; /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_fork.c0000664000175000017500000004565113256712137015241 0ustar smbsmb/* * Copyright (C) 2012 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * Internal child process machinery for use by other parts of libxl */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" /* * carefd arrangements * * carefd_begin and _unlock take out the no_forking lock, which we * also take and release in our pthread_atfork handlers. So when this * lock is held the whole process cannot fork. We therefore protect * our fds from leaking into children made by other threads. * * We maintain a list of all the carefds, so that if the application * wants to fork a long-running but non-execing child, we can close * them all. * * So the record function sets CLOEXEC for the benefit of execing * children, and makes a note of the fd for the benefit of non-execing * ones. */ struct libxl__carefd { LIBXL_LIST_ENTRY(libxl__carefd) entry; int fd; }; static pthread_mutex_t no_forking = PTHREAD_MUTEX_INITIALIZER; static int atfork_registered; static LIBXL_LIST_HEAD(, libxl__carefd) carefds = LIBXL_LIST_HEAD_INITIALIZER(carefds); /* Protected against concurrency by no_forking. sigchld_users is * protected against being interrupted by SIGCHLD (and thus read * asynchronously by the signal handler) by sigchld_defer (see * below). */ static bool sigchld_installed; /* 0 means not */ static pthread_mutex_t sigchld_defer_mutex = PTHREAD_MUTEX_INITIALIZER; static LIBXL_LIST_HEAD(, libxl_ctx) sigchld_users = LIBXL_LIST_HEAD_INITIALIZER(sigchld_users); static struct sigaction sigchld_saved_action; static void sigchld_removehandler_core(void); /* idempotent */ static void sigchld_user_remove(libxl_ctx *ctx); /* idempotent */ static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old); static void defer_sigchld(void); static void release_sigchld(void); static void atfork_lock(void) { int r = pthread_mutex_lock(&no_forking); assert(!r); } static void atfork_unlock(void) { int r = pthread_mutex_unlock(&no_forking); assert(!r); } int libxl__atfork_init(libxl_ctx *ctx) { int r, rc; atfork_lock(); if (atfork_registered) { rc = 0; goto out; } r = pthread_atfork(atfork_lock, atfork_unlock, atfork_unlock); if (r) { assert(r == ENOMEM); libxl__alloc_failed(ctx, __func__, 0,0); } atfork_registered = 1; rc = 0; out: atfork_unlock(); return rc; } void libxl__carefd_begin(void) { atfork_lock(); } void libxl__carefd_unlock(void) { atfork_unlock(); } libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd) { libxl__carefd *cf = 0; libxl_fd_set_cloexec(ctx, fd, 1); cf = libxl__zalloc(&ctx->nogc_gc, sizeof(*cf)); cf->fd = fd; LIBXL_LIST_INSERT_HEAD(&carefds, cf, entry); return cf; } libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd) { libxl__carefd *cf = 0; int saved_errno = errno; if (fd >= 0) cf = libxl__carefd_record(ctx, fd); libxl__carefd_unlock(); errno = saved_errno; return cf; } void libxl_postfork_child_noexec(libxl_ctx *ctx) { /* * Anything running without the no_forking lock (atfork_lock) * might be interrupted by fork. But libxl functions other than * this one are then forbidden to the child. * * Conversely, this function might interrupt any other libxl * operation (even though that other operation has the libxl ctx * lock). We don't take the lock ourselves, since we are running * in the child and if the lock is held the thread that took it no * longer exists. To prevent us being interrupted by another call * to ourselves (whether in another thread or by virtue of another * fork) we take the atfork lock ourselves. */ libxl__carefd *cf, *cf_tmp; int r; atfork_lock(); LIBXL_LIST_FOREACH_SAFE(cf, &carefds, entry, cf_tmp) { if (cf->fd >= 0) { r = close(cf->fd); if (r) LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_WARNING, "failed to close fd=%d" " (perhaps of another libxl ctx)", cf->fd); } free(cf); } LIBXL_LIST_INIT(&carefds); if (sigchld_installed) { /* We are in theory not at risk of concurrent execution of the * SIGCHLD handler, because the application should call * libxl_postfork_child_noexec before the child forks again. * (If the SIGCHLD was in flight in the parent at the time of * the fork, the thread it was delivered on exists only in the * parent so is not our concern.) * * But in case the application violated this rule (and did so * while multithreaded in the child), we use our deferral * machinery. The result is that the SIGCHLD may then be lost * (i.e. signaled to the now-defunct libxl ctx(s)). But at * least we won't execute undefined behaviour (by examining * the list in the signal handler concurrently with clearing * it here), and since we won't actually reap the new children * things will in fact go OK if the application doesn't try to * use SIGCHLD, but instead just waits for the child(ren). */ defer_sigchld(); LIBXL_LIST_INIT(&sigchld_users); /* After this the ->sigchld_user_registered entries in the * now-obsolete contexts may be lies. But that's OK because * no-one will look at them. */ release_sigchld(); sigchld_removehandler_core(); } atfork_unlock(); } int libxl__carefd_close(libxl__carefd *cf) { if (!cf) return 0; atfork_lock(); int r = cf->fd < 0 ? 0 : close(cf->fd); int esave = errno; LIBXL_LIST_REMOVE(cf, entry); atfork_unlock(); free(cf); errno = esave; return r; } int libxl__carefd_fd(const libxl__carefd *cf) { if (!cf) return -1; return cf->fd; } /* * Low-level functions for child process handling, including * the main SIGCHLD handler. */ /* Like waitpid(,,WNOHANG) but handles all errors except ECHILD. */ static pid_t checked_waitpid(libxl__egc *egc, pid_t want, int *status) { for (;;) { pid_t got = waitpid(want, status, WNOHANG); if (got != -1) return got; if (errno == ECHILD) return got; if (errno == EINTR) continue; LIBXL__EVENT_DISASTER(egc, "waitpid() failed", errno, 0); return 0; } } static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents); static void sigchld_handler(int signo) { /* This function has to be reentrant! Luckily it is. */ libxl_ctx *notify; int esave = errno; int r = pthread_mutex_lock(&sigchld_defer_mutex); assert(!r); LIBXL_LIST_FOREACH(notify, &sigchld_users, sigchld_users_entry) { int e = libxl__self_pipe_wakeup(notify->sigchld_selfpipe[1]); if (e) abort(); /* errors are probably EBADF, very bad */ } r = pthread_mutex_unlock(&sigchld_defer_mutex); assert(!r); errno = esave; } static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old) { struct sigaction ours; int r; memset(&ours,0,sizeof(ours)); ours.sa_handler = handler; sigemptyset(&ours.sa_mask); ours.sa_flags = SA_NOCLDSTOP | SA_RESTART; r = sigaction(SIGCHLD, &ours, old); assert(!r); } /* * SIGCHLD deferral * * sigchld_defer and sigchld_release are a bit like using sigprocmask * to block the signal only they work for the whole process. Sadly * this has to be done by setting a special handler that records the * "pendingness" of the signal here in the program. How tedious. * * A property of this approach is that the signal handler itself * must be reentrant (see the comment in release_sigchld). * * Callers have the atfork_lock so there is no risk of concurrency * within these functions, aside from the risk of being interrupted by * the signal. We use sigchld_defer_mutex to guard against the * possibility of the real signal handler being still running on * another thread. */ static volatile sig_atomic_t sigchld_occurred_while_deferred; static void sigchld_handler_when_deferred(int signo) { sigchld_occurred_while_deferred = 1; } static void defer_sigchld(void) { assert(sigchld_installed); sigchld_sethandler_raw(sigchld_handler_when_deferred, 0); /* Now _this thread_ cannot any longer be interrupted by the * signal, so we can take the mutex without risk of deadlock. If * another thread is in the signal handler, either it or we will * block and wait for the other. */ int r = pthread_mutex_lock(&sigchld_defer_mutex); assert(!r); } static void release_sigchld(void) { assert(sigchld_installed); int r = pthread_mutex_unlock(&sigchld_defer_mutex); assert(!r); sigchld_sethandler_raw(sigchld_handler, 0); if (sigchld_occurred_while_deferred) { sigchld_occurred_while_deferred = 0; /* We might get another SIGCHLD here, in which case * sigchld_handler will be interrupted and re-entered. * This is OK. */ sigchld_handler(SIGCHLD); } } /* * Meat of the child process handling. */ static void sigchld_removehandler_core(void) /* idempotent */ { struct sigaction was; int r; if (!sigchld_installed) return; r = sigaction(SIGCHLD, &sigchld_saved_action, &was); assert(!r); assert(!(was.sa_flags & SA_SIGINFO)); assert(was.sa_handler == sigchld_handler); sigchld_installed = 0; } static void sigchld_installhandler_core(void) /* idempotent */ { if (sigchld_installed) return; sigchld_installed = 1; sigchld_sethandler_raw(sigchld_handler, &sigchld_saved_action); assert(((void)"application must negotiate with libxl about SIGCHLD", !(sigchld_saved_action.sa_flags & SA_SIGINFO) && (sigchld_saved_action.sa_handler == SIG_DFL || sigchld_saved_action.sa_handler == SIG_IGN))); } static void sigchld_user_remove(libxl_ctx *ctx) /* idempotent */ { if (!ctx->sigchld_user_registered) return; atfork_lock(); defer_sigchld(); LIBXL_LIST_REMOVE(ctx, sigchld_users_entry); release_sigchld(); if (LIBXL_LIST_EMPTY(&sigchld_users)) sigchld_removehandler_core(); atfork_unlock(); ctx->sigchld_user_registered = 0; } void libxl__sigchld_notneeded(libxl__gc *gc) /* non-reentrant, idempotent */ { sigchld_user_remove(CTX); libxl__ev_fd_deregister(gc, &CTX->sigchld_selfpipe_efd); } int libxl__sigchld_needed(libxl__gc *gc) /* non-reentrant, idempotent */ { int rc; if (CTX->sigchld_selfpipe[0] < 0) { rc = libxl__pipe_nonblock(CTX, CTX->sigchld_selfpipe); if (rc) goto out; } if (!libxl__ev_fd_isregistered(&CTX->sigchld_selfpipe_efd)) { rc = libxl__ev_fd_register(gc, &CTX->sigchld_selfpipe_efd, sigchld_selfpipe_handler, CTX->sigchld_selfpipe[0], POLLIN); if (rc) goto out; } else { rc = libxl__ev_fd_modify(gc, &CTX->sigchld_selfpipe_efd, POLLIN); if (rc) goto out; } if (!CTX->sigchld_user_registered) { atfork_lock(); sigchld_installhandler_core(); defer_sigchld(); LIBXL_LIST_INSERT_HEAD(&sigchld_users, CTX, sigchld_users_entry); release_sigchld(); atfork_unlock(); CTX->sigchld_user_registered = 1; } rc = 0; out: return rc; } static bool chldmode_ours(libxl_ctx *ctx, bool creating) { switch (ctx->childproc_hooks->chldowner) { case libxl_sigchld_owner_libxl: return creating || !LIBXL_LIST_EMPTY(&ctx->children); case libxl_sigchld_owner_mainloop: return 0; case libxl_sigchld_owner_libxl_always: case libxl_sigchld_owner_libxl_always_selective_reap: return 1; } abort(); } static void perhaps_sigchld_notneeded(libxl__gc *gc) { if (!chldmode_ours(CTX, 0)) libxl__sigchld_notneeded(gc); } static int perhaps_sigchld_needed(libxl__gc *gc, bool creating) { int rc; if (chldmode_ours(CTX, creating)) { rc = libxl__sigchld_needed(gc); if (rc) return rc; } return 0; } static void childproc_reaped_ours(libxl__egc *egc, libxl__ev_child *ch, int status) { pid_t pid = ch->pid; LIBXL_LIST_REMOVE(ch, entry); ch->pid = -1; ch->callback(egc, ch, pid, status); } static int childproc_reaped(libxl__egc *egc, pid_t pid, int status) { EGC_GC; libxl__ev_child *ch; LIBXL_LIST_FOREACH(ch, &CTX->children, entry) if (ch->pid == pid) goto found; /* not found */ return ERROR_UNKNOWN_CHILD; found: childproc_reaped_ours(egc, ch, status); perhaps_sigchld_notneeded(gc); return 0; } int libxl_childproc_reaped(libxl_ctx *ctx, pid_t pid, int status) { EGC_INIT(ctx); CTX_LOCK; assert(CTX->childproc_hooks->chldowner == libxl_sigchld_owner_mainloop); int rc = childproc_reaped(egc, pid, status); CTX_UNLOCK; EGC_FREE; return rc; } static void childproc_checkall(libxl__egc *egc) { EGC_GC; libxl__ev_child *ch; for (;;) { int status; pid_t got; LIBXL_LIST_FOREACH(ch, &CTX->children, entry) { got = checked_waitpid(egc, ch->pid, &status); if (got) goto found; } /* not found */ return; found: if (got == -1) { LIBXL__EVENT_DISASTER (egc, "waitpid() gave ECHILD but we have a child", ECHILD, 0); /* it must have finished but we don't know its status */ status = 255<<8; /* no wait.h macro for this! */ assert(WIFEXITED(status)); assert(WEXITSTATUS(status)==255); assert(!WIFSIGNALED(status)); assert(!WIFSTOPPED(status)); } childproc_reaped_ours(egc, ch, status); /* we need to restart the loop, as children may have been edited */ } } void libxl_childproc_sigchld_occurred(libxl_ctx *ctx) { EGC_INIT(ctx); CTX_LOCK; assert(CTX->childproc_hooks->chldowner == libxl_sigchld_owner_mainloop); childproc_checkall(egc); CTX_UNLOCK; EGC_FREE; } static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { /* May make callbacks into the application for child processes. * So, this function may unlock and relock the CTX. This is OK * because event callback functions are always called with the CTX * locked exactly once, and from code which copes with reentrancy. * (See also the comment in afterpoll_internal.) */ EGC_GC; int selfpipe = CTX->sigchld_selfpipe[0]; if (revents & ~POLLIN) { LOG(ERROR, "unexpected poll event 0x%x on SIGCHLD self pipe", revents); LIBXL__EVENT_DISASTER(egc, "unexpected poll event on SIGCHLD self pipe", 0, 0); } assert(revents & POLLIN); int e = libxl__self_pipe_eatall(selfpipe); if (e) LIBXL__EVENT_DISASTER(egc, "read sigchld pipe", e, 0); if (CTX->childproc_hooks->chldowner == libxl_sigchld_owner_libxl_always_selective_reap) { childproc_checkall(egc); return; } while (chldmode_ours(CTX, 0) /* in case the app changes the mode */) { int status; pid_t pid = checked_waitpid(egc, -1, &status); if (pid == 0 || pid == -1 /* ECHILD */) return; int rc = childproc_reaped(egc, pid, status); if (rc) { if (CTX->childproc_hooks->reaped_callback) { CTX_UNLOCK; rc = CTX->childproc_hooks->reaped_callback (pid, status, CTX->childproc_user); CTX_LOCK; if (rc != 0 && rc != ERROR_UNKNOWN_CHILD) { char disasterbuf[200]; snprintf(disasterbuf, sizeof(disasterbuf), " reported by" " libxl_childproc_hooks->reaped_callback" " (for pid=%lu, status=%d; error code %d)", (unsigned long)pid, status, rc); LIBXL__EVENT_DISASTER(egc, disasterbuf, 0, 0); return; } } else { rc = ERROR_UNKNOWN_CHILD; } if (rc) libxl_report_child_exitstatus(CTX, XTL_WARN, "unknown child", (long)pid, status); } } } pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *ch, libxl__ev_child_callback *death) { CTX_LOCK; int rc; perhaps_sigchld_needed(gc, 1); pid_t pid = CTX->childproc_hooks->fork_replacement ? CTX->childproc_hooks->fork_replacement(CTX->childproc_user) : fork(); if (pid == -1) { LOGE(ERROR, "fork failed"); rc = ERROR_FAIL; goto out; } if (!pid) { /* woohoo! */ if (CTX->xsh) { xs_daemon_destroy_postfork(CTX->xsh); CTX->xsh = NULL; /* turns mistakes into crashes */ } /* Yes, CTX is left locked in the child. */ return 0; } ch->pid = pid; ch->callback = death; LIBXL_LIST_INSERT_HEAD(&CTX->children, ch, entry); rc = pid; out: perhaps_sigchld_notneeded(gc); CTX_UNLOCK; return rc; } void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks, void *user) { GC_INIT(ctx); CTX_LOCK; assert(LIBXL_LIST_EMPTY(&CTX->children)); if (!hooks) hooks = &libxl__childproc_default_hooks; ctx->childproc_hooks = hooks; ctx->childproc_user = user; perhaps_sigchld_notneeded(gc); perhaps_sigchld_needed(gc, 0); /* idempotent, ok to ignore errors for now */ CTX_UNLOCK; GC_FREE; } const libxl_childproc_hooks libxl__childproc_default_hooks = { libxl_sigchld_owner_libxl, 0, 0 }; int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what) { int rc; assert(!CTX->xsh); CTX->xsh = xs_daemon_open(); if (!CTX->xsh) { LOGE(ERROR, "%s: xenstore reopen failed", what); rc = ERROR_FAIL; goto out; } libxl_fd_set_cloexec(CTX, xs_fileno(CTX->xsh), 1); return 0; out: return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_colo_nic.c0000664000175000017500000002153113256712137016054 0ustar smbsmb/* * Copyright (C) 2016 FUJITSU LIMITED * Author: Wen Congyang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" enum { primary, secondary, }; /* ========== init() and cleanup() ========== */ int init_subkind_colo_nic(libxl__checkpoint_devices_state *cds) { return 0; } void cleanup_subkind_colo_nic(libxl__checkpoint_devices_state *cds) { } /* ========== helper functions ========== */ static void colo_save_setup_script_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status); static void colo_save_teardown_script_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status); /* * If the device has a vifname, then use that instead of * the vifX.Y format. * it must ONLY be used for remus because if driver domains * were in use it would constitute a security vulnerability. */ static const char *get_vifname(libxl__checkpoint_device *dev, const libxl_device_nic *nic) { const char *vifname = NULL; const char *path; int rc; STATE_AO_GC(dev->cds->ao); /* Convenience aliases */ const uint32_t domid = dev->cds->domid; path = GCSPRINTF("%s/backend/vif/%d/%d/vifname", libxl__xs_get_dompath(gc, 0), domid, nic->devid); rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname); if (!rc && !vifname) { vifname = libxl__device_nic_devname(gc, domid, nic->devid, nic->nictype); } return vifname; } /* * the script needs the following env & args * $vifname * $forwarddev * $mode(primary/secondary) * $index * $bridge * setup/teardown as command line arg. */ static void setup_async_exec(libxl__checkpoint_device *dev, char *op, libxl__colo_proxy_state *cps, int side, char *colo_proxy_script) { int arraysize, nr = 0; char **env = NULL, **args = NULL; libxl__colo_device_nic *colo_nic = dev->concrete_data; libxl__checkpoint_devices_state *cds = dev->cds; libxl__async_exec_state *aes = &dev->aodev.aes; const libxl_device_nic *nic = dev->backend_dev; STATE_AO_GC(cds->ao); /* Convenience aliases */ const char *const vif = colo_nic->vif; arraysize = 11; GCNEW_ARRAY(env, arraysize); env[nr++] = "vifname"; env[nr++] = libxl__strdup(gc, vif); env[nr++] = "forwarddev"; env[nr++] = libxl__strdup(gc, nic->coloft_forwarddev); env[nr++] = "mode"; if (side == primary) env[nr++] = "primary"; else env[nr++] = "secondary"; env[nr++] = "index"; env[nr++] = GCSPRINTF("%d", cps->index); env[nr++] = "bridge"; env[nr++] = libxl__strdup(gc, nic->bridge); env[nr++] = NULL; assert(nr == arraysize); arraysize = 3; nr = 0; GCNEW_ARRAY(args, arraysize); args[nr++] = colo_proxy_script; args[nr++] = op; args[nr++] = NULL; assert(nr == arraysize); aes->ao = dev->cds->ao; aes->what = GCSPRINTF("%s %s", args[0], args[1]); aes->env = env; aes->args = args; aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000; aes->stdfds[0] = -1; aes->stdfds[1] = -1; aes->stdfds[2] = -1; if (!strcmp(op, "teardown")) aes->callback = colo_save_teardown_script_cb; else aes->callback = colo_save_setup_script_cb; } /* ========== setup() and teardown() ========== */ static void colo_nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev, libxl__colo_proxy_state *cps, int side, char *colo_proxy_script) { int rc; libxl__colo_device_nic *colo_nic; const libxl_device_nic *nic = dev->backend_dev; STATE_AO_GC(dev->cds->ao); /* * thers's no subkind of nic devices, so nic ops is always matched * with nic devices, we begin to setup the nic device */ dev->matched = 1; if (!nic->coloft_forwarddev) { rc = ERROR_FAIL; goto out; } GCNEW(colo_nic); dev->concrete_data = colo_nic; colo_nic->devid = nic->devid; colo_nic->vif = get_vifname(dev, nic); if (!colo_nic->vif) { rc = ERROR_FAIL; goto out; } setup_async_exec(dev, "setup", cps, side, colo_proxy_script); rc = libxl__async_exec_start(&dev->aodev.aes); if (rc) goto out; return; out: dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } static void colo_save_setup_script_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status) { libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes); libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev); libxl__colo_device_nic *colo_nic = dev->concrete_data; libxl__checkpoint_devices_state *cds = dev->cds; const char *out_path_base, *hotplug_error = NULL; EGC_GC; /* Convenience aliases */ const uint32_t domid = cds->domid; const int devid = colo_nic->devid; const char *const vif = colo_nic->vif; if (status && !rc) rc = ERROR_FAIL; if (rc) goto out; out_path_base = GCSPRINTF("%s/colo_proxy/%d", libxl__xs_libxl_path(gc, domid), devid); rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/hotplug-error", out_path_base), &hotplug_error); if (rc) goto out; if (hotplug_error) { LOGD(ERROR, domid, "colo_proxy script %s setup failed for vif %s: %s", aes->args[0], vif, hotplug_error); rc = ERROR_FAIL; goto out; } rc = 0; out: aodev->rc = rc; aodev->callback(egc, aodev); } static void colo_nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev, libxl__colo_proxy_state *cps, int side, char *colo_proxy_script) { int rc; libxl__colo_device_nic *colo_nic = dev->concrete_data; if (!colo_nic || !colo_nic->vif) { /* colo nic has not yet been set up, just return */ rc = 0; goto out; } setup_async_exec(dev, "teardown", cps, side, colo_proxy_script); rc = libxl__async_exec_start(&dev->aodev.aes); if (rc) goto out; return; out: dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } static void colo_save_teardown_script_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status) { libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes); if (status && !rc) rc = ERROR_FAIL; else rc = 0; aodev->rc = rc; aodev->callback(egc, aodev); } /* ======== primary ======== */ static void colo_nic_save_setup(libxl__egc *egc, libxl__checkpoint_device *dev) { libxl__colo_save_state *css = dev->cds->concrete_data; colo_nic_setup(egc, dev, &css->cps, primary, css->colo_proxy_script); } static void colo_nic_save_teardown(libxl__egc *egc, libxl__checkpoint_device *dev) { libxl__colo_save_state *css = dev->cds->concrete_data; colo_nic_teardown(egc, dev, &css->cps, primary, css->colo_proxy_script); } const libxl__checkpoint_device_instance_ops colo_save_device_nic = { .kind = LIBXL__DEVICE_KIND_VIF, .setup = colo_nic_save_setup, .teardown = colo_nic_save_teardown, }; /* ======== secondary ======== */ static void colo_nic_restore_setup(libxl__egc *egc, libxl__checkpoint_device *dev) { libxl__colo_restore_state *crs = dev->cds->concrete_data; colo_nic_setup(egc, dev, &crs->cps, secondary, crs->colo_proxy_script); } static void colo_nic_restore_teardown(libxl__egc *egc, libxl__checkpoint_device *dev) { libxl__colo_restore_state *crs = dev->cds->concrete_data; colo_nic_teardown(egc, dev, &crs->cps, secondary, crs->colo_proxy_script); } const libxl__checkpoint_device_instance_ops colo_restore_device_nic = { .kind = LIBXL__DEVICE_KIND_VIF, .setup = colo_nic_restore_setup, .teardown = colo_nic_restore_teardown, }; xen-4.9.2/tools/libxl/libxl_save_callout.c0000664000175000017500000003261013256712137016750 0ustar smbsmb/* * Copyright (C) 2012 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" /* stream_fd is as from the caller (eventually, the application). * It may be 0, 1 or 2, in which case we need to dup it elsewhere. * The actual fd value is not included in the supplied argnums; rather * it will be automatically supplied by run_helper as the 2nd argument. * * preserve_fds are fds that the caller is intending to pass to the * helper so which need cloexec clearing. They may not be 0, 1 or 2. * An entry may be -1 in which case it will be ignored. */ static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs, const char *mode_arg, int stream_fd, int back_channel_fd, const int *preserve_fds, int num_preserve_fds, const unsigned long *argnums, int num_argnums); static void helper_failed(libxl__egc*, libxl__save_helper_state *shs, int rc); static void helper_stop(libxl__egc *egc, libxl__ao_abortable*, int rc); static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents); static void helper_exited(libxl__egc *egc, libxl__ev_child *ch, pid_t pid, int status); static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs); /*----- entrypoints -----*/ void libxl__xc_domain_restore(libxl__egc *egc, libxl__domain_create_state *dcs, libxl__save_helper_state *shs, int hvm, int pae, int superpages) { STATE_AO_GC(dcs->ao); /* Convenience aliases */ const uint32_t domid = dcs->guest_domid; const int restore_fd = dcs->libxc_fd; const int send_back_fd = dcs->send_back_fd; libxl__domain_build_state *const state = &dcs->build_state; unsigned cbflags = libxl__srm_callout_enumcallbacks_restore(&shs->callbacks.restore.a); const unsigned long argnums[] = { domid, state->store_port, state->store_domid, state->console_port, state->console_domid, hvm, pae, superpages, cbflags, dcs->restore_params.checkpointed_stream, }; shs->ao = ao; shs->domid = domid; shs->recv_callback = libxl__srm_callout_received_restore; if (dcs->restore_params.checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) shs->completion_callback = libxl__colo_restore_teardown; else shs->completion_callback = libxl__xc_domain_restore_done; shs->caller_state = dcs; shs->need_results = 1; run_helper(egc, shs, "--restore-domain", restore_fd, send_back_fd, 0, 0, argnums, ARRAY_SIZE(argnums)); } void libxl__xc_domain_save(libxl__egc *egc, libxl__domain_save_state *dss, libxl__save_helper_state *shs) { STATE_AO_GC(dss->ao); unsigned cbflags = libxl__srm_callout_enumcallbacks_save(&shs->callbacks.save.a); const unsigned long argnums[] = { dss->domid, 0, 0, dss->xcflags, dss->hvm, cbflags, dss->checkpointed_stream, }; shs->ao = ao; shs->domid = dss->domid; shs->recv_callback = libxl__srm_callout_received_save; shs->completion_callback = libxl__xc_domain_save_done; shs->caller_state = dss; shs->need_results = 0; run_helper(egc, shs, "--save-domain", dss->fd, dss->recv_fd, NULL, 0, argnums, ARRAY_SIZE(argnums)); return; } void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc, libxl__save_helper_state *shs, int return_value) { shs->egc = egc; libxl__srm_callout_sendreply(return_value, shs); shs->egc = 0; } void libxl__save_helper_init(libxl__save_helper_state *shs) { libxl__ao_abortable_init(&shs->abrt); libxl__ev_fd_init(&shs->readable); libxl__ev_child_init(&shs->child); } /*----- helper execution -----*/ /* This function can not fail. */ static int dup_cloexec(libxl__gc *gc, int fd, const char *what) { int dup_fd = fd; if (fd <= 2) { dup_fd = dup(fd); if (dup_fd < 0) { LOGE(ERROR,"dup %s", what); exit(-1); } } libxl_fd_set_cloexec(CTX, dup_fd, 0); return dup_fd; } /* * Both save and restore share four parameters: * 1) Path to libxl-save-helper. * 2) --[restore|save]-domain. * 3) stream file descriptor. * 4) back channel file descriptor. * n) save/restore specific parameters. * 5) A \0 at the end. */ #define HELPER_NR_ARGS 5 static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs, const char *mode_arg, int stream_fd, int back_channel_fd, const int *preserve_fds, int num_preserve_fds, const unsigned long *argnums, int num_argnums) { STATE_AO_GC(shs->ao); const char *args[HELPER_NR_ARGS + num_argnums]; const char **arg = args; int i, rc; /* Resources we must free */ libxl__carefd *childs_pipes[2] = { 0,0 }; /* Convenience aliases */ const uint32_t domid = shs->domid; shs->rc = 0; shs->completed = 0; shs->pipes[0] = shs->pipes[1] = 0; libxl__save_helper_init(shs); shs->abrt.ao = shs->ao; shs->abrt.callback = helper_stop; rc = libxl__ao_abortable_register(&shs->abrt); if (rc) goto out; shs->stdin_what = GCSPRINTF("domain %"PRIu32" save/restore helper" " stdin pipe", domid); shs->stdout_what = GCSPRINTF("domain %"PRIu32" save/restore helper" " stdout pipe", domid); *arg++ = getenv("LIBXL_SAVE_HELPER") ?: LIBEXEC_BIN "/" "libxl-save-helper"; *arg++ = mode_arg; const char **stream_fd_arg = arg++; const char **back_channel_fd_arg = arg++; for (i=0; ipipes[childfd] = libxl__carefd_record(CTX, fds[our_end]); } libxl__carefd_unlock(); pid_t pid = libxl__ev_child_fork(gc, &shs->child, helper_exited); if (!pid) { stream_fd = dup_cloexec(gc, stream_fd, "migration stream fd"); *stream_fd_arg = GCSPRINTF("%d", stream_fd); if (back_channel_fd >= 0) back_channel_fd = dup_cloexec(gc, back_channel_fd, "migration back channel fd"); *back_channel_fd_arg = GCSPRINTF("%d", back_channel_fd); for (i=0; i= 0) { assert(preserve_fds[i] > 2); libxl_fd_set_cloexec(CTX, preserve_fds[i], 0); } libxl__exec(gc, libxl__carefd_fd(childs_pipes[0]), libxl__carefd_fd(childs_pipes[1]), -1, args[0], (char**)args, 0); } libxl__carefd_close(childs_pipes[0]); libxl__carefd_close(childs_pipes[1]); rc = libxl__ev_fd_register(gc, &shs->readable, helper_stdout_readable, libxl__carefd_fd(shs->pipes[1]), POLLIN|POLLPRI); if (rc) goto out; return; out: libxl__carefd_close(childs_pipes[0]); libxl__carefd_close(childs_pipes[1]); helper_failed(egc, shs, rc);; } static void helper_failed(libxl__egc *egc, libxl__save_helper_state *shs, int rc) { STATE_AO_GC(shs->ao); if (!shs->rc) shs->rc = rc; libxl__ev_fd_deregister(gc, &shs->readable); if (!libxl__save_helper_inuse(shs)) { helper_done(egc, shs); return; } libxl__kill(gc, shs->child.pid, SIGKILL, "save/restore helper"); } static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc) { libxl__save_helper_state *shs = CONTAINER_OF(abrt, *shs, abrt); STATE_AO_GC(shs->ao); if (!libxl__save_helper_inuse(shs)) { helper_failed(egc, shs, rc); return; } if (!shs->rc) shs->rc = rc; libxl__kill(gc, shs->child.pid, SIGTERM, "save/restore helper"); } void libxl__save_helper_abort(libxl__egc *egc, libxl__save_helper_state *shs) { helper_stop(egc, &shs->abrt, ERROR_FAIL); } static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { libxl__save_helper_state *shs = CONTAINER_OF(ev, *shs, readable); STATE_AO_GC(shs->ao); int rc, errnoval; if (revents & (POLLERR|POLLPRI)) { LOGD(ERROR, shs->domid, "%s signaled POLLERR|POLLPRI (%#x)", shs->stdout_what, revents); rc = ERROR_FAIL; out: /* this is here because otherwise we bypass the decl of msg[] */ helper_failed(egc, shs, rc); return; } uint16_t msglen; errnoval = libxl_read_exactly(CTX, fd, &msglen, sizeof(msglen), shs->stdout_what, "ipc msg header"); if (errnoval) { rc = ERROR_FAIL; goto out; } unsigned char msg[msglen]; errnoval = libxl_read_exactly(CTX, fd, msg, msglen, shs->stdout_what, "ipc msg body"); if (errnoval) { rc = ERROR_FAIL; goto out; } shs->egc = egc; shs->recv_callback(msg, msglen, shs); shs->egc = 0; return; } static void helper_exited(libxl__egc *egc, libxl__ev_child *ch, pid_t pid, int status) { libxl__save_helper_state *shs = CONTAINER_OF(ch, *shs, child); STATE_AO_GC(shs->ao); /* Convenience aliases */ const uint32_t domid = shs->domid; const char *what = GCSPRINTF("domain %"PRIu32" save/restore helper", domid); if (status) { libxl_report_child_exitstatus(CTX, XTL_ERROR, what, pid, status); if (!shs->rc) shs->rc = ERROR_FAIL; } if (shs->need_results) { if (!shs->rc) { LOGD(ERROR,shs->domid,"%s exited without providing results",what); shs->rc = ERROR_FAIL; } } if (!shs->completed) { if (!shs->rc) { LOGD(ERROR,shs->domid,"%s exited without signaling completion",what); shs->rc = ERROR_FAIL; } } helper_done(egc, shs); return; } static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs) { STATE_AO_GC(shs->ao); libxl__ao_abortable_deregister(&shs->abrt); libxl__ev_fd_deregister(gc, &shs->readable); libxl__carefd_close(shs->pipes[0]); shs->pipes[0] = 0; libxl__carefd_close(shs->pipes[1]); shs->pipes[1] = 0; assert(!libxl__save_helper_inuse(shs)); shs->egc = egc; shs->completion_callback(egc, shs->caller_state, shs->rc, shs->retval, shs->errnoval); shs->egc = 0; } /*----- generic helpers for the autogenerated code -----*/ const libxl__srm_save_autogen_callbacks* libxl__srm_callout_get_callbacks_save(void *user) { libxl__save_helper_state *shs = user; return &shs->callbacks.save.a; } const libxl__srm_restore_autogen_callbacks* libxl__srm_callout_get_callbacks_restore(void *user) { libxl__save_helper_state *shs = user; return &shs->callbacks.restore.a; } void libxl__srm_callout_sendreply(int r, void *user) { libxl__save_helper_state *shs = user; libxl__egc *egc = shs->egc; STATE_AO_GC(shs->ao); int errnoval; errnoval = libxl_write_exactly(CTX, libxl__carefd_fd(shs->pipes[0]), &r, sizeof(r), shs->stdin_what, "callback return value"); if (errnoval) helper_failed(egc, shs, ERROR_FAIL); } void libxl__srm_callout_callback_log(uint32_t level, uint32_t errnoval, const char *context, const char *formatted, void *user) { libxl__save_helper_state *shs = user; STATE_AO_GC(shs->ao); xtl_log(CTX->lg, level, errnoval, context, "%s", formatted); } void libxl__srm_callout_callback_progress(const char *context, const char *doing_what, unsigned long done, unsigned long total, void *user) { libxl__save_helper_state *shs = user; STATE_AO_GC(shs->ao); xtl_progress(CTX->lg, context, doing_what, done, total); } int libxl__srm_callout_callback_complete(int retval, int errnoval, void *user) { libxl__save_helper_state *shs = user; STATE_AO_GC(shs->ao); shs->completed = 1; shs->retval = retval; shs->errnoval = errnoval; libxl__ev_fd_deregister(gc, &shs->readable); return 0; } xen-4.9.2/tools/libxl/libxl_pci.c0000664000175000017500000015625313256712137015054 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #define PCI_BDF "%04x:%02x:%02x.%01x" #define PCI_BDF_SHORT "%02x:%02x.%01x" #define PCI_BDF_VDEVFN "%04x:%02x:%02x.%01x@%02x" #define PCI_OPTIONS "msitranslate=%d,power_mgmt=%d" #define PCI_BDF_XSPATH "%04x-%02x-%02x-%01x" static unsigned int pcidev_encode_bdf(libxl_device_pci *pcidev) { unsigned int value; value = pcidev->domain << 16; value |= (pcidev->bus & 0xff) << 8; value |= (pcidev->dev & 0x1f) << 3; value |= (pcidev->func & 0x7); return value; } static void pcidev_struct_fill(libxl_device_pci *pcidev, unsigned int domain, unsigned int bus, unsigned int dev, unsigned int func, unsigned int vdevfn) { pcidev->domain = domain; pcidev->bus = bus; pcidev->dev = dev; pcidev->func = func; pcidev->vdevfn = vdevfn; } static void libxl_create_pci_backend_device(libxl__gc *gc, flexarray_t *back, int num, libxl_device_pci *pcidev) { flexarray_append(back, GCSPRINTF("key-%d", num)); flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func)); flexarray_append(back, GCSPRINTF("dev-%d", num)); flexarray_append(back, GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func)); if (pcidev->vdevfn) flexarray_append_pair(back, GCSPRINTF("vdevfn-%d", num), GCSPRINTF("%x", pcidev->vdevfn)); flexarray_append(back, GCSPRINTF("opts-%d", num)); flexarray_append(back, GCSPRINTF("msitranslate=%d,power_mgmt=%d,permissive=%d", pcidev->msitranslate, pcidev->power_mgmt, pcidev->permissive)); flexarray_append_pair(back, GCSPRINTF("state-%d", num), GCSPRINTF("%d", XenbusStateInitialising)); } static void libxl__device_from_pcidev(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, libxl__device *device) { device->backend_devid = 0; device->backend_domid = 0; device->backend_kind = LIBXL__DEVICE_KIND_PCI; device->devid = 0; device->domid = domid; device->kind = LIBXL__DEVICE_KIND_PCI; } int libxl__create_pci_backend(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int num) { flexarray_t *front = NULL; flexarray_t *back = NULL; libxl__device device; int i; front = flexarray_make(gc, 16, 1); back = flexarray_make(gc, 16, 1); LOGD(DEBUG, domid, "Creating pci backend"); /* add pci device */ libxl__device_from_pcidev(gc, domid, pcidev, &device); flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid)); flexarray_append_pair(back, "online", "1"); flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append_pair(back, "domain", libxl__domid_to_name(gc, domid)); for (i = 0; i < num; i++, pcidev++) libxl_create_pci_backend_device(gc, back, i, pcidev); flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num)); flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", 0)); flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising)); return libxl__device_generic_add(gc, XBT_NULL, &device, libxl__xs_kvs_of_flexarray(gc, back), libxl__xs_kvs_of_flexarray(gc, front), NULL); } static int libxl__device_pci_add_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int starting) { flexarray_t *back; char *num_devs, *be_path; int num = 0; xs_transaction_t t = XBT_NULL; libxl__device *device; int rc; libxl_domain_config d_config; libxl_device_pci pcidev_saved; libxl__domain_userdata_lock *lock = NULL; libxl_domain_config_init(&d_config); libxl_device_pci_init(&pcidev_saved); libxl_device_pci_copy(CTX, &pcidev_saved, pcidev); be_path = GCSPRINTF("%s/backend/pci/%d/0", libxl__xs_get_dompath(gc, 0), domid); num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path)); if (!num_devs) return libxl__create_pci_backend(gc, domid, pcidev, 1); libxl_domain_type domtype = libxl__domain_type(gc, domid); if (domtype == LIBXL_DOMAIN_TYPE_INVALID) return ERROR_FAIL; if (!starting && domtype == LIBXL_DOMAIN_TYPE_PV) { if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) return ERROR_FAIL; } back = flexarray_make(gc, 16, 1); LOGD(DEBUG, domid, "Adding new pci device to xenstore"); num = atoi(num_devs); libxl_create_pci_backend_device(gc, back, num, pcidev); flexarray_append_pair(back, "num_devs", GCSPRINTF("%d", num + 1)); if (!starting) flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateReconfiguring)); GCNEW(device); libxl__device_from_pcidev(gc, domid, pcidev, device); lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc) goto out; DEVICE_ADD(pci, pcidevs, domid, &pcidev_saved, COMPARE_PCI, &d_config); rc = libxl__dm_check_start(gc, &d_config, domid); if (rc) goto out; for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; rc = libxl__set_domain_configuration(gc, domid, &d_config); if (rc) goto out; libxl__xs_writev(gc, t, be_path, libxl__xs_kvs_of_flexarray(gc, back)); rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } out: libxl__xs_transaction_abort(gc, &t); if (lock) libxl__unlock_domain_userdata(lock); libxl_device_pci_dispose(&pcidev_saved); libxl_domain_config_dispose(&d_config); return rc; } static int libxl__device_pci_remove_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev) { libxl_ctx *ctx = libxl__gc_owner(gc); char *be_path, *num_devs_path, *num_devs, *xsdev, *tmp, *tmppath; int num, i, j; xs_transaction_t t; be_path = GCSPRINTF("%s/backend/pci/%d/0", libxl__xs_get_dompath(gc, 0), domid); num_devs_path = GCSPRINTF("%s/num_devs", be_path); num_devs = libxl__xs_read(gc, XBT_NULL, num_devs_path); if (!num_devs) return ERROR_INVAL; num = atoi(num_devs); libxl_domain_type domtype = libxl__domain_type(gc, domid); if (domtype == LIBXL_DOMAIN_TYPE_INVALID) return ERROR_FAIL; if (domtype == LIBXL_DOMAIN_TYPE_PV) { if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) { LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path); return ERROR_FAIL; } } for (i = 0; i < num; i++) { unsigned int domain = 0, bus = 0, dev = 0, func = 0; xsdev = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, i)); sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func); if (domain == pcidev->domain && bus == pcidev->bus && pcidev->dev == dev && pcidev->func == func) { break; } } if (i == num) { LOGD(ERROR, domid, "Couldn't find the device on xenstore"); return ERROR_INVAL; } retry_transaction: t = xs_transaction_start(ctx->xsh); xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i), GCSPRINTF("%d", XenbusStateClosing), 1); xs_write(ctx->xsh, t, GCSPRINTF("%s/state", be_path), GCSPRINTF("%d", XenbusStateReconfiguring), 1); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; if (domtype == LIBXL_DOMAIN_TYPE_PV) { if (libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)) < 0) { LOGD(DEBUG, domid, "pci backend at %s is not ready", be_path); return ERROR_FAIL; } } retry_transaction2: t = xs_transaction_start(ctx->xsh); xs_rm(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, i)); xs_rm(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, i)); xs_rm(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, i)); xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, i)); xs_rm(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, i)); xs_rm(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, i)); libxl__xs_printf(gc, t, num_devs_path, "%d", num - 1); for (j = i + 1; j < num; j++) { tmppath = GCSPRINTF("%s/state-%d", be_path, j); tmp = libxl__xs_read(gc, t, tmppath); xs_write(ctx->xsh, t, GCSPRINTF("%s/state-%d", be_path, j - 1), tmp, strlen(tmp)); xs_rm(ctx->xsh, t, tmppath); tmppath = GCSPRINTF("%s/dev-%d", be_path, j); tmp = libxl__xs_read(gc, t, tmppath); xs_write(ctx->xsh, t, GCSPRINTF("%s/dev-%d", be_path, j - 1), tmp, strlen(tmp)); xs_rm(ctx->xsh, t, tmppath); tmppath = GCSPRINTF("%s/key-%d", be_path, j); tmp = libxl__xs_read(gc, t, tmppath); xs_write(ctx->xsh, t, GCSPRINTF("%s/key-%d", be_path, j - 1), tmp, strlen(tmp)); xs_rm(ctx->xsh, t, tmppath); tmppath = GCSPRINTF("%s/vdev-%d", be_path, j); tmp = libxl__xs_read(gc, t, tmppath); if (tmp) { xs_write(ctx->xsh, t, GCSPRINTF("%s/vdev-%d", be_path, j - 1), tmp, strlen(tmp)); xs_rm(ctx->xsh, t, tmppath); } tmppath = GCSPRINTF("%s/opts-%d", be_path, j); tmp = libxl__xs_read(gc, t, tmppath); if (tmp) { xs_write(ctx->xsh, t, GCSPRINTF("%s/opts-%d", be_path, j - 1), tmp, strlen(tmp)); xs_rm(ctx->xsh, t, tmppath); } tmppath = GCSPRINTF("%s/vdevfn-%d", be_path, j); tmp = libxl__xs_read(gc, t, tmppath); if (tmp) { xs_write(ctx->xsh, t, GCSPRINTF("%s/vdevfn-%d", be_path, j - 1), tmp, strlen(tmp)); xs_rm(ctx->xsh, t, tmppath); } } if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction2; if (num == 1) { libxl__device dev; if (libxl__parse_backend_path(gc, be_path, &dev) != 0) return ERROR_FAIL; dev.domid = domid; dev.kind = LIBXL__DEVICE_KIND_PCI; dev.devid = 0; libxl__device_destroy(gc, &dev); return 0; } return 0; } static int get_all_assigned_devices(libxl__gc *gc, libxl_device_pci **list, int *num) { char **domlist; unsigned int nd = 0, i; *list = NULL; *num = 0; domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &nd); for(i = 0; i < nd; i++) { char *path, *num_devs; path = GCSPRINTF("/local/domain/0/backend/pci/%s/0/num_devs", domlist[i]); num_devs = libxl__xs_read(gc, XBT_NULL, path); if ( num_devs ) { int ndev = atoi(num_devs), j; char *devpath, *bdf; for(j = 0; j < ndev; j++) { devpath = GCSPRINTF("/local/domain/0/backend/pci/%s/0/dev-%u", domlist[i], j); bdf = libxl__xs_read(gc, XBT_NULL, devpath); if ( bdf ) { unsigned dom, bus, dev, func; if ( sscanf(bdf, PCI_BDF, &dom, &bus, &dev, &func) != 4 ) continue; *list = realloc(*list, sizeof(libxl_device_pci) * ((*num) + 1)); if (*list == NULL) return ERROR_NOMEM; pcidev_struct_fill(*list + *num, dom, bus, dev, func, 0); (*num)++; } } } } libxl__ptr_add(gc, *list); return 0; } static int is_pcidev_in_array(libxl_device_pci *assigned, int num_assigned, int dom, int bus, int dev, int func) { int i; for(i = 0; i < num_assigned; i++) { if ( assigned[i].domain != dom ) continue; if ( assigned[i].bus != bus ) continue; if ( assigned[i].dev != dev ) continue; if ( assigned[i].func != func ) continue; return 1; } return 0; } /* Write the standard BDF into the sysfs path given by sysfs_path. */ static int sysfs_write_bdf(libxl__gc *gc, const char * sysfs_path, libxl_device_pci *pcidev) { int rc, fd; char *buf; fd = open(sysfs_path, O_WRONLY); if (fd < 0) { LOGE(ERROR, "Couldn't open %s", sysfs_path); return ERROR_FAIL; } buf = GCSPRINTF(PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); rc = write(fd, buf, strlen(buf)); /* Annoying to have two if's, but we need the errno */ if (rc < 0) LOGE(ERROR, "write to %s returned %d", sysfs_path, rc); close(fd); if (rc < 0) return ERROR_FAIL; return 0; } libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num) { GC_INIT(ctx); libxl_device_pci *pcidevs = NULL, *new, *assigned; struct dirent *de; DIR *dir; int r, num_assigned; *num = 0; r = get_all_assigned_devices(gc, &assigned, &num_assigned); if (r) goto out; dir = opendir(SYSFS_PCIBACK_DRIVER); if (NULL == dir) { if (errno == ENOENT) { LOG(ERROR, "Looks like pciback driver not loaded"); } else { LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER); } goto out; } while((de = readdir(dir))) { unsigned dom, bus, dev, func; if (sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4) continue; if (is_pcidev_in_array(assigned, num_assigned, dom, bus, dev, func)) continue; new = realloc(pcidevs, ((*num) + 1) * sizeof(*new)); if (NULL == new) continue; pcidevs = new; new = pcidevs + *num; memset(new, 0, sizeof(*new)); pcidev_struct_fill(new, dom, bus, dev, func, 0); (*num)++; } closedir(dir); out: GC_FREE; return pcidevs; } /* Unbind device from its current driver, if any. If driver_path is non-NULL, * store the path to the original driver in it. */ static int sysfs_dev_unbind(libxl__gc *gc, libxl_device_pci *pcidev, char **driver_path) { char * spath, *dp = NULL; struct stat st; spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/driver", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); if ( !lstat(spath, &st) ) { /* Find the canonical path to the driver. */ dp = libxl__zalloc(gc, PATH_MAX); dp = realpath(spath, dp); if ( !dp ) { LOGE(ERROR, "realpath() failed"); return -1; } LOG(DEBUG, "Driver re-plug path: %s", dp); /* Unbind from the old driver */ spath = GCSPRINTF("%s/unbind", dp); if ( sysfs_write_bdf(gc, spath, pcidev) < 0 ) { LOGE(ERROR, "Couldn't unbind device"); return -1; } } if ( driver_path ) *driver_path = dp; return 0; } static uint16_t sysfs_dev_get_vendor(libxl__gc *gc, libxl_device_pci *pcidev) { char *pci_device_vendor_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/vendor", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); uint16_t read_items; uint16_t pci_device_vendor; FILE *f = fopen(pci_device_vendor_path, "r"); if (!f) { LOGE(ERROR, "pci device "PCI_BDF" does not have vendor attribute", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); return 0xffff; } read_items = fscanf(f, "0x%hx\n", &pci_device_vendor); fclose(f); if (read_items != 1) { LOGE(ERROR, "cannot read vendor of pci device "PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); return 0xffff; } return pci_device_vendor; } static uint16_t sysfs_dev_get_device(libxl__gc *gc, libxl_device_pci *pcidev) { char *pci_device_device_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/device", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); uint16_t read_items; uint16_t pci_device_device; FILE *f = fopen(pci_device_device_path, "r"); if (!f) { LOGE(ERROR, "pci device "PCI_BDF" does not have device attribute", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); return 0xffff; } read_items = fscanf(f, "0x%hx\n", &pci_device_device); fclose(f); if (read_items != 1) { LOGE(ERROR, "cannot read device of pci device "PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); return 0xffff; } return pci_device_device; } typedef struct { uint16_t vendor; uint16_t device; } pci_info; static const pci_info fixup_ids[] = { /* Intel HSW Classic */ {0x8086, 0x0402}, /* HSWGT1D, HSWD_w7 */ {0x8086, 0x0406}, /* HSWGT1M, HSWM_w7 */ {0x8086, 0x0412}, /* HSWGT2D, HSWD_w7 */ {0x8086, 0x0416}, /* HSWGT2M, HSWM_w7 */ {0x8086, 0x041E}, /* HSWGT15D, HSWD_w7 */ /* Intel HSW ULT */ {0x8086, 0x0A06}, /* HSWGT1UT, HSWM_w7 */ {0x8086, 0x0A16}, /* HSWGT2UT, HSWM_w7 */ {0x8086, 0x0A26}, /* HSWGT3UT, HSWM_w7 */ {0x8086, 0x0A2E}, /* HSWGT3UT28W, HSWM_w7 */ {0x8086, 0x0A1E}, /* HSWGT2UX, HSWM_w7 */ {0x8086, 0x0A0E}, /* HSWGT1ULX, HSWM_w7 */ /* Intel HSW CRW */ {0x8086, 0x0D26}, /* HSWGT3CW, HSWM_w7 */ {0x8086, 0x0D22}, /* HSWGT3CWDT, HSWD_w7 */ /* Intel HSW Server */ {0x8086, 0x041A}, /* HSWSVGT2, HSWD_w7 */ /* Intel HSW SRVR */ {0x8086, 0x040A}, /* HSWSVGT1, HSWD_w7 */ /* Intel BSW */ {0x8086, 0x1606}, /* BDWULTGT1, BDWM_w7 */ {0x8086, 0x1616}, /* BDWULTGT2, BDWM_w7 */ {0x8086, 0x1626}, /* BDWULTGT3, BDWM_w7 */ {0x8086, 0x160E}, /* BDWULXGT1, BDWM_w7 */ {0x8086, 0x161E}, /* BDWULXGT2, BDWM_w7 */ {0x8086, 0x1602}, /* BDWHALOGT1, BDWM_w7 */ {0x8086, 0x1612}, /* BDWHALOGT2, BDWM_w7 */ {0x8086, 0x1622}, /* BDWHALOGT3, BDWM_w7 */ {0x8086, 0x162B}, /* BDWHALO28W, BDWM_w7 */ {0x8086, 0x162A}, /* BDWGT3WRKS, BDWM_w7 */ {0x8086, 0x162D}, /* BDWGT3SRVR, BDWM_w7 */ }; /* * Some devices may need some ways to work well. Here like IGD, * we have to pass a specific option to qemu. */ bool libxl__is_igd_vga_passthru(libxl__gc *gc, const libxl_domain_config *d_config) { unsigned int i, j, num = ARRAY_SIZE(fixup_ids); uint16_t vendor, device, pt_vendor, pt_device; for (i = 0 ; i < d_config->num_pcidevs ; i++) { libxl_device_pci *pcidev = &d_config->pcidevs[i]; pt_vendor = sysfs_dev_get_vendor(gc, pcidev); pt_device = sysfs_dev_get_device(gc, pcidev); if (pt_vendor == 0xffff || pt_device == 0xffff) continue; for (j = 0 ; j < num ; j++) { vendor = fixup_ids[j].vendor; device = fixup_ids[j].device; if (pt_vendor == vendor && pt_device == device) return true; } } return false; } /* * A brief comment about slots. I don't know what slots are for; however, * I have by experimentation determined: * - Before a device can be bound to pciback, its BDF must first be listed * in pciback/slots * - The way to get the BDF listed there is to write BDF to * pciback/new_slot * - Writing the same BDF to pciback/new_slot is not idempotent; it results * in two entries of the BDF in pciback/slots * It's not clear whether having two entries in pciback/slots is a problem * or not. Just to be safe, this code does the conservative thing, and * first checks to see if there is a slot, adding one only if one does not * already exist. */ /* Scan through /sys/.../pciback/slots looking for pcidev's BDF */ static int pciback_dev_has_slot(libxl__gc *gc, libxl_device_pci *pcidev) { FILE *f; int rc = 0; unsigned dom, bus, dev, func; f = fopen(SYSFS_PCIBACK_DRIVER"/slots", "r"); if (f == NULL) { LOGE(ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER"/slots"); return ERROR_FAIL; } while(fscanf(f, "%x:%x:%x.%d\n", &dom, &bus, &dev, &func)==4) { if(dom == pcidev->domain && bus == pcidev->bus && dev == pcidev->dev && func == pcidev->func) { rc = 1; goto out; } } out: fclose(f); return rc; } static int pciback_dev_is_assigned(libxl__gc *gc, libxl_device_pci *pcidev) { char * spath; int rc; struct stat st; if ( access(SYSFS_PCIBACK_DRIVER, F_OK) < 0 ) { if ( errno == ENOENT ) { LOG(ERROR, "Looks like pciback driver is not loaded"); } else { LOGE(ERROR, "Can't access "SYSFS_PCIBACK_DRIVER); } return -1; } spath = GCSPRINTF(SYSFS_PCIBACK_DRIVER"/"PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); rc = lstat(spath, &st); if( rc == 0 ) return 1; if ( rc < 0 && errno == ENOENT ) return 0; LOGE(ERROR, "Accessing %s", spath); return -1; } static int pciback_dev_assign(libxl__gc *gc, libxl_device_pci *pcidev) { int rc; if ( (rc=pciback_dev_has_slot(gc, pcidev)) < 0 ) { LOGE(ERROR, "Error checking for pciback slot"); return ERROR_FAIL; } else if (rc == 0) { if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/new_slot", pcidev) < 0 ) { LOGE(ERROR, "Couldn't bind device to pciback!"); return ERROR_FAIL; } } if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/bind", pcidev) < 0 ) { LOGE(ERROR, "Couldn't bind device to pciback!"); return ERROR_FAIL; } return 0; } static int pciback_dev_unassign(libxl__gc *gc, libxl_device_pci *pcidev) { /* Remove from pciback */ if ( sysfs_dev_unbind(gc, pcidev, NULL) < 0 ) { LOG(ERROR, "Couldn't unbind device!"); return ERROR_FAIL; } /* Remove slot if necessary */ if ( pciback_dev_has_slot(gc, pcidev) > 0 ) { if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/remove_slot", pcidev) < 0 ) { LOGE(ERROR, "Couldn't remove pciback slot"); return ERROR_FAIL; } } return 0; } #define PCIBACK_INFO_PATH "/libxl/pciback" static void pci_assignable_driver_path_write(libxl__gc *gc, libxl_device_pci *pcidev, char *driver_path) { char *path; path = GCSPRINTF(PCIBACK_INFO_PATH"/"PCI_BDF_XSPATH"/driver_path", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); if ( libxl__xs_printf(gc, XBT_NULL, path, "%s", driver_path) < 0 ) { LOGE(WARN, "Write of %s to node %s failed.", driver_path, path); } } static char * pci_assignable_driver_path_read(libxl__gc *gc, libxl_device_pci *pcidev) { return libxl__xs_read(gc, XBT_NULL, GCSPRINTF( PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH "/driver_path", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func)); } static void pci_assignable_driver_path_remove(libxl__gc *gc, libxl_device_pci *pcidev) { libxl_ctx *ctx = libxl__gc_owner(gc); /* Remove the xenstore entry */ xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF(PCIBACK_INFO_PATH "/" PCI_BDF_XSPATH, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func) ); } static int libxl__device_pci_assignable_add(libxl__gc *gc, libxl_device_pci *pcidev, int rebind) { unsigned dom, bus, dev, func; char *spath, *driver_path = NULL; int rc; struct stat st; /* Local copy for convenience */ dom = pcidev->domain; bus = pcidev->bus; dev = pcidev->dev; func = pcidev->func; /* See if the device exists */ spath = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF, dom, bus, dev, func); if ( lstat(spath, &st) ) { LOGE(ERROR, "Couldn't lstat %s", spath); return ERROR_FAIL; } /* Check to see if it's already assigned to pciback */ rc = pciback_dev_is_assigned(gc, pcidev); if ( rc < 0 ) { return ERROR_FAIL; } if ( rc ) { LOG(WARN, PCI_BDF" already assigned to pciback", dom, bus, dev, func); return 0; } /* Check to see if there's already a driver that we need to unbind from */ if ( sysfs_dev_unbind(gc, pcidev, &driver_path ) ) { LOG(ERROR, "Couldn't unbind "PCI_BDF" from driver", dom, bus, dev, func); return ERROR_FAIL; } /* Store driver_path for rebinding to dom0 */ if ( rebind ) { if ( driver_path ) { pci_assignable_driver_path_write(gc, pcidev, driver_path); } else if ( (driver_path = pci_assignable_driver_path_read(gc, pcidev)) != NULL ) { LOG(INFO, PCI_BDF" not bound to a driver, will be rebound to %s", dom, bus, dev, func, driver_path); } else { LOG(WARN, PCI_BDF" not bound to a driver, will not be rebound.", dom, bus, dev, func); } } else { pci_assignable_driver_path_remove(gc, pcidev); } if ( pciback_dev_assign(gc, pcidev) ) { LOG(ERROR, "Couldn't bind device to pciback!"); return ERROR_FAIL; } return 0; } static int libxl__device_pci_assignable_remove(libxl__gc *gc, libxl_device_pci *pcidev, int rebind) { int rc; char *driver_path; /* Unbind from pciback */ if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) { return ERROR_FAIL; } else if ( rc ) { pciback_dev_unassign(gc, pcidev); } else { LOG(WARN, "Not bound to pciback"); } /* Rebind if necessary */ driver_path = pci_assignable_driver_path_read(gc, pcidev); if ( driver_path ) { if ( rebind ) { LOG(INFO, "Rebinding to driver at %s", driver_path); if ( sysfs_write_bdf(gc, GCSPRINTF("%s/bind", driver_path), pcidev) < 0 ) { LOGE(ERROR, "Couldn't bind device to %s", driver_path); return -1; } pci_assignable_driver_path_remove(gc, pcidev); } } else { if ( rebind ) { LOG(WARN, "Couldn't find path for original driver; not rebinding"); } } return 0; } int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind) { GC_INIT(ctx); int rc; rc = libxl__device_pci_assignable_add(gc, pcidev, rebind); GC_FREE; return rc; } int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind) { GC_INIT(ctx); int rc; rc = libxl__device_pci_assignable_remove(gc, pcidev, rebind); GC_FREE; return rc; } /* * This function checks that all functions of a device are bound to pciback * driver. It also initialises a bit-mask of which function numbers are present * on that device. */ static int pci_multifunction_check(libxl__gc *gc, libxl_device_pci *pcidev, unsigned int *func_mask) { struct dirent *de; DIR *dir; *func_mask = 0; dir = opendir(SYSFS_PCI_DEV); if ( NULL == dir ) { LOGE(ERROR, "Couldn't open %s", SYSFS_PCI_DEV); return -1; } while( (de = readdir(dir)) ) { unsigned dom, bus, dev, func; struct stat st; char *path; if ( sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4 ) continue; if ( pcidev->domain != dom ) continue; if ( pcidev->bus != bus ) continue; if ( pcidev->dev != dev ) continue; path = GCSPRINTF("%s/" PCI_BDF, SYSFS_PCIBACK_DRIVER, dom, bus, dev, func); if ( lstat(path, &st) ) { if ( errno == ENOENT ) LOG(ERROR, PCI_BDF " is not assigned to pciback driver", dom, bus, dev, func); else LOGE(ERROR, "Couldn't lstat %s", path); closedir(dir); return -1; } (*func_mask) |= (1 << func); } closedir(dir); return 0; } static int pci_ins_check(libxl__gc *gc, uint32_t domid, const char *state, void *priv) { char *orig_state = priv; if ( !strcmp(state, "pci-insert-failed") ) return -1; if ( !strcmp(state, "pci-inserted") ) return 0; if ( !strcmp(state, orig_state) ) return 1; return 1; } static int qemu_pci_add_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev) { libxl_ctx *ctx = libxl__gc_owner(gc); int rc = 0; char *path; char *state, *vdevfn; uint32_t dm_domid; dm_domid = libxl_get_stubdom_id(CTX, domid); path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state"); state = libxl__xs_read(gc, XBT_NULL, path); path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter"); if (pcidev->vdevfn) { libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF_VDEVFN","PCI_OPTIONS, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func, pcidev->vdevfn, pcidev->msitranslate, pcidev->power_mgmt); } else { libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF","PCI_OPTIONS, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func, pcidev->msitranslate, pcidev->power_mgmt); } libxl__qemu_traditional_cmd(gc, domid, "pci-ins"); rc = libxl__wait_for_device_model_deprecated(gc, domid, NULL, NULL, pci_ins_check, state); path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter"); vdevfn = libxl__xs_read(gc, XBT_NULL, path); path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state"); if ( rc < 0 ) LOGD(ERROR, domid, "qemu refused to add device: %s", vdevfn); else if ( sscanf(vdevfn, "0x%x", &pcidev->vdevfn) != 1 ) { LOGD(ERROR, domid, "wrong format for the vdevfn: '%s'", vdevfn); rc = -1; } xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state)); return rc; } static int do_pci_add(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int starting) { libxl_ctx *ctx = libxl__gc_owner(gc); libxl_domain_type type = libxl__domain_type(gc, domid); char *sysfs_path; FILE *f; unsigned long long start, end, flags, size; int irq, i, rc, hvm = 0; uint32_t flag = XEN_DOMCTL_DEV_RDM_RELAXED; uint32_t domainid = domid; bool isstubdom = libxl_is_stubdom(ctx, domid, &domainid); if (type == LIBXL_DOMAIN_TYPE_INVALID) return ERROR_FAIL; if (type == LIBXL_DOMAIN_TYPE_HVM) { hvm = 1; if (libxl__wait_for_device_model_deprecated(gc, domid, "running", NULL, NULL, NULL) < 0) { return ERROR_FAIL; } switch (libxl__device_model_version_running(gc, domid)) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: rc = qemu_pci_add_xenstore(gc, domid, pcidev); break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: rc = libxl__qmp_pci_add(gc, domid, pcidev); break; default: return ERROR_INVAL; } if ( rc ) return ERROR_FAIL; } sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); f = fopen(sysfs_path, "r"); start = end = flags = size = 0; irq = 0; if (f == NULL) { LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path); return ERROR_FAIL; } for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) { if (fscanf(f, "0x%llx 0x%llx 0x%llx\n", &start, &end, &flags) != 3) continue; size = end - start + 1; if (start) { if (flags & PCI_BAR_IO) { rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 1); if (rc < 0) { LOGED(ERROR, domainid, "Error: xc_domain_ioport_permission error 0x%llx/0x%llx", start, size); fclose(f); return ERROR_FAIL; } } else { rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT, (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 1); if (rc < 0) { LOGED(ERROR, domainid, "Error: xc_domain_iomem_permission error 0x%llx/0x%llx", start, size); fclose(f); return ERROR_FAIL; } } } } fclose(f); sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); f = fopen(sysfs_path, "r"); if (f == NULL) { LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path); goto out; } if ((fscanf(f, "%u", &irq) == 1) && irq) { rc = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq); if (rc < 0) { LOGED(ERROR, domainid, "Error: xc_physdev_map_pirq irq=%d", irq); fclose(f); return ERROR_FAIL; } rc = xc_domain_irq_permission(ctx->xch, domid, irq, 1); if (rc < 0) { LOGED(ERROR, domainid, "Error: xc_domain_irq_permission irq=%d", irq); fclose(f); return ERROR_FAIL; } } fclose(f); /* Don't restrict writes to the PCI config space from this VM */ if (pcidev->permissive) { if ( sysfs_write_bdf(gc, SYSFS_PCIBACK_DRIVER"/permissive", pcidev) < 0 ) { LOGD(ERROR, domainid, "Setting permissive for device"); return ERROR_FAIL; } } out: if (!isstubdom) { if (pcidev->rdm_policy == LIBXL_RDM_RESERVE_POLICY_STRICT) { flag &= ~XEN_DOMCTL_DEV_RDM_RELAXED; } else if (pcidev->rdm_policy != LIBXL_RDM_RESERVE_POLICY_RELAXED) { LOGED(ERROR, domainid, "unknown rdm check flag."); return ERROR_FAIL; } rc = xc_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev), flag); if (rc < 0 && (hvm || errno != ENOSYS)) { LOGED(ERROR, domainid, "xc_assign_device failed"); return ERROR_FAIL; } } if (!starting) rc = libxl__device_pci_add_xenstore(gc, domid, pcidev, starting); else rc = 0; return rc; } static int libxl__device_pci_reset(libxl__gc *gc, unsigned int domain, unsigned int bus, unsigned int dev, unsigned int func) { char *reset; int fd, rc; reset = GCSPRINTF("%s/do_flr", SYSFS_PCIBACK_DRIVER); fd = open(reset, O_WRONLY); if (fd >= 0) { char *buf = GCSPRINTF(PCI_BDF, domain, bus, dev, func); rc = write(fd, buf, strlen(buf)); if (rc < 0) LOGD(ERROR, domain, "write to %s returned %d", reset, rc); close(fd); return rc < 0 ? rc : 0; } if (errno != ENOENT) LOGED(ERROR, domain, "Failed to access pciback path %s", reset); reset = GCSPRINTF("%s/"PCI_BDF"/reset", SYSFS_PCI_DEV, domain, bus, dev, func); fd = open(reset, O_WRONLY); if (fd >= 0) { rc = write(fd, "1", 1); if (rc < 0) LOGED(ERROR, domain, "write to %s returned %d", reset, rc); close(fd); return rc < 0 ? rc : 0; } if (errno == ENOENT) { LOGD(ERROR, domain, "The kernel doesn't support reset from sysfs for PCI device "PCI_BDF, domain, bus, dev, func); } else { LOGED(ERROR, domain, "Failed to access reset path %s", reset); } return -1; } int libxl__device_pci_setdefault(libxl__gc *gc, libxl_device_pci *pci) { /* We'd like to force reserve rdm specific to a device by default.*/ if (pci->rdm_policy == LIBXL_RDM_RESERVE_POLICY_INVALID) pci->rdm_policy = LIBXL_RDM_RESERVE_POLICY_STRICT; return 0; } int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int rc; rc = libxl__device_pci_add(gc, domid, pcidev, 0); libxl__ao_complete(egc, ao, rc); return AO_INPROGRESS; } static int libxl_pcidev_assignable(libxl_ctx *ctx, libxl_device_pci *pcidev) { libxl_device_pci *pcidevs; int num, i; pcidevs = libxl_device_pci_assignable_list(ctx, &num); for (i = 0; i < num; i++) { if (pcidevs[i].domain == pcidev->domain && pcidevs[i].bus == pcidev->bus && pcidevs[i].dev == pcidev->dev && pcidevs[i].func == pcidev->func) break; } free(pcidevs); return i != num; } int libxl__device_pci_add(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int starting) { libxl_ctx *ctx = libxl__gc_owner(gc); unsigned int orig_vdev, pfunc_mask; libxl_device_pci *assigned; int num_assigned, i, rc; int stubdomid = 0; if (libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) { rc = xc_test_assign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev)); if (rc) { LOGD(ERROR, domid, "PCI device %04x:%02x:%02x.%u %s?", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func, errno == ENOSYS ? "cannot be assigned - no IOMMU" : "already assigned to a different guest"); goto out; } } rc = libxl__device_pci_setdefault(gc, pcidev); if (rc) goto out; if (pcidev->seize && !pciback_dev_is_assigned(gc, pcidev)) { rc = libxl__device_pci_assignable_add(gc, pcidev, 1); if ( rc ) goto out; } if (!libxl_pcidev_assignable(ctx, pcidev)) { LOGD(ERROR, domid, "PCI device %x:%x:%x.%x is not assignable", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); rc = ERROR_FAIL; goto out; } rc = get_all_assigned_devices(gc, &assigned, &num_assigned); if ( rc ) { LOGD(ERROR, domid, "cannot determine if device is assigned, refusing to continue"); goto out; } if ( is_pcidev_in_array(assigned, num_assigned, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func) ) { LOGD(ERROR, domid, "PCI device already attached to a domain"); rc = ERROR_FAIL; goto out; } libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); stubdomid = libxl_get_stubdom_id(ctx, domid); if (stubdomid != 0) { libxl_device_pci pcidev_s = *pcidev; /* stubdomain is always running by now, even at create time */ rc = do_pci_add(gc, stubdomid, &pcidev_s, 0); if ( rc ) goto out; } orig_vdev = pcidev->vdevfn & ~7U; if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) { if ( !(pcidev->vdevfn >> 3) ) { LOGD(ERROR, domid, "Must specify a v-slot for multi-function devices"); rc = ERROR_INVAL; goto out; } if ( pci_multifunction_check(gc, pcidev, &pfunc_mask) ) { rc = ERROR_FAIL; goto out; } pcidev->vfunc_mask &= pfunc_mask; /* so now vfunc_mask == pfunc_mask */ }else{ pfunc_mask = (1 << pcidev->func); } for(rc = 0, i = 7; i >= 0; --i) { if ( (1 << i) & pfunc_mask ) { if ( pcidev->vfunc_mask == pfunc_mask ) { pcidev->func = i; pcidev->vdevfn = orig_vdev | i; }else{ /* if not passing through multiple devices in a block make * sure that virtual function number 0 is always used otherwise * guest won't see the device */ pcidev->vdevfn = orig_vdev; } if ( do_pci_add(gc, domid, pcidev, starting) ) rc = ERROR_FAIL; } } out: return rc; } static void libxl__add_pcidevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid, libxl_domain_config *d_config, libxl__multidev *multidev) { AO_GC; libxl__ao_device *aodev = libxl__multidev_prepare(multidev); int i, rc = 0; for (i = 0; i < d_config->num_pcidevs; i++) { rc = libxl__device_pci_add(gc, domid, &d_config->pcidevs[i], 1); if (rc < 0) { LOGD(ERROR, domid, "libxl_device_pci_add failed: %d", rc); goto out; } } if (d_config->num_pcidevs > 0) { rc = libxl__create_pci_backend(gc, domid, d_config->pcidevs, d_config->num_pcidevs); if (rc < 0) { LOGD(ERROR, domid, "libxl_create_pci_backend failed: %d", rc); goto out; } } out: aodev->rc = rc; aodev->callback(egc, aodev); } static int qemu_pci_remove_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int force) { libxl_ctx *ctx = libxl__gc_owner(gc); char *state; char *path; uint32_t dm_domid; dm_domid = libxl_get_stubdom_id(CTX, domid); path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state"); state = libxl__xs_read(gc, XBT_NULL, path); path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/parameter"); libxl__xs_printf(gc, XBT_NULL, path, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); /* Remove all functions at once atomically by only signalling * device-model for function 0 */ if ( !force && (pcidev->vdevfn & 0x7) == 0 ) { libxl__qemu_traditional_cmd(gc, domid, "pci-rem"); if (libxl__wait_for_device_model_deprecated(gc, domid, "pci-removed", NULL, NULL, NULL) < 0) { LOGD(ERROR, domid, "Device Model didn't respond in time"); /* This depends on guest operating system acknowledging the * SCI, if it doesn't respond in time then we may wish to * force the removal. */ return ERROR_FAIL; } } path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state"); xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state)); return 0; } static int libxl__device_pci_remove_common(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int force); static int do_pci_remove(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int force) { libxl_ctx *ctx = libxl__gc_owner(gc); libxl_device_pci *assigned; libxl_domain_type type = libxl__domain_type(gc, domid); int hvm = 0, rc, num; int stubdomid = 0; uint32_t domainid = domid; bool isstubdom = libxl_is_stubdom(ctx, domid, &domainid); assigned = libxl_device_pci_list(ctx, domid, &num); if ( assigned == NULL ) return ERROR_FAIL; rc = ERROR_INVAL; if ( !is_pcidev_in_array(assigned, num, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func) ) { LOGD(ERROR, domainid, "PCI device not attached to this domain"); goto out_fail; } rc = ERROR_FAIL; if (type == LIBXL_DOMAIN_TYPE_HVM) { hvm = 1; if (libxl__wait_for_device_model_deprecated(gc, domid, "running", NULL, NULL, NULL) < 0) goto out_fail; switch (libxl__device_model_version_running(gc, domid)) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: rc = qemu_pci_remove_xenstore(gc, domid, pcidev, force); break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: rc = libxl__qmp_pci_del(gc, domid, pcidev); break; default: rc = ERROR_INVAL; goto out_fail; } if (rc && !force) { rc = ERROR_FAIL; goto out_fail; } } else { assert(type == LIBXL_DOMAIN_TYPE_PV); char *sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); FILE *f = fopen(sysfs_path, "r"); unsigned int start = 0, end = 0, flags = 0, size = 0; int irq = 0; int i; if (f == NULL) { LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path); goto skip1; } for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) { if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3) continue; size = end - start + 1; if (start) { if (flags & PCI_BAR_IO) { rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 0); if (rc < 0) LOGED(ERROR, domainid, "xc_domain_ioport_permission error 0x%x/0x%x", start, size); } else { rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT, (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 0); if (rc < 0) LOGED(ERROR, domainid, "xc_domain_iomem_permission error 0x%x/0x%x", start, size); } } } fclose(f); skip1: sysfs_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); f = fopen(sysfs_path, "r"); if (f == NULL) { LOGED(ERROR, domainid, "Couldn't open %s", sysfs_path); goto out; } if ((fscanf(f, "%u", &irq) == 1) && irq) { rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq); if (rc < 0) { LOGED(ERROR, domainid, "xc_physdev_unmap_pirq irq=%d", irq); } rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0); if (rc < 0) { LOGED(ERROR, domainid, "xc_domain_irq_permission irq=%d", irq); } } fclose(f); } out: /* don't do multiple resets while some functions are still passed through */ if ( (pcidev->vdevfn & 0x7) == 0 ) { libxl__device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); } if (!isstubdom) { rc = xc_deassign_device(ctx->xch, domid, pcidev_encode_bdf(pcidev)); if (rc < 0 && (hvm || errno != ENOSYS)) LOGED(ERROR, domainid, "xc_deassign_device failed"); } stubdomid = libxl_get_stubdom_id(ctx, domid); if (stubdomid != 0) { libxl_device_pci pcidev_s = *pcidev; libxl__device_pci_remove_common(gc, stubdomid, &pcidev_s, force); } libxl__device_pci_remove_xenstore(gc, domid, pcidev); rc = 0; out_fail: free(assigned); return rc; } static int libxl__device_pci_remove_common(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int force) { unsigned int orig_vdev, pfunc_mask; int i, rc; orig_vdev = pcidev->vdevfn & ~7U; if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) { if ( pci_multifunction_check(gc, pcidev, &pfunc_mask) ) { rc = ERROR_FAIL; goto out; } pcidev->vfunc_mask &= pfunc_mask; }else{ pfunc_mask = (1 << pcidev->func); } for(rc = 0, i = 7; i >= 0; --i) { if ( (1 << i) & pfunc_mask ) { if ( pcidev->vfunc_mask == pfunc_mask ) { pcidev->func = i; pcidev->vdevfn = orig_vdev | i; }else{ pcidev->vdevfn = orig_vdev; } if ( do_pci_remove(gc, domid, pcidev, force) ) rc = ERROR_FAIL; } } out: return rc; } int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int rc; rc = libxl__device_pci_remove_common(gc, domid, pcidev, 0); libxl__ao_complete(egc, ao, rc); return AO_INPROGRESS; } int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int rc; rc = libxl__device_pci_remove_common(gc, domid, pcidev, 1); libxl__ao_complete(egc, ao, rc); return AO_INPROGRESS; } static void libxl__device_pci_from_xs_be(libxl__gc *gc, const char *be_path, libxl_device_pci *pci, int nr) { char *s; unsigned int domain = 0, bus = 0, dev = 0, func = 0, vdevfn = 0; s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/dev-%d", be_path, nr)); sscanf(s, PCI_BDF, &domain, &bus, &dev, &func); s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vdevfn-%d", be_path, nr)); if (s) vdevfn = strtol(s, (char **) NULL, 16); pcidev_struct_fill(pci, domain, bus, dev, func, vdevfn); s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/opts-%d", be_path, nr)); if (s) { char *saveptr; char *p = strtok_r(s, ",=", &saveptr); do { while (*p == ' ') p++; if (!strcmp(p, "msitranslate")) { p = strtok_r(NULL, ",=", &saveptr); pci->msitranslate = atoi(p); } else if (!strcmp(p, "power_mgmt")) { p = strtok_r(NULL, ",=", &saveptr); pci->power_mgmt = atoi(p); } else if (!strcmp(p, "permissive")) { p = strtok_r(NULL, ",=", &saveptr); pci->permissive = atoi(p); } } while ((p = strtok_r(NULL, ",=", &saveptr)) != NULL); } } libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid, int *num) { GC_INIT(ctx); char *be_path, *num_devs; int n, i; libxl_device_pci *pcidevs = NULL; *num = 0; be_path = GCSPRINTF("%s/backend/pci/%d/0", libxl__xs_get_dompath(gc, 0), domid); num_devs = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/num_devs", be_path)); if (!num_devs) goto out; n = atoi(num_devs); pcidevs = calloc(n, sizeof(libxl_device_pci)); for (i = 0; i < n; i++) libxl__device_pci_from_xs_be(gc, be_path, pcidevs + i, i); *num = n; out: GC_FREE; return pcidevs; } int libxl__device_pci_destroy_all(libxl__gc *gc, uint32_t domid) { libxl_ctx *ctx = libxl__gc_owner(gc); libxl_device_pci *pcidevs; int num, i, rc = 0; pcidevs = libxl_device_pci_list(ctx, domid, &num); if ( pcidevs == NULL ) return 0; for (i = 0; i < num; i++) { /* Force remove on shutdown since, on HVM, qemu will not always * respond to SCI interrupt because the guest kernel has shut down the * devices by the time we even get here! */ if (libxl__device_pci_remove_common(gc, domid, pcidevs + i, 1) < 0) rc = ERROR_FAIL; } free(pcidevs); return rc; } int libxl__grant_vga_iomem_permission(libxl__gc *gc, const uint32_t domid, libxl_domain_config *const d_config) { int i, ret; if (!libxl_defbool_val(d_config->b_info.u.hvm.gfx_passthru)) return 0; for (i = 0 ; i < d_config->num_pcidevs ; i++) { uint64_t vga_iomem_start = 0xa0000 >> XC_PAGE_SHIFT; uint32_t stubdom_domid; libxl_device_pci *pcidev = &d_config->pcidevs[i]; char *pci_device_class_path = GCSPRINTF(SYSFS_PCI_DEV"/"PCI_BDF"/class", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); int read_items; unsigned long pci_device_class; FILE *f = fopen(pci_device_class_path, "r"); if (!f) { LOGED(ERROR, domid, "pci device "PCI_BDF" does not have class attribute", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); continue; } read_items = fscanf(f, "0x%lx\n", &pci_device_class); fclose(f); if (read_items != 1) { LOGED(ERROR, domid, "cannot read class of pci device "PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); continue; } if (pci_device_class != 0x030000) /* VGA class */ continue; stubdom_domid = libxl_get_stubdom_id(CTX, domid); ret = xc_domain_iomem_permission(CTX->xch, stubdom_domid, vga_iomem_start, 0x20, 1); if (ret < 0) { LOGED(ERROR, domid, "failed to give stubdom%d access to iomem range " "%"PRIx64"-%"PRIx64" for VGA passthru", stubdom_domid, vga_iomem_start, (vga_iomem_start + 0x20 - 1)); return ret; } ret = xc_domain_iomem_permission(CTX->xch, domid, vga_iomem_start, 0x20, 1); if (ret < 0) { LOGED(ERROR, domid, "failed to give dom%d access to iomem range " "%"PRIx64"-%"PRIx64" for VGA passthru", domid, vga_iomem_start, (vga_iomem_start + 0x20 - 1)); return ret; } break; } return 0; } static int libxl_device_pci_compare(libxl_device_pci *d1, libxl_device_pci *d2) { return COMPARE_PCI(d1, d2); } DEFINE_DEVICE_TYPE_STRUCT_X(pcidev, pci); /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/check-xl-vcpupin-parse0000775000175000017500000002205213256712137017146 0ustar smbsmb#!/bin/bash set -e if [ -x ./xl ] ; then export LD_LIBRARY_PATH=.:../libxc:../xenstore: XL=./xl else XL=xl fi fprefix=tmp.check-xl-vcpupin-parse outfile=check-xl-vcpupin-parse.data usage () { cat <$fprefix.expected } # by default, re-seed with our PID seed=$$ failures=0 # Execute one test and check the result against the provided # rc value and output one () { expected_rc=$1; shift printf "test case %s...\n" "$*" set +e ${XL} -N vcpu-pin 0 all "$@" $fprefix.actual 2>/dev/null actual_rc=$? if [ $actual_rc != $expected_rc ]; then diff -u $fprefix.expected $fprefix.actual echo >&2 "test case \`$*' failed ($actual_rc $diff_rc)" failures=$(( $failures + 1 )) fi set -e } # Write an entry in the test vector file. Format is as follows: # test-string*expected-rc*expected-output write () { printf "$1*$2*$3\n" >> $outfile } complete () { if [ "$failures" = 0 ]; then echo all ok.; exit 0 else echo "$failures tests failed."; exit 1 fi } # Test a specific pinning string string () { expected_rc=$1; shift printf "test case %s...\n" "$*" set +e ${XL} -N vcpu-pin 0 all "$@" &> /dev/null actual_rc=$? set -e if [ $actual_rc != $expected_rc ]; then echo >&2 "test case \`$*' failed ($actual_rc)" else echo >&2 "test case \`$*' succeeded" fi exit 0 } # Read a test vector file (provided as $1) line by line and # test all the entries it contains run () { while read line do if [ ${line:0:1} != '#' ]; then test_string="`echo $line | cut -f1 -d'*'`" exp_rc="`echo $line | cut -f2 -d'*'`" exp_output="`echo $line | cut -f3 -d'*'`" expected <$outfile <> $outfile write foo 255 "" echo "# Testing the 'all' syntax" >> $outfile write "all" 0 "cpumap: all" write "nodes:all" 0 "cpumap: all" write "all,nodes:all" 0 "cpumap: all" write "all,^nodes:0,all" 0 "cpumap: all" echo "# Testing the empty cpumap case" >> $outfile write "^0" 0 "cpumap: none" echo "# A few attempts of pinning to just one random cpu" >> $outfile if [ $nr_cpus -gt 1 ]; then for i in `seq 0 3`; do cpu=$(($RANDOM % nr_cpus)) write "$cpu" 0 "cpumap: $cpu" done fi echo "# A few attempts of pinning to all but one random cpu" >> $outfile if [ $nr_cpus -gt 2 ]; then for i in `seq 0 3`; do cpu=$(($RANDOM % nr_cpus)) if [ $cpu -eq 0 ]; then expected_range="1-$((nr_cpus - 1))" elif [ $cpu -eq 1 ]; then expected_range="0,2-$((nr_cpus - 1))" elif [ $cpu -eq $((nr_cpus - 2)) ]; then expected_range="0-$((cpu - 1)),$((nr_cpus - 1))" elif [ $cpu -eq $((nr_cpus - 1)) ]; then expected_range="0-$((nr_cpus - 2))" else expected_range="0-$((cpu - 1)),$((cpu + 1))-$((nr_cpus - 1))" fi write "all,^$cpu" 0 "cpumap: $expected_range" done fi echo "# A few attempts of pinning to a random range of cpus" >> $outfile if [ $nr_cpus -gt 2 ]; then for i in `seq 0 3`; do cpua=$(($RANDOM % nr_cpus)) range=$((nr_cpus - cpua)) cpub=$(($RANDOM % range)) cpubb=$((cpua + cpub)) if [ $cpua -eq $cpubb ]; then expected_range="$cpua" else expected_range="$cpua-$cpubb" fi write "$expected_range" 0 "cpumap: $expected_range" done fi echo "# A few attempts of pinning to just one random node" >> $outfile if [ $nr_nodes -gt 1 ]; then for i in `seq 0 3`; do node=$(($RANDOM % nr_nodes)) # this assumes that the first $nr_cpus_per_node (from cpu # 0 to cpu $nr_cpus_per_node-1) are assigned to the first node # (node 0), the second $nr_cpus_per_node (from $nr_cpus_per_node # to 2*$nr_cpus_per_node-1) are assigned to the second node (node # 1), etc. Expect failures if that is not the case. write "nodes:$node" 0 "cpumap: $((nr_cpus_per_node*node))-$((nr_cpus_per_node*(node+1)-1))" done fi echo "# A few attempts of pinning to all but one random node" >> $outfile if [ $nr_nodes -gt 1 ]; then for i in `seq 0 3`; do node=$(($RANDOM % nr_nodes)) # this assumes that the first $nr_cpus_per_node (from cpu # 0 to cpu $nr_cpus_per_node-1) are assigned to the first node # (node 0), the second $nr_cpus_per_node (from $nr_cpus_per_node # to 2*$nr_cpus_per_node-1) are assigned to the second node (node # 1), etc. Expect failures if that is not the case. if [ $node -eq 0 ]; then expected_range="$nr_cpus_per_node-$((nr_cpus - 1))" elif [ $node -eq $((nr_nodes - 1)) ]; then expected_range="0-$((nr_cpus - nr_cpus_per_node - 1))" else expected_range="0-$((nr_cpus_per_node*node-1)),$((nr_cpus_per_node*(node+1)))-$nr_cpus" fi write "all,^nodes:$node" 0 "cpumap: $expected_range" done fi echo "# A few attempts of pinning to a random range of nodes" >> $outfile if [ $nr_nodes -gt 1 ]; then for i in `seq 0 3`; do nodea=$(($RANDOM % nr_nodes)) range=$((nr_nodes - nodea)) nodeb=$(($RANDOM % range)) nodebb=$((nodea + nodeb)) # this assumes that the first $nr_cpus_per_node (from cpu # 0 to cpu $nr_cpus_per_node-1) are assigned to the first node # (node 0), the second $nr_cpus_per_node (from $nr_cpus_per_node # to 2*$nr_cpus_per_node-1) are assigned to the second node (node # 1), etc. Expect failures if that is not the case. if [ $nodea -eq 0 ] && [ $nodebb -eq $((nr_nodes - 1)) ]; then expected_range="all" else expected_range="$((nr_cpus_per_node*nodea))-$((nr_cpus_per_node*(nodebb+1) - 1))" fi write "nodes:$nodea-$nodebb" 0 "cpumap: $expected_range" done fi echo "# A few attempts of pinning to a node but excluding one random cpu" >> $outfile if [ $nr_nodes -gt 1 ]; then for i in `seq 0 3`; do node=$(($RANDOM % nr_nodes)) # this assumes that the first $nr_cpus_per_node (from cpu # 0 to cpu $nr_cpus_per_node-1) are assigned to the first node # (node 0), the second $nr_cpus_per_node (from $nr_cpus_per_node # to 2*$nr_cpus_per_node-1) are assigned to the second node (node # 1), etc. Expect failures if that is not the case. cpu=$(($RANDOM % nr_cpus_per_node + nr_cpus_per_node*node)) if [ $cpu -eq $((nr_cpus_per_node*node)) ]; then expected_range="$((nr_cpus_per_node*node + 1))-$((nr_cpus_per_node*(node+1) - 1))" elif [ $cpu -eq $((nr_cpus_per_node*node + 1)) ]; then expected_range="$((nr_cpus_per_node*node)),$((nr_cpus_per_node*node + 2))-$((nr_cpus_per_node*(node+1) - 1))" elif [ $cpu -eq $((nr_cpus_per_node*(node+1) - 2)) ]; then expected_range="$((nr_cpus_per_node*node))-$((nr_cpus_per_node*(node+1) - 3)),$((nr_cpus_per_node*(node+1) - 1))" elif [ $cpu -eq $((nr_cpus_per_node*(node+1) - 1)) ]; then expected_range="$((nr_cpus_per_node*node))-$((nr_cpus_per_node*(node+1) - 2))" else expected_range="$((nr_cpus_per_node*node))-$((cpu - 1)),$((cpu + 1))-$((nr_cpus_per_node*(node+1) - 1))" fi write "nodes:$node,^$cpu" 0 "cpumap: $expected_range" done fi run $outfile xen-4.9.2/tools/libxl/libxl_device.c0000664000175000017500000016023713256712137015535 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" static char *libxl__device_frontend_path(libxl__gc *gc, libxl__device *device) { char *dom_path = libxl__xs_get_dompath(gc, device->domid); /* Console 0 is a special case */ if (device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0) return GCSPRINTF("%s/console", dom_path); return GCSPRINTF("%s/device/%s/%d", dom_path, libxl__device_kind_to_string(device->kind), device->devid); } char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device) { char *dom_path = libxl__xs_get_dompath(gc, device->backend_domid); return GCSPRINTF("%s/backend/%s/%u/%d", dom_path, libxl__device_kind_to_string(device->backend_kind), device->domid, device->devid); } char *libxl__device_libxl_path(libxl__gc *gc, libxl__device *device) { char *libxl_dom_path = libxl__xs_libxl_path(gc, device->domid); return GCSPRINTF("%s/device/%s/%d", libxl_dom_path, libxl__device_kind_to_string(device->kind), device->devid); } /* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */ int libxl__device_exists(libxl__gc *gc, xs_transaction_t t, libxl__device *device) { int rc; char *be_path = libxl__device_libxl_path(gc, device); const char *dir; rc = libxl__xs_read_checked(gc, t, be_path, &dir); if (rc) return rc; if (dir) return 1; return 0; } int libxl__parse_backend_path(libxl__gc *gc, const char *path, libxl__device *dev) { /* /local/domain//backend/// */ char strkind[16]; /* Longest is actually "console" */ int rc = sscanf(path, "/local/domain/%d/backend/%15[^/]/%u/%d", &dev->backend_domid, strkind, &dev->domid, &dev->devid); if (rc != 4) return ERROR_FAIL; return libxl__device_kind_from_string(strkind, &dev->backend_kind); } int libxl__nic_type(libxl__gc *gc, libxl__device *dev, libxl_nic_type *nictype) { char *snictype, *be_path; int rc = 0; be_path = libxl__device_backend_path(gc, dev); snictype = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "type")); if (!snictype) { LOGED(ERROR, dev->domid, "unable to read nictype from %s", be_path); rc = ERROR_FAIL; goto out; } rc = libxl_nic_type_from_string(snictype, nictype); if (rc) { LOGED(ERROR, dev->domid, "unable to parse nictype from %s", be_path); goto out; } rc = 0; out: return rc; } int libxl__device_generic_add(libxl__gc *gc, xs_transaction_t t, libxl__device *device, char **bents, char **fents, char **ro_fents) { libxl_ctx *ctx = libxl__gc_owner(gc); char *frontend_path = NULL, *backend_path = NULL, *libxl_path; struct xs_permissions frontend_perms[2]; struct xs_permissions ro_frontend_perms[2]; struct xs_permissions backend_perms[2]; int create_transaction = t == XBT_NULL; int libxl_only = device->backend_kind == LIBXL__DEVICE_KIND_NONE; int rc; if (libxl_only) { /* bents should be set as this is used to setup libxl_path content. */ assert(!fents && !ro_fents); } else { frontend_path = libxl__device_frontend_path(gc, device); backend_path = libxl__device_backend_path(gc, device); } libxl_path = libxl__device_libxl_path(gc, device); frontend_perms[0].id = device->domid; frontend_perms[0].perms = XS_PERM_NONE; frontend_perms[1].id = device->backend_domid; frontend_perms[1].perms = XS_PERM_READ; ro_frontend_perms[0].id = backend_perms[0].id = device->backend_domid; ro_frontend_perms[0].perms = backend_perms[0].perms = XS_PERM_NONE; ro_frontend_perms[1].id = backend_perms[1].id = device->domid; ro_frontend_perms[1].perms = backend_perms[1].perms = XS_PERM_READ; retry_transaction: if (create_transaction) t = xs_transaction_start(ctx->xsh); /* FIXME: read frontend_path and check state before removing stuff */ rc = libxl__xs_rm_checked(gc, t, libxl_path); if (rc) goto out; if (!libxl_only) { rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/frontend",libxl_path), frontend_path); if (rc) goto out; rc = libxl__xs_write_checked(gc, t, GCSPRINTF("%s/backend",libxl_path), backend_path); if (rc) goto out; } /* xxx much of this function lacks error checks! */ if (fents || ro_fents) { xs_rm(ctx->xsh, t, frontend_path); xs_mkdir(ctx->xsh, t, frontend_path); /* Console 0 is a special case. It doesn't use the regular PV * state machine but also the frontend directory has * historically contained other information, such as the * vnc-port, which we don't want the guest fiddling with. */ if (device->kind == LIBXL__DEVICE_KIND_CONSOLE && device->devid == 0) xs_set_permissions(ctx->xsh, t, frontend_path, ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms)); else xs_set_permissions(ctx->xsh, t, frontend_path, frontend_perms, ARRAY_SIZE(frontend_perms)); xs_write(ctx->xsh, t, GCSPRINTF("%s/backend", frontend_path), backend_path, strlen(backend_path)); if (fents) libxl__xs_writev_perms(gc, t, frontend_path, fents, frontend_perms, ARRAY_SIZE(frontend_perms)); if (ro_fents) libxl__xs_writev_perms(gc, t, frontend_path, ro_fents, ro_frontend_perms, ARRAY_SIZE(ro_frontend_perms)); } if (bents) { if (!libxl_only) { xs_rm(ctx->xsh, t, backend_path); xs_mkdir(ctx->xsh, t, backend_path); xs_set_permissions(ctx->xsh, t, backend_path, backend_perms, ARRAY_SIZE(backend_perms)); xs_write(ctx->xsh, t, GCSPRINTF("%s/frontend", backend_path), frontend_path, strlen(frontend_path)); libxl__xs_writev(gc, t, backend_path, bents); } /* * We make a copy of everything for the backend in the libxl * path as well. This means we don't need to trust the * backend. Ideally this information would not be used and we * would use the information from the json configuration * instead. But there are still places in libxl that try to * reconstruct a config from xenstore. * * For devices without PV backend (e.g. USB devices emulated via qemu) * only the libxl path is written. * * This duplication will typically produces duplicate keys * which will go out of date, but that's OK because nothing * reads those. For example, there is usually * /libxl/$guest/device/$kind/$devid/state * which starts out containing XenbusStateInitialising ("1") * just like the copy in * /local/domain/$driverdom/backend/$guest/$kind/$devid/state * but which won't ever be updated. * * This duplication is superfluous and messy but as discussed * the proper fix is more intrusive than we want to do now. */ rc = libxl__xs_writev(gc, t, libxl_path, bents); if (rc) goto out; } if (!create_transaction) return 0; if (!xs_transaction_end(ctx->xsh, t, 0)) { if (errno == EAGAIN) goto retry_transaction; else { LOGED(ERROR, device->domid, "xs transaction failed"); return ERROR_FAIL; } } return 0; out: if (create_transaction && t) libxl__xs_transaction_abort(gc, &t); return rc; } typedef struct { libxl__gc *gc; libxl_device_disk *disk; struct stat stab; } disk_try_backend_args; static int disk_try_backend(disk_try_backend_args *a, libxl_disk_backend backend) { libxl__gc *gc = a->gc; /* returns 0 (ie, DISK_BACKEND_UNKNOWN) on failure, or * backend on success */ switch (backend) { case LIBXL_DISK_BACKEND_PHY: if (a->disk->format != LIBXL_DISK_FORMAT_RAW) { goto bad_format; } if (libxl_defbool_val(a->disk->colo_enable)) goto bad_colo; if (a->disk->backend_domid != LIBXL_TOOLSTACK_DOMID) { LOG(DEBUG, "Disk vdev=%s, is using a storage driver domain, " "skipping physical device check", a->disk->vdev); return backend; } if (a->disk->script) { LOG(DEBUG, "Disk vdev=%s, uses script=... assuming phy backend", a->disk->vdev); return backend; } if (libxl__try_phy_backend(a->stab.st_mode)) return backend; LOG(DEBUG, "Disk vdev=%s, backend phy unsuitable as phys path not a " "block device", a->disk->vdev); return 0; case LIBXL_DISK_BACKEND_TAP: if (a->disk->script) goto bad_script; if (libxl_defbool_val(a->disk->colo_enable)) goto bad_colo; if (a->disk->is_cdrom) { LOG(DEBUG, "Disk vdev=%s, backend tap unsuitable for cdroms", a->disk->vdev); return 0; } if (!libxl__blktap_enabled(a->gc)) { LOG(DEBUG, "Disk vdev=%s, backend tap unsuitable because blktap " "not available", a->disk->vdev); return 0; } if (!(a->disk->format == LIBXL_DISK_FORMAT_RAW || a->disk->format == LIBXL_DISK_FORMAT_VHD)) { goto bad_format; } return backend; case LIBXL_DISK_BACKEND_QDISK: if (a->disk->script) goto bad_script; return backend; default: LOG(DEBUG, "Disk vdev=%s, backend %d unknown", a->disk->vdev, backend); return 0; } abort(); /* notreached */ bad_format: LOG(DEBUG, "Disk vdev=%s, backend %s unsuitable due to format %s", a->disk->vdev, libxl_disk_backend_to_string(backend), libxl_disk_format_to_string(a->disk->format)); return 0; bad_script: LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with script=...", a->disk->vdev, libxl_disk_backend_to_string(backend)); return 0; bad_colo: LOG(DEBUG, "Disk vdev=%s, backend %s not compatible with colo", a->disk->vdev, libxl_disk_backend_to_string(backend)); return 0; } int libxl__backendpath_parse_domid(libxl__gc *gc, const char *be_path, libxl_domid *domid_out) { int r; unsigned int domid_sc; char delim_sc; r = sscanf(be_path, "/local/domain/%u%c", &domid_sc, &delim_sc); if (!(r==2 && delim_sc=='/')) { LOG(ERROR, "internal error: backend path %s unparseable!", be_path); return ERROR_FAIL; } *domid_out = domid_sc; return 0; } int libxl__device_disk_set_backend(libxl__gc *gc, libxl_device_disk *disk) { libxl_disk_backend ok; disk_try_backend_args a; a.gc = gc; a.disk = disk; LOG(DEBUG, "Disk vdev=%s spec.backend=%s", disk->vdev, libxl_disk_backend_to_string(disk->backend)); if (disk->format == LIBXL_DISK_FORMAT_EMPTY) { if (!disk->is_cdrom) { LOG(ERROR, "Disk vdev=%s is empty but not cdrom", disk->vdev); return ERROR_INVAL; } if (disk->pdev_path != NULL && strcmp(disk->pdev_path, "")) { LOG(ERROR, "Disk vdev=%s is empty but an image has been provided: %s", disk->vdev, disk->pdev_path); return ERROR_INVAL; } memset(&a.stab, 0, sizeof(a.stab)); } else if ((disk->backend == LIBXL_DISK_BACKEND_UNKNOWN || disk->backend == LIBXL_DISK_BACKEND_PHY) && disk->backend_domid == LIBXL_TOOLSTACK_DOMID && !disk->script) { if (stat(disk->pdev_path, &a.stab)) { LOGE(ERROR, "Disk vdev=%s failed to stat: %s", disk->vdev, disk->pdev_path); return ERROR_INVAL; } } if (disk->backend != LIBXL_DISK_BACKEND_UNKNOWN) { ok= disk_try_backend(&a, disk->backend); } else { ok= disk_try_backend(&a, LIBXL_DISK_BACKEND_PHY) ?: disk_try_backend(&a, LIBXL_DISK_BACKEND_QDISK) ?: disk_try_backend(&a, LIBXL_DISK_BACKEND_TAP); if (ok) LOG(DEBUG, "Disk vdev=%s, using backend %s", disk->vdev, libxl_disk_backend_to_string(ok)); } if (!ok) { LOG(ERROR, "no suitable backend for disk %s", disk->vdev); return ERROR_INVAL; } disk->backend = ok; return 0; } char *libxl__device_disk_string_of_format(libxl_disk_format format) { switch (format) { case LIBXL_DISK_FORMAT_QCOW: return "qcow"; case LIBXL_DISK_FORMAT_QCOW2: return "qcow2"; case LIBXL_DISK_FORMAT_VHD: return "vhd"; case LIBXL_DISK_FORMAT_RAW: case LIBXL_DISK_FORMAT_EMPTY: return "aio"; case LIBXL_DISK_FORMAT_QED: return "qed"; default: return NULL; } } char *libxl__device_disk_string_of_backend(libxl_disk_backend backend) { switch (backend) { case LIBXL_DISK_BACKEND_QDISK: return "qdisk"; case LIBXL_DISK_BACKEND_TAP: return "phy"; case LIBXL_DISK_BACKEND_PHY: return "phy"; default: return NULL; } } int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor) { struct stat buf; if (stat(physpath, &buf) < 0) return -1; if (!S_ISBLK(buf.st_mode)) return -1; *major = major(buf.st_rdev); *minor = minor(buf.st_rdev); return 0; } static int device_virtdisk_matches(const char *virtpath, const char *devtype, int *index_r, int max_index, int *partition_r, int max_partition) { const char *p; char *ep; int tl, c; long pl; tl = strlen(devtype); if (memcmp(virtpath, devtype, tl)) return 0; /* We decode the drive letter as if it were in base 52 * with digits a-zA-Z, more or less */ *index_r = -1; p = virtpath + tl; for (;;) { c = *p++; if (c >= 'a' && c <= 'z') { c -= 'a'; } else { --p; break; } (*index_r)++; (*index_r) *= 26; (*index_r) += c; if (*index_r > max_index) return 0; } if (!*p) { *partition_r = 0; return 1; } if (*p=='0') return 0; /* leading zeroes not permitted in partition number */ pl = strtoul(p, &ep, 10); if (pl > max_partition || *ep) return 0; *partition_r = pl; return 1; } int libxl__device_disk_dev_number(const char *virtpath, int *pdisk, int *ppartition) { int disk, partition; char *ep; unsigned long ul; int chrused; chrused = -1; if ((sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused) >= 2 && chrused == strlen(virtpath) && disk < (1<<20) && partition < 256) || device_virtdisk_matches(virtpath, "xvd", &disk, (1<<20)-1, &partition, 255)) { if (pdisk) *pdisk = disk; if (ppartition) *ppartition = partition; if (disk <= 15 && partition <= 15) return (202 << 8) | (disk << 4) | partition; else return (1 << 28) | (disk << 8) | partition; } errno = 0; ul = strtoul(virtpath, &ep, 0); if (!errno && !*ep && ul <= INT_MAX) { /* FIXME: should parse ul to determine these. */ if (pdisk || ppartition) return -1; return ul; } if (device_virtdisk_matches(virtpath, "hd", &disk, 3, &partition, 63)) { if (pdisk) *pdisk = disk; if (ppartition) *ppartition = partition; return ((disk<2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition; } if (device_virtdisk_matches(virtpath, "sd", &disk, 15, &partition, 15)) { if (pdisk) *pdisk = disk; if (ppartition) *ppartition = partition; return (8 << 8) | (disk << 4) | partition; } return -1; } static char *encode_disk_name(char *ptr, unsigned int n) { if (n >= 26) ptr = encode_disk_name(ptr, n / 26 - 1); *ptr = 'a' + n % 26; return ptr + 1; } char *libxl__devid_to_vdev(libxl__gc *gc, int devid) { unsigned int minor; int offset; int nr_parts; char *ptr = NULL; /* Same as in Linux. * encode_disk_name might end up using up to 29 bytes (BUFFER_SIZE - 3) * including the trailing \0. * * The code is safe because 26 raised to the power of 28 (that is the * maximum offset that can be stored in the allocated buffer as a * string) is far greater than UINT_MAX on 64 bits so offset cannot be * big enough to exhaust the available bytes in ret. */ #define BUFFER_SIZE 32 char *ret = libxl__zalloc(gc, BUFFER_SIZE); #define EXT_SHIFT 28 #define EXTENDED (1<ao = ao; aodev->rc = 0; aodev->dev = NULL; aodev->num_exec = 0; /* Initialize timer for QEMU Bodge */ libxl__ev_time_init(&aodev->timeout); /* * Initialize xs_watch, because it's not used on all possible * execution paths, but it's unconditionally destroyed when finished. */ libxl__xswait_init(&aodev->xswait); aodev->active = 1; /* We init this here because we might call device_hotplug_done * without actually calling any hotplug script */ libxl__async_exec_init(&aodev->aes); libxl__ev_child_init(&aodev->child); } /* multidev */ void libxl__multidev_begin(libxl__ao *ao, libxl__multidev *multidev) { AO_GC; multidev->ao = ao; multidev->array = 0; multidev->used = multidev->allocd = 0; /* We allocate an aodev to represent the operation of preparing * all of the other operations. This operation is completed when * we have started all the others (ie, when the user calls * _prepared). That arranges automatically that * (i) we do not think we have finished even if one of the * operations completes while we are still preparing * (ii) if we are starting zero operations, we do still * make the callback as soon as we know this fact * (iii) we have a nice consistent way to deal with any * error that might occur while deciding what to initiate */ multidev->preparation = libxl__multidev_prepare(multidev); } void libxl__multidev_prepare_with_aodev(libxl__multidev *multidev, libxl__ao_device *aodev) { STATE_AO_GC(multidev->ao); aodev->multidev = multidev; aodev->callback = libxl__multidev_one_callback; libxl__prepare_ao_device(ao, aodev); if (multidev->used >= multidev->allocd) { multidev->allocd = multidev->used * 2 + 5; GCREALLOC_ARRAY(multidev->array, multidev->allocd); } multidev->array[multidev->used++] = aodev; } libxl__ao_device *libxl__multidev_prepare(libxl__multidev *multidev) { STATE_AO_GC(multidev->ao); libxl__ao_device *aodev; GCNEW(aodev); libxl__multidev_prepare_with_aodev(multidev, aodev); return aodev; } void libxl__multidev_one_callback(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); libxl__multidev *multidev = aodev->multidev; int i, error = 0; aodev->active = 0; for (i = 0; i < multidev->used; i++) { if (multidev->array[i]->active) return; if (multidev->array[i]->rc) error = multidev->array[i]->rc; } multidev->callback(egc, multidev, error); return; } void libxl__multidev_prepared(libxl__egc *egc, libxl__multidev *multidev, int rc) { multidev->preparation->rc = rc; libxl__multidev_one_callback(egc, multidev->preparation); } /******************************************************************************/ int libxl__device_destroy(libxl__gc *gc, libxl__device *dev) { const char *be_path = NULL; const char *fe_path = NULL; const char *libxl_path = libxl__device_libxl_path(gc, dev); const char *tapdisk_path = NULL; const char *tapdisk_params = NULL; xs_transaction_t t = 0; int rc; uint32_t domid; int libxl_only = dev->backend_kind == LIBXL__DEVICE_KIND_NONE; if (!libxl_only) { be_path = libxl__device_backend_path(gc, dev); fe_path = libxl__device_frontend_path(gc, dev); tapdisk_path = GCSPRINTF("%s/%s", be_path, "tapdisk-params"); } rc = libxl__get_domid(gc, &domid); if (rc) goto out; for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; /* May not exist if this is not a tap device */ if (tapdisk_path) { rc = libxl__xs_read_checked(gc, t, tapdisk_path, &tapdisk_params); if (rc) goto out; } if (domid == LIBXL_TOOLSTACK_DOMID) { /* * The toolstack domain is in charge of removing the * frontend and libxl paths. */ if (!libxl_only) libxl__xs_path_cleanup(gc, t, fe_path); libxl__xs_path_cleanup(gc, t, libxl_path); } if (dev->backend_domid == domid && !libxl_only) { /* * The driver domain is in charge of removing what it can * from the backend path. */ libxl__xs_path_cleanup(gc, t, be_path); } rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } if (tapdisk_params) rc = libxl__device_destroy_tapdisk(gc, tapdisk_params); out: libxl__xs_transaction_abort(gc, &t); return rc; } /* Callback for device destruction */ static void devices_remove_callback(libxl__egc *egc, libxl__multidev *multidev, int rc); void libxl__devices_destroy(libxl__egc *egc, libxl__devices_remove_state *drs) { STATE_AO_GC(drs->ao); uint32_t domid = drs->domid; char *path; unsigned int num_kinds, num_dev_xsentries; char **kinds = NULL, **devs = NULL; int i, j, rc = 0; libxl__device *dev; libxl__multidev *multidev = &drs->multidev; libxl__ao_device *aodev; libxl__device_kind kind; libxl__multidev_begin(ao, multidev); multidev->callback = devices_remove_callback; path = GCSPRINTF("/libxl/%d/device", domid); kinds = libxl__xs_directory(gc, XBT_NULL, path, &num_kinds); if (!kinds) { if (errno != ENOENT) { LOGE(ERROR, "unable to get xenstore device listing %s", path); goto out; } num_kinds = 0; } for (i = 0; i < num_kinds; i++) { if (libxl__device_kind_from_string(kinds[i], &kind)) continue; path = GCSPRINTF("/libxl/%d/device/%s", domid, kinds[i]); devs = libxl__xs_directory(gc, XBT_NULL, path, &num_dev_xsentries); if (!devs) continue; for (j = 0; j < num_dev_xsentries; j++) { path = GCSPRINTF("/libxl/%d/device/%s/%s/backend", domid, kinds[i], devs[j]); path = libxl__xs_read(gc, XBT_NULL, path); GCNEW(dev); if (path && libxl__parse_backend_path(gc, path, dev) == 0) { dev->domid = domid; dev->kind = kind; dev->devid = atoi(devs[j]); if (dev->backend_kind == LIBXL__DEVICE_KIND_CONSOLE) { /* Currently console devices can be destroyed * synchronously by just removing xenstore entries, * this is what libxl__device_destroy does. */ libxl__device_destroy(gc, dev); continue; } aodev = libxl__multidev_prepare(multidev); aodev->action = LIBXL__DEVICE_ACTION_REMOVE; aodev->dev = dev; aodev->force = drs->force; if (dev->kind == LIBXL__DEVICE_KIND_VUSB) libxl__initiate_device_usbctrl_remove(egc, aodev); else libxl__initiate_device_generic_remove(egc, aodev); } } } out: libxl__multidev_prepared(egc, multidev, rc); } /* Callbacks for device related operations */ /* * device_backend_callback is the main callback entry point, for both device * addition and removal. It gets called if we reach the desired state * (XenbusStateClosed or XenbusStateInitWait). After that, all this * functions get called in the order displayed below. * * If new device types are added, they should only need to modify the * specific hotplug scripts call, which can be found in each OS specific * file. If this new devices don't need a hotplug script, no modification * should be needed. */ /* This callback is part of the Qemu devices Badge */ static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc); static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds, int rc); static void device_backend_cleanup(libxl__gc *gc, libxl__ao_device *aodev); static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev); static void device_hotplug_child_death_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status); static void device_destroy_be_watch_cb(libxl__egc *egc, libxl__xswait_state *xswait, int rc, const char *data); static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev); static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev); void libxl__wait_device_connection(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); char *be_path = libxl__device_backend_path(gc, aodev->dev); char *state_path = GCSPRINTF("%s/state", be_path); int rc = 0; if (QEMU_BACKEND(aodev->dev)) { /* * If Qemu is not running, there's no point in waiting for * it to change the state of the device. * * If Qemu is running, it will set the state of the device to * 4 directly, without waiting in state 2 for any hotplug execution. */ device_hotplug(egc, aodev); return; } rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds, device_backend_callback, state_path, XenbusStateInitWait, LIBXL_INIT_TIMEOUT * 1000); if (rc) { LOGD(ERROR, aodev->dev->domid, "unable to initialize device %s", be_path); goto out; } return; out: aodev->rc = rc; device_hotplug_done(egc, aodev); return; } void libxl__initiate_device_generic_remove(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); xs_transaction_t t = 0; char *be_path = libxl__device_backend_path(gc, aodev->dev); char *state_path = GCSPRINTF("%s/state", be_path); char *online_path = GCSPRINTF("%s/online", be_path); const char *state; libxl_dominfo info; uint32_t my_domid, domid = aodev->dev->domid; int rc = 0; libxl_dominfo_init(&info); rc = libxl__get_domid(gc, &my_domid); if (rc) { LOGD(ERROR, domid, "unable to get my domid"); goto out; } if (my_domid == LIBXL_TOOLSTACK_DOMID) { rc = libxl_domain_info(CTX, &info, domid); if (rc) { LOGD(ERROR, domid, "unable to get info for domain %d", domid); goto out; } if (QEMU_BACKEND(aodev->dev) && (info.paused || info.dying || info.shutdown)) { /* * TODO: 4.2 Bodge due to QEMU, see comment on top of * libxl__initiate_device_generic_remove in libxl_internal.h */ rc = libxl__ev_time_register_rel(ao, &aodev->timeout, device_qemu_timeout, LIBXL_QEMU_BODGE_TIMEOUT * 1000); if (rc) { LOGD(ERROR, domid, "unable to register timeout for Qemu device %s", be_path); goto out; } goto out_success; } } for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) { LOGD(ERROR, domid, "unable to start transaction"); goto out; } if (aodev->force) libxl__xs_path_cleanup(gc, t, libxl__device_frontend_path(gc, aodev->dev)); rc = libxl__xs_read_checked(gc, t, state_path, &state); if (rc) { LOGD(ERROR, domid, "unable to read device state from path %s", state_path); goto out; } rc = libxl__xs_write_checked(gc, t, online_path, "0"); if (rc) goto out; /* * Check if device is already in "closed" state, in which case * it should not be changed. */ if (state && atoi(state) != XenbusStateClosed) { rc = libxl__xs_write_checked(gc, t, state_path, GCSPRINTF("%d", XenbusStateClosing)); if (rc) { LOGD(ERROR, domid, "unable to write to xenstore path %s", state_path); goto out; } } rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds, device_backend_callback, state_path, XenbusStateClosed, LIBXL_DESTROY_TIMEOUT * 1000); if (rc) { LOGD(ERROR, domid, "unable to remove device %s", be_path); goto out; } out_success: libxl_dominfo_dispose(&info); return; out: aodev->rc = rc; libxl_dominfo_dispose(&info); libxl__xs_transaction_abort(gc, &t); device_hotplug_done(egc, aodev); return; } static void device_qemu_timeout(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc) { libxl__ao_device *aodev = CONTAINER_OF(ev, *aodev, timeout); STATE_AO_GC(aodev->ao); char *be_path = libxl__device_backend_path(gc, aodev->dev); char *state_path = GCSPRINTF("%s/state", be_path); const char *xs_state; xs_transaction_t t = 0; if (rc != ERROR_TIMEDOUT) goto out; libxl__ev_time_deregister(gc, &aodev->timeout); for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) { LOGD(ERROR, aodev->dev->domid, "unable to start transaction"); goto out; } /* * Check that the state path exists and is actually different than * 6 before unconditionally setting it. If Qemu runs on a driver * domain it is possible that the driver domain has already cleaned * the backend path if the device has reached state 6. */ rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &xs_state); if (rc) goto out; if (xs_state && atoi(xs_state) != XenbusStateClosed) { rc = libxl__xs_write_checked(gc, XBT_NULL, state_path, GCSPRINTF("%d", XenbusStateClosed)); if (rc) goto out; } rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } device_hotplug(egc, aodev); return; out: libxl__xs_transaction_abort(gc, &t); aodev->rc = rc; device_hotplug_done(egc, aodev); } static void device_backend_callback(libxl__egc *egc, libxl__ev_devstate *ds, int rc) { libxl__ao_device *aodev = CONTAINER_OF(ds, *aodev, backend_ds); STATE_AO_GC(aodev->ao); LOGD(DEBUG, aodev->dev->domid, "calling device_backend_cleanup"); device_backend_cleanup(gc, aodev); if (rc == ERROR_TIMEDOUT && aodev->action == LIBXL__DEVICE_ACTION_REMOVE && !aodev->force) { LOGD(DEBUG, aodev->dev->domid, "Timeout reached, initiating forced remove"); aodev->force = 1; libxl__initiate_device_generic_remove(egc, aodev); return; } if (rc) { LOGD(ERROR, aodev->dev->domid, "unable to %s device with path %s", libxl__device_action_to_string(aodev->action), libxl__device_backend_path(gc, aodev->dev)); goto out; } device_hotplug(egc, aodev); return; out: aodev->rc = rc; device_hotplug_done(egc, aodev); return; } static void device_backend_cleanup(libxl__gc *gc, libxl__ao_device *aodev) { if (!aodev) return; libxl__ev_devstate_cancel(gc, &aodev->backend_ds); } static void device_hotplug(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); libxl__async_exec_state *aes = &aodev->aes; char *be_path = libxl__device_backend_path(gc, aodev->dev); char **args = NULL, **env = NULL; int rc = 0; int hotplug, nullfd = -1; uint32_t domid; /* * If device is attached from a driver domain don't try to execute * hotplug scripts */ rc = libxl__get_domid(gc, &domid); if (rc) { LOGD(ERROR, aodev->dev->domid, "Failed to get domid"); goto out; } if (aodev->dev->backend_domid != domid) { LOGD(DEBUG, aodev->dev->domid, "Backend domid %d, domid %d, assuming driver domains", aodev->dev->backend_domid, domid); if (aodev->action != LIBXL__DEVICE_ACTION_REMOVE) { LOG(DEBUG, "Not a remove, not executing hotplug scripts"); goto out; } aodev->xswait.ao = ao; aodev->xswait.what = "removal of backend path"; aodev->xswait.path = be_path; aodev->xswait.timeout_ms = LIBXL_DESTROY_TIMEOUT * 1000; aodev->xswait.callback = device_destroy_be_watch_cb; rc = libxl__xswait_start(gc, &aodev->xswait); if (rc) { LOGD(ERROR, aodev->dev->domid, "Setup of backend removal watch failed (path %s)", be_path); goto out; } return; } /* Check if we have to execute hotplug scripts for this device * and return the necessary args/env vars for execution */ hotplug = libxl__get_hotplug_script_info(gc, aodev->dev, &args, &env, aodev->action, aodev->num_exec); switch (hotplug) { case 0: /* no hotplug script to execute */ LOGD(DEBUG, aodev->dev->domid, "No hotplug script to execute"); goto out; case 1: /* execute hotplug script */ break; default: /* everything else is an error */ LOGD(ERROR, aodev->dev->domid, "unable to get args/env to execute hotplug script for " "device %s", libxl__device_backend_path(gc, aodev->dev)); rc = hotplug; goto out; } assert(args != NULL); LOGD(DEBUG, aodev->dev->domid, "calling hotplug script: %s %s", args[0], args[1]); LOGD(DEBUG, aodev->dev->domid, "extra args:"); { const char *arg; unsigned int x; for (x = 2; (arg = args[x]); x++) LOGD(DEBUG, aodev->dev->domid, "\t%s", arg); } LOGD(DEBUG, aodev->dev->domid, "env:"); if (env != NULL) { const char *k, *v; unsigned int x; for (x = 0; (k = env[x]); x += 2) { v = env[x+1]; LOGD(DEBUG, aodev->dev->domid, "\t%s: %s", k, v); } } nullfd = open("/dev/null", O_RDONLY); if (nullfd < 0) { LOGD(ERROR, aodev->dev->domid, "unable to open /dev/null for hotplug script"); rc = ERROR_FAIL; goto out; } aes->ao = ao; aes->what = GCSPRINTF("%s %s", args[0], args[1]); aes->env = env; aes->args = args; aes->callback = device_hotplug_child_death_cb; aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000; aes->stdfds[0] = nullfd; aes->stdfds[1] = 2; aes->stdfds[2] = -1; rc = libxl__async_exec_start(aes); if (rc) goto out; close(nullfd); assert(libxl__async_exec_inuse(&aodev->aes)); return; out: if (nullfd >= 0) close(nullfd); aodev->rc = rc; device_hotplug_done(egc, aodev); return; } static void device_hotplug_child_death_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status) { libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes); STATE_AO_GC(aodev->ao); char *be_path = libxl__device_backend_path(gc, aodev->dev); char *hotplug_error; device_hotplug_clean(gc, aodev); if (status && !rc) { hotplug_error = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/hotplug-error", be_path)); if (hotplug_error) LOG(ERROR, "script: %s", hotplug_error); rc = ERROR_FAIL; } if (rc) { if (!aodev->rc) aodev->rc = rc; if (aodev->action == LIBXL__DEVICE_ACTION_ADD) /* * Only fail on device connection, on disconnection * ignore error, and continue with the remove process */ goto error; } /* Increase num_exec and call hotplug scripts again if necessary * If no more executions are needed, device_hotplug will call * device_hotplug_done breaking the loop. */ aodev->num_exec++; device_hotplug(egc, aodev); return; error: assert(aodev->rc); device_hotplug_done(egc, aodev); } static void device_destroy_be_watch_cb(libxl__egc *egc, libxl__xswait_state *xswait, int rc, const char *dir) { libxl__ao_device *aodev = CONTAINER_OF(xswait, *aodev, xswait); STATE_AO_GC(aodev->ao); if (rc) { if (rc == ERROR_TIMEDOUT) LOGD(ERROR, aodev->dev->domid, "timed out while waiting for %s to be removed", xswait->path); aodev->rc = rc; goto out; } if (dir) { /* backend path still exists, wait a little longer... */ return; } out: /* We are done, backend path no longer exists */ device_hotplug_done(egc, aodev); } static void device_hotplug_done(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); int rc; device_hotplug_clean(gc, aodev); /* Clean xenstore if it's a disconnection */ if (aodev->action == LIBXL__DEVICE_ACTION_REMOVE) { rc = libxl__device_destroy(gc, aodev->dev); if (!aodev->rc) aodev->rc = rc; } aodev->callback(egc, aodev); return; } static void device_hotplug_clean(libxl__gc *gc, libxl__ao_device *aodev) { /* Clean events and check reentrancy */ libxl__ev_time_deregister(gc, &aodev->timeout); libxl__xswait_stop(gc, &aodev->xswait); assert(!libxl__async_exec_inuse(&aodev->aes)); } static void devices_remove_callback(libxl__egc *egc, libxl__multidev *multidev, int rc) { libxl__devices_remove_state *drs = CONTAINER_OF(multidev, *drs, multidev); STATE_AO_GC(drs->ao); drs->callback(egc, drs, rc); return; } int libxl__wait_for_device_model_deprecated(libxl__gc *gc, uint32_t domid, char *state, libxl__spawn_starting *spawning, int (*check_callback)(libxl__gc *gc, uint32_t domid, const char *state, void *userdata), void *check_callback_userdata) { char *path; uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid); path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state"); return libxl__xenstore_child_wait_deprecated(gc, domid, LIBXL_DEVICE_MODEL_START_TIMEOUT, "Device Model", path, state, spawning, check_callback, check_callback_userdata); } int libxl__wait_for_backend(libxl__gc *gc, const char *be_path, const char *state) { int watchdog = 100; const char *p, *path = GCSPRINTF("%s/state", be_path); int rc; while (watchdog-- > 0) { rc = libxl__xs_read_checked(gc, XBT_NULL, path, &p); if (rc) return rc; if (p == NULL) { LOG(ERROR, "Backend %s does not exist", be_path); return ERROR_FAIL; } if (!strcmp(p, state)) return 0; usleep(100000); } LOG(ERROR, "Backend %s not ready", be_path); return ERROR_FAIL; } /* generic callback for devices that only need to set ao_complete */ void device_addrm_aocomplete(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); if (aodev->rc) { if (aodev->dev) { LOGD(ERROR, aodev->dev->domid, "Unable to %s %s with id %u", libxl__device_action_to_string(aodev->action), libxl__device_kind_to_string(aodev->dev->kind), aodev->dev->devid); } else { LOG(ERROR, "unable to %s device", libxl__device_action_to_string(aodev->action)); } goto out; } out: libxl__ao_complete(egc, ao, aodev->rc); return; } /* common function to get next device id */ int libxl__device_nextid(libxl__gc *gc, uint32_t domid, char *device) { char *libxl_dom_path, **l; unsigned int nb; int nextid = -1; if (!(libxl_dom_path = libxl__xs_libxl_path(gc, domid))) return nextid; l = libxl__xs_directory(gc, XBT_NULL, GCSPRINTF("%s/device/%s", libxl_dom_path, device), &nb); if (l == NULL || nb == 0) nextid = 0; else nextid = strtoul(l[nb - 1], NULL, 10) + 1; return nextid; } static void device_complete(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); LOG(DEBUG, "device %s %s %s", libxl__device_backend_path(gc, aodev->dev), libxl__device_action_to_string(aodev->action), aodev->rc ? "failed" : "succeed"); libxl__nested_ao_free(aodev->ao); } static void qdisk_spawn_outcome(libxl__egc *egc, libxl__dm_spawn_state *dmss, int rc) { STATE_AO_GC(dmss->spawn.ao); LOGD(DEBUG, dmss->guest_domid, "qdisk backend spawn %s", rc ? "failed" : "succeed"); libxl__nested_ao_free(dmss->spawn.ao); } /* * Data structures used to track devices handled by driver domains */ /* * Structure that describes a device handled by a driver domain */ typedef struct libxl__ddomain_device { libxl__device *dev; LIBXL_SLIST_ENTRY(struct libxl__ddomain_device) next; } libxl__ddomain_device; /* * Structure that describes a domain and it's associated devices */ typedef struct libxl__ddomain_guest { uint32_t domid; int num_vifs, num_vbds, num_qdisks; LIBXL_SLIST_HEAD(, struct libxl__ddomain_device) devices; LIBXL_SLIST_ENTRY(struct libxl__ddomain_guest) next; } libxl__ddomain_guest; /* * Main structure used by a driver domain to keep track of devices * currently in use */ typedef struct { libxl__ao *ao; libxl__ev_xswatch watch; LIBXL_SLIST_HEAD(, struct libxl__ddomain_guest) guests; } libxl__ddomain; static libxl__ddomain_guest *search_for_guest(libxl__ddomain *ddomain, uint32_t domid) { libxl__ddomain_guest *dguest; LIBXL_SLIST_FOREACH(dguest, &ddomain->guests, next) { if (dguest->domid == domid) return dguest; } return NULL; } static libxl__ddomain_device *search_for_device(libxl__ddomain_guest *dguest, libxl__device *dev) { libxl__ddomain_device *ddev; LIBXL_SLIST_FOREACH(ddev, &dguest->devices, next) { #define LIBXL_DEVICE_CMP(dev1, dev2, entry) (dev1->entry == dev2->entry) if (LIBXL_DEVICE_CMP(ddev->dev, dev, backend_devid) && LIBXL_DEVICE_CMP(ddev->dev, dev, backend_domid) && LIBXL_DEVICE_CMP(ddev->dev, dev, devid) && LIBXL_DEVICE_CMP(ddev->dev, dev, domid) && LIBXL_DEVICE_CMP(ddev->dev, dev, backend_kind) && LIBXL_DEVICE_CMP(ddev->dev, dev, kind)) return ddev; #undef LIBXL_DEVICE_CMP } return NULL; } static void check_and_maybe_remove_guest(libxl__gc *gc, libxl__ddomain *ddomain, libxl__ddomain_guest *dguest) { assert(ddomain); if (dguest != NULL && dguest->num_vifs + dguest->num_vbds + dguest->num_qdisks == 0) { LIBXL_SLIST_REMOVE(&ddomain->guests, dguest, libxl__ddomain_guest, next); LOGD(DEBUG, dguest->domid, "Removed domain from the list of active guests"); /* Clear any leftovers in libxl/ */ libxl__xs_rm_checked(gc, XBT_NULL, GCSPRINTF("libxl/%u", dguest->domid)); free(dguest); } } /* * The following comment applies to both add_device and remove_device. * * If the return value is greater than 0, it means there's no ao dispatched, * so the free of the nested ao should be done by the parent when it has * finished. */ static int add_device(libxl__egc *egc, libxl__ao *ao, libxl__ddomain_guest *dguest, libxl__device *dev) { AO_GC; libxl__ao_device *aodev; libxl__ddomain_device *ddev; libxl__dm_spawn_state *dmss; int rc = 0; /* * New device addition, allocate a struct to hold it and add it * to the list of active devices for a given guest. */ ddev = libxl__zalloc(NOGC, sizeof(*ddev)); ddev->dev = libxl__zalloc(NOGC, sizeof(*ddev->dev)); *ddev->dev = *dev; LIBXL_SLIST_INSERT_HEAD(&dguest->devices, ddev, next); LOGD(DEBUG, dev->domid, "Added device %s to the list of active devices", libxl__device_backend_path(gc, dev)); switch(dev->backend_kind) { case LIBXL__DEVICE_KIND_VBD: case LIBXL__DEVICE_KIND_VIF: if (dev->backend_kind == LIBXL__DEVICE_KIND_VBD) dguest->num_vbds++; if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) dguest->num_vifs++; GCNEW(aodev); libxl__prepare_ao_device(ao, aodev); /* * Clone the libxl__device to avoid races if remove_device is called * before the device addition has finished. */ GCNEW(aodev->dev); *aodev->dev = *dev; aodev->action = LIBXL__DEVICE_ACTION_ADD; aodev->callback = device_complete; libxl__wait_device_connection(egc, aodev); break; case LIBXL__DEVICE_KIND_QDISK: if (dguest->num_qdisks == 0) { GCNEW(dmss); dmss->guest_domid = dev->domid; dmss->spawn.ao = ao; dmss->callback = qdisk_spawn_outcome; libxl__spawn_qdisk_backend(egc, dmss); } dguest->num_qdisks++; break; default: rc = 1; break; } return rc; } static int remove_device(libxl__egc *egc, libxl__ao *ao, libxl__ddomain_guest *dguest, libxl__ddomain_device *ddev) { AO_GC; libxl__device *dev = ddev->dev; libxl__ao_device *aodev; int rc = 0; switch(ddev->dev->backend_kind) { case LIBXL__DEVICE_KIND_VBD: case LIBXL__DEVICE_KIND_VIF: if (dev->backend_kind == LIBXL__DEVICE_KIND_VBD) dguest->num_vbds--; if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) dguest->num_vifs--; GCNEW(aodev); libxl__prepare_ao_device(ao, aodev); /* * Clone the libxl__device to avoid races if there's a add_device * running in parallel. */ GCNEW(aodev->dev); *aodev->dev = *dev; aodev->action = LIBXL__DEVICE_ACTION_REMOVE; aodev->callback = device_complete; libxl__initiate_device_generic_remove(egc, aodev); break; case LIBXL__DEVICE_KIND_QDISK: if (--dguest->num_qdisks == 0) { rc = libxl__destroy_qdisk_backend(gc, dev->domid); if (rc) goto out; } libxl__device_destroy(gc, dev); /* Fall through to return > 0, no ao has been dispatched */ default: rc = 1; break; } /* * Removal of an active device, remove it from the list and * free it's data structures if they are no longer needed. * * NB: the freeing is safe because all the async ops launched * above or from add_device make a copy of the data they use, so * there's no risk of dereferencing. */ LIBXL_SLIST_REMOVE(&dguest->devices, ddev, libxl__ddomain_device, next); LOGD(DEBUG, dev->domid, "Removed device %s from the list of active devices", libxl__device_backend_path(gc, dev)); free(ddev->dev); free(ddev); out: return rc; } static void backend_watch_callback(libxl__egc *egc, libxl__ev_xswatch *watch, const char *watch_path, const char *event_path) { libxl__ddomain *ddomain = CONTAINER_OF(watch, *ddomain, watch); libxl__ao *nested_ao = libxl__nested_ao_create(ddomain->ao); STATE_AO_GC(nested_ao); char *p, *path; const char *sstate, *sonline; int state, online, rc; libxl__device *dev; libxl__ddomain_device *ddev = NULL; libxl__ddomain_guest *dguest = NULL; bool free_ao = false; /* Check if event_path ends with "state" or "online" and truncate it. */ path = libxl__strdup(gc, event_path); p = strrchr(path, '/'); if (p == NULL) goto skip; if (strcmp(p, "/state") != 0 && strcmp(p, "/online") != 0) goto skip; /* Truncate the string so it points to the backend directory. */ *p = '\0'; /* Fetch the value of the state and online nodes. */ rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/state", path), &sstate); if (rc || !sstate) goto skip; state = atoi(sstate); rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/online", path), &sonline); if (rc || !sonline) goto skip; online = atoi(sonline); GCNEW(dev); rc = libxl__parse_backend_path(gc, path, dev); if (rc) goto skip; dguest = search_for_guest(ddomain, dev->domid); if (dguest == NULL && state == XenbusStateClosed) { /* * Spurious state change, device has already been disconnected * or never attached. */ goto skip; } if (dguest == NULL) { /* Create a new guest struct and initialize it */ dguest = libxl__zalloc(NOGC, sizeof(*dguest)); dguest->domid = dev->domid; LIBXL_SLIST_INIT(&dguest->devices); LIBXL_SLIST_INSERT_HEAD(&ddomain->guests, dguest, next); LOGD(DEBUG, dguest->domid, "Added domain to the list of active guests"); } ddev = search_for_device(dguest, dev); if (ddev == NULL && state == XenbusStateClosed) { /* * Spurious state change, device has already been disconnected * or never attached. */ goto skip; } else if (ddev == NULL) { rc = add_device(egc, nested_ao, dguest, dev); if (rc > 0) free_ao = true; } else if (state == XenbusStateClosed && online == 0) { rc = remove_device(egc, nested_ao, dguest, ddev); if (rc > 0) free_ao = true; check_and_maybe_remove_guest(gc, ddomain, dguest); } if (free_ao) libxl__nested_ao_free(nested_ao); return; skip: libxl__nested_ao_free(nested_ao); check_and_maybe_remove_guest(gc, ddomain, dguest); return; } /* Handler of events for device driver domains */ int libxl_device_events_handler(libxl_ctx *ctx, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, 0, ao_how); int rc; uint32_t domid; libxl__ddomain ddomain; char *be_path; char **kinds = NULL, **domains = NULL, **devs = NULL; const char *sstate; char *state_path; int state; unsigned int nkinds, ndomains, ndevs; int i, j, k; ddomain.ao = ao; LIBXL_SLIST_INIT(&ddomain.guests); rc = libxl__get_domid(gc, &domid); if (rc) { LOG(ERROR, "unable to get domain id"); goto out; } /* * We use absolute paths because we want xswatch to also return * absolute paths that can be parsed by libxl__parse_backend_path. */ be_path = GCSPRINTF("/local/domain/%u/backend", domid); rc = libxl__ev_xswatch_register(gc, &ddomain.watch, backend_watch_callback, be_path); if (rc) goto out; kinds = libxl__xs_directory(gc, XBT_NULL, be_path, &nkinds); if (kinds) { for (i = 0; i < nkinds; i++) { domains = libxl__xs_directory(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, kinds[i]), &ndomains); if (!domains) continue; for (j = 0; j < ndomains; j++) { devs = libxl__xs_directory(gc, XBT_NULL, GCSPRINTF("%s/%s/%s", be_path, kinds[i], domains[j]), &ndevs); if (!devs) continue; for (k = 0; k < ndevs; k++) { state_path = GCSPRINTF("%s/%s/%s/%s/state", be_path, kinds[i], domains[j], devs[k]); rc = libxl__xs_read_checked(gc, XBT_NULL, state_path, &sstate); if (rc || !sstate) continue; state = atoi(sstate); if (state == XenbusStateInitWait) backend_watch_callback(egc, &ddomain.watch, be_path, state_path); } } } } return AO_INPROGRESS; out: return AO_CREATE_FAIL(rc); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_psr.c0000664000175000017500000002342413256712137015076 0ustar smbsmb/* * Copyright (C) 2014 Intel Corporation * Author Dongxiao Xu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include #define IA32_QM_CTR_ERROR_MASK (0x3ul << 62) static void libxl__psr_log_err_msg(libxl__gc *gc, int err) { char *msg; switch (err) { case ENOSYS: case EOPNOTSUPP: msg = "unsupported operation"; break; case ESRCH: msg = "invalid domain ID"; break; case ENOTSOCK: msg = "socket is not supported"; break; case EFAULT: msg = "failed to exchange data with Xen"; break; default: msg = "unknown error"; break; } LOGE(ERROR, "%s", msg); } static void libxl__psr_cmt_log_err_msg(libxl__gc *gc, int err) { char *msg; switch (err) { case ENODEV: msg = "CMT is not supported in this system"; break; case EEXIST: msg = "CMT is already attached to this domain"; break; case ENOENT: msg = "CMT is not attached to this domain"; break; case EOVERFLOW: msg = "no free RMID available"; break; default: libxl__psr_log_err_msg(gc, err); return; } LOGE(ERROR, "%s", msg); } static void libxl__psr_cat_log_err_msg(libxl__gc *gc, int err) { char *msg; switch (err) { case ENODEV: msg = "CAT is not supported in this system"; break; case ENOENT: msg = "CAT is not enabled on the socket"; break; case EOVERFLOW: msg = "no free COS available"; break; case EEXIST: msg = "The same CBM is already set to this domain"; break; case ENXIO: msg = "Unable to set code or data CBM when CDP is disabled"; break; default: libxl__psr_log_err_msg(gc, err); return; } LOGE(ERROR, "%s", msg); } static int libxl__pick_socket_cpu(libxl__gc *gc, uint32_t socketid) { int i, nr_cpus; libxl_cputopology *topology; int cpu = ERROR_FAIL; topology = libxl_get_cpu_topology(CTX, &nr_cpus); if (!topology) return ERROR_FAIL; for (i = 0; i < nr_cpus; i++) if (topology[i].socket == socketid) { cpu = i; break; } libxl_cputopology_list_free(topology, nr_cpus); return cpu; } int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid) { GC_INIT(ctx); int rc; rc = xc_psr_cmt_attach(ctx->xch, domid); if (rc < 0) { libxl__psr_cmt_log_err_msg(gc, errno); rc = ERROR_FAIL; } GC_FREE; return rc; } int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid) { GC_INIT(ctx); int rc; rc = xc_psr_cmt_detach(ctx->xch, domid); if (rc < 0) { libxl__psr_cmt_log_err_msg(gc, errno); rc = ERROR_FAIL; } GC_FREE; return rc; } int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid) { int rc; uint32_t rmid; rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid); if (rc < 0) return 0; return !!rmid; } int libxl_psr_cmt_enabled(libxl_ctx *ctx) { return xc_psr_cmt_enabled(ctx->xch); } int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid) { GC_INIT(ctx); int rc; rc = xc_psr_cmt_get_total_rmid(ctx->xch, total_rmid); if (rc < 0) { libxl__psr_cmt_log_err_msg(gc, errno); rc = ERROR_FAIL; } GC_FREE; return rc; } int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx, uint32_t socketid, uint32_t *l3_cache_size) { GC_INIT(ctx); int rc; int cpu = libxl__pick_socket_cpu(gc, socketid); if (cpu < 0) { LOGE(ERROR, "failed to get socket cpu"); rc = ERROR_FAIL; goto out; } rc = xc_psr_cmt_get_l3_cache_size(ctx->xch, cpu, l3_cache_size); if (rc < 0) { libxl__psr_cmt_log_err_msg(gc, errno); rc = ERROR_FAIL; } out: GC_FREE; return rc; } int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type) { GC_INIT(ctx); uint32_t event_mask; int rc; rc = xc_psr_cmt_get_l3_event_mask(ctx->xch, &event_mask); if (rc < 0) { libxl__psr_cmt_log_err_msg(gc, errno); rc = 0; } else { rc = event_mask & (1 << (type - 1)); } GC_FREE; return rc; } int libxl_psr_cmt_get_sample(libxl_ctx *ctx, uint32_t domid, libxl_psr_cmt_type type, uint64_t scope, uint64_t *sample_r, uint64_t *tsc_r) { GC_INIT(ctx); unsigned int rmid; uint32_t upscaling_factor; uint64_t monitor_data; int cpu, rc; rc = xc_psr_cmt_get_domain_rmid(ctx->xch, domid, &rmid); if (rc < 0 || rmid == 0) { LOGED(ERROR, domid, "fail to get the domain rmid, " "or domain is not attached with platform QoS monitoring service"); rc = ERROR_FAIL; goto out; } cpu = libxl__pick_socket_cpu(gc, scope); if (cpu < 0) { LOGED(ERROR, domid, "failed to get socket cpu"); rc = ERROR_FAIL; goto out; } rc = xc_psr_cmt_get_data(ctx->xch, rmid, cpu, type - 1, &monitor_data, tsc_r); if (rc < 0) { LOGED(ERROR, domid, "failed to get monitoring data"); rc = ERROR_FAIL; goto out; } rc = xc_psr_cmt_get_l3_upscaling_factor(ctx->xch, &upscaling_factor); if (rc < 0) { LOGED(ERROR, domid, "failed to get L3 upscaling factor"); rc = ERROR_FAIL; goto out; } *sample_r = monitor_data * upscaling_factor; out: GC_FREE; return rc; } int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx, uint32_t domid, uint32_t socketid, uint32_t *l3_cache_occupancy) { uint64_t data; int rc; rc = libxl_psr_cmt_get_sample(ctx, domid, LIBXL_PSR_CMT_TYPE_CACHE_OCCUPANCY, socketid, &data, NULL); if (rc < 0) goto out; *l3_cache_occupancy = data / 1024; out: return rc; } static inline xc_psr_cat_type libxl__psr_cbm_type_to_libxc_psr_cat_type( libxl_psr_cbm_type type) { BUILD_BUG_ON(sizeof(libxl_psr_cbm_type) != sizeof(xc_psr_cat_type)); return (xc_psr_cat_type)type; } int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid, libxl_psr_cbm_type type, libxl_bitmap *target_map, uint64_t cbm) { GC_INIT(ctx); int rc; int socketid, nr_sockets; rc = libxl__count_physical_sockets(gc, &nr_sockets); if (rc) { LOGED(ERROR, domid, "failed to get system socket count"); goto out; } libxl_for_each_set_bit(socketid, *target_map) { xc_psr_cat_type xc_type; if (socketid >= nr_sockets) break; xc_type = libxl__psr_cbm_type_to_libxc_psr_cat_type(type); if (xc_psr_cat_set_domain_data(ctx->xch, domid, xc_type, socketid, cbm)) { libxl__psr_cat_log_err_msg(gc, errno); rc = ERROR_FAIL; } } out: GC_FREE; return rc; } int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid, libxl_psr_cbm_type type, uint32_t target, uint64_t *cbm_r) { GC_INIT(ctx); int rc = 0; xc_psr_cat_type xc_type = libxl__psr_cbm_type_to_libxc_psr_cat_type(type); if (xc_psr_cat_get_domain_data(ctx->xch, domid, xc_type, target, cbm_r)) { libxl__psr_cat_log_err_msg(gc, errno); rc = ERROR_FAIL; } GC_FREE; return rc; } int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info, int *nr) { GC_INIT(ctx); int rc; int i = 0, socketid, nr_sockets; libxl_bitmap socketmap; libxl_psr_cat_info *ptr; libxl_bitmap_init(&socketmap); rc = libxl__count_physical_sockets(gc, &nr_sockets); if (rc) { LOGE(ERROR, "failed to get system socket count"); goto out; } libxl_socket_bitmap_alloc(ctx, &socketmap, nr_sockets); rc = libxl_get_online_socketmap(ctx, &socketmap); if (rc < 0) { LOGE(ERROR, "failed to get available sockets"); goto out; } ptr = libxl__malloc(NOGC, nr_sockets * sizeof(libxl_psr_cat_info)); libxl_for_each_set_bit(socketid, socketmap) { ptr[i].id = socketid; if (xc_psr_cat_get_l3_info(ctx->xch, socketid, &ptr[i].cos_max, &ptr[i].cbm_len, &ptr[i].cdp_enabled)) { libxl__psr_cat_log_err_msg(gc, errno); rc = ERROR_FAIL; free(ptr); goto out; } i++; } *info = ptr; *nr = i; out: libxl_bitmap_dispose(&socketmap); GC_FREE; return rc; } void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_psr_cat_info_dispose(&list[i]); free(list); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/osdeps.c0000664000175000017500000000327213256712137014374 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include #include #include #include #include #ifdef NEED_OWN_ASPRINTF int vasprintf(char **buffer, const char *fmt, va_list ap) { int size = 0; int nchars; *buffer = 0; nchars = vsnprintf(*buffer, 0, fmt, ap); if (nchars >= size) { char *tmpbuff; /* Reallocate buffer now that we know how much space is needed. */ size = nchars+1; tmpbuff = (char*)realloc(*buffer, size); if (tmpbuff == NULL) { /* we need to free it*/ free(*buffer); return -1; } *buffer=tmpbuff; /* Try again. */ nchars = vsnprintf(*buffer, size, fmt, ap); } if (nchars < 0) return nchars; return size; } int asprintf(char **buffer, char *fmt, ...) { int status; va_list ap; va_start (ap, fmt); status = vasprintf (buffer, fmt, ap); va_end (ap); return status; } #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/CODING_STYLE0000664000175000017500000002775613256712137014616 0ustar smbsmbLIBXENLIGHT CODING STYLE ======================== AN APOLOGY AND WARNING ---------------------- Much of the code in libxl does not yet follow this coding style document in every respect. However, new code is expected to conform. Patches to improve the style of existing code are welcome. Please separate these out from functional changes. If it is not feasible to conform fully to the style while patching old code, without doing substantial style reengineering first, we may accept patches which contain nonconformant elements, provided that they don't make the coding style problem worse overall. In this case, the new code should conform to the prevailing style in the area being touched. MEMORY ALLOCATION ----------------- Memory allocation for libxl-internal purposes should normally be done with the provided gc mechanisms; there is then no need to free. See "libxl memory management" in libxl.h. CONVENTIONAL VARIABLE NAMES --------------------------- The following local variable names should be used where applicable: int rc; /* a libxl error code - and not anything else */ int r; /* the return value from a system call (or libxc call) */ bool ok; /* the success return value from a boolean function */ uint32_t domid; libxl__gc *gc; libxl__egc *egc; libxl__ao *ao; libxl_foo_bar_state *fbs; /* local variable */ libxl_foo_bar_state foo_bar; /* inside another state struct */ CONVENIENCE MACROS ------------------ There are a number of convenience macros which shorten the program and avoid opportunity for mistakes. In some cases non-use of the macros produces functional bugs or incorrect error handling. Use the macros whenever they are applicable. For example: Usually, don't use: Instead, use (see libxl_internal.h): libxl__log[v] LOG, LOGE, LOGEV libxl__sprintf GCSPRINTF libxl__*alloc et al. GCNEW, GCNEW_ARRAY, GCREALLOC_ARRAY isalnum etc. directly CTYPE libxl__ctx_[un]lock CTX_LOCK, CTX_UNLOCK gc=...; ao=...; EGC_GC, AO_GC, STATE_AO_GC explicit gc creation GC_INIT, GC_FREE memset(..,0,sizeof..) FILLZERO Instead of malloc et al one should (as an exception to the above) use libxl__{zalloc,calloc,realloc} etc but passing NOGC. ERROR HANDLING -------------- Unless, there are good reasons to do otherwise, the following error handling and cleanup paradigm should be used: * All local variables referring to resources which might need cleaning up are declared at the top of the function, and initialised to a sentinel value indicating "nothing allocated". For example, libxl_evgen_disk_eject *evg = NULL; int nullfd = -1; * If the function is to return a libxl error value, `rc' is used to contain the error code, but it is NOT initialised: int rc; * There is only one error cleanup path out of the function. It starts with a label `out:'. That error cleanup path checks for each allocated resource and frees it iff necessary. It then returns rc. For example, out: if (evg) libxl__evdisable_disk_eject(gc, evg); if (nullfd >= 0) close(nullfd); return rc; * Function calls which might fail (ie most function calls) are handled by putting the return/status value into a variable, and then checking it in a separate statement: char *dompath = libxl__xs_get_dompath(gc, bl->domid); if (!dompath) { rc = ERROR_FAIL; goto out; } * If a resource is freed in the main body of the function (for example, in a loop), the corresponding variable has to be reset to the sentinel at the point where it's freed. Whether to use the `out' path for successful returns as well as error returns is a matter of taste and convenience for the specific function. Not reusing the out path is fine if the duplicated function exit code is only `CTX_UNLOCK; GC_FREE;' (or similar). If you reuse the `out' path for successful returns, there may be resources which are to be returned to the caller rather than freed. In that case you have to reset the local variable to `nothing here', to avoid the resource being freed on the out path. That resetting should be done immediately after the resource value is stored at the applicable _r function parameter (or equivalent). Do not test `rc' in the out section, to discover whether to free things. The uses of the single-line formatting in the examples above are permitted exceptions to the usual libxl code formatting rules. IDEMPOTENT DATA STRUCTURE CONSTRUCTION/DESTRUCTION -------------------------------------------------- Nontrivial data structures (in structs) should come with an idempotent _dispose function, which must free all resources associated with the data structure (but not free the struct itself). Such a struct should also come with an _init function which initialises the struct so that _dispose is a no-op. ASYNCHRONOUS/LONG-RUNNING OPERATIONS ------------------------------------ All long-running operations in libxl need to use the asynchronous operation machinery. Consult the programmer documentation in libxl_internal.h for details - search for "Machinery for asynchronous operations". The code for asynchronous operations should be laid out in chronological order. That is, where there is a chain of callback functions, each subsequent function should be, textually, the next function in the file. This will normally involve predeclaring the callback functions. Synchronous helper functions should be separated out into a section preceding the main callback chain. Control flow arrangements in asynchronous operations should be made as simple as possible, because it can otherwise be very hard to see through the tangle. When inventing a new sub-operation in asynchronous code, consider whether to structure it formally as a sub-operation with its own state structure. (See, for example, libxl__datacopier_*.) An ao-suboperation state structure should contain, in this order: * fields that the caller must fill in, and which are, effectively, the parameters to the operation, including: - libxl__ao *ao - the callback function pointer(s), which should be named callback or callback_*. * shared information fields or ones used for returning information to the calling operation * private fields These sections should be clearly demarcated by comments. An asynchronous operation should normally have an idempotent stop or cancel function. It should normally also have an _init function for its state struct, which arranges that the stop is a no-op. The permitted order of calls into your ao operation's methods must be documented in comments, if it is nontrivial. When using an ao sub-operation, you should normally: * Physically include the sub-operation state struct in your own state struct; * Use CONTAINER_OF to find your own state struct at the start of your implementations of the sub-operation callback functions; * Unconditionally initialise the sub-operation's struct (with its _init method) in your own _init method. * Unconditionally cancel or destroy the sub-operation in your own cancel or destroy method. FORMATTING AND NAMING --------------------- Blatantly copied from qemu and linux with few modifications. 1. Whitespace Of course, the most important aspect in any coding style is whitespace. Crusty old coders who have trouble spotting the glasses on their noses can tell the difference between a tab and eight spaces from a distance of approximately fifteen parsecs. Many a flamewar have been fought and lost on this issue. Libxenlight indents are four spaces. Tabs are never used, except in Makefiles where they have been irreversibly coded into the syntax. Spaces of course are superior to tabs because: - You have just one way to specify whitespace, not two. Ambiguity breeds mistakes. - The confusion surrounding 'use tabs to indent, spaces to justify' is gone. - Tab indents push your code to the right, making your screen seriously unbalanced. - Tabs will be rendered incorrectly on editors who are misconfigured not to use tab stops of eight positions. - Tabs are rendered badly in patches, causing off-by-one errors in almost every line. - It is the libxenlight coding style. Do not leave whitespace dangling off the ends of lines. 2. Line width Lines are limited to 75-80 characters. Rationale: - Some people like to tile their 24" screens with a 6x4 matrix of 80x24 xterms and use vi in all of them. The best way to punish them is to let them keep doing it. - Code and especially patches is much more readable if limited to a sane line length. Eighty is traditional. - It is the libxenlight coding style. 3. Naming C is a Spartan language, and so should your naming be. Unlike Modula-2 and Pascal programmers, C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable "tmp", which is much easier to write, and not the least more difficult to understand. HOWEVER, while mixed-case names are frowned upon, descriptive names for global variables are a must. To call a global function "foo" is a shooting offense. GLOBAL variables (to be used only if you _really_ need them) need to have descriptive names, as do global functions. If you have a function that counts the number of active users, you should call that "count_active_users()" or similar, you should _not_ call it "cntusr()". Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged - the compiler knows the types anyway and can check those, and it only confuses the programmer. LOCAL variable names should be short, and to the point. If you have some random integer loop counter, it should probably be called "i". Calling it "loop_counter" is non-productive, if there is no chance of it being mis-understood. Similarly, "tmp" can be just about any type of variable that is used to hold a temporary value. Local variables used to store return values should have descriptive name like "rc" or "ret". Following the same reasoning the label used as exit path should be called "out". Function arguments which are used to return values to the caller should be suffixed `_r' or `_out'. Variables, type names and function names are lower_case_with_underscores. Type names and function names use the prefix libxl__ when internal to libxenlight and libxl_ when exported in libxl.h. Xl should avoid using libxl_ and libxl__ as prefix for its own function names. When wrapping standard library functions, use the prefix libxl_ to alert readers that they are seeing a wrapped version; otherwise avoid this prefix. Typedefs are used to eliminate the redundant 'struct' keyword. It is the libxenlight coding style. 4. Statements Don't put multiple statements on a single line. Don't put multiple assignments on a single line either. Error code paths with an if statement and a goto or a return on the same line are allowed. Examples: if (rc) goto out; if (rc < 0) return; Libxenlight coding style is super simple. Avoid tricky expressions. 5. Block structure Every indented statement is braced, but blocks that contain just one statement may have the braces omitted. To avoid confusion, either all the blocks in an if...else chain have braces, or none of them do. The opening brace is on the line that contains the control flow statement that introduces the new block; the closing brace is on the same line as the else keyword, or on a line by itself if there is no else keyword. Examples: if (a == 5) { printf("a was 5.\n"); } else if (a == 6) { printf("a was 6.\n"); } else { printf("a was something else entirely.\n"); } if (a == 5) printf("a was 5.\n"); An exception is the opening brace for a function; for reasons of tradition and clarity it comes on a line by itself: void a_function(void) { do_something(); } Rationale: a consistent (except for functions...) bracing style reduces ambiguity and avoids needless churn when lines are added or removed. Furthermore, it is the libxenlight coding style. xen-4.9.2/tools/libxl/libxl_usb.c0000664000175000017500000017071013256712137015064 0ustar smbsmb/* * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. * Author Chunyan Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include #include #define USBBACK_INFO_PATH "/libxl/usbback" #define USBHUB_CLASS_CODE 9 static int usbback_is_loaded(libxl__gc *gc) { int r; struct stat st; r = lstat(SYSFS_USBBACK_DRIVER, &st); if (r == 0) return 1; if (r < 0 && errno == ENOENT) return 0; LOGE(ERROR, "Accessing %s", SYSFS_USBBACK_DRIVER); return ERROR_FAIL; } static int libxl__device_usbctrl_setdefault(libxl__gc *gc, uint32_t domid, libxl_device_usbctrl *usbctrl) { int rc; libxl_domain_type domtype = libxl__domain_type(gc, domid); if (usbctrl->type == LIBXL_USBCTRL_TYPE_AUTO) { if (domtype == LIBXL_DOMAIN_TYPE_PV) { rc = usbback_is_loaded(gc); if (rc < 0) goto out; usbctrl->type = rc ? LIBXL_USBCTRL_TYPE_PV : LIBXL_USBCTRL_TYPE_QUSB; } else if (domtype == LIBXL_DOMAIN_TYPE_HVM) { /* FIXME: See if we can detect PV frontend */ usbctrl->type = LIBXL_USBCTRL_TYPE_DEVICEMODEL; } } switch (usbctrl->type) { case LIBXL_USBCTRL_TYPE_PV: case LIBXL_USBCTRL_TYPE_QUSB: if (!usbctrl->version) usbctrl->version = 2; if (usbctrl->version < 1 || usbctrl->version > 2) { LOG(ERROR, "USB version for paravirtualized devices must be 1 or 2"); rc = ERROR_INVAL; goto out; } if (!usbctrl->ports) usbctrl->ports = 8; if (usbctrl->ports < 1 || usbctrl->ports > USBIF_MAX_PORTNR) { LOG(ERROR, "Number of ports for USB controller is limited to %u", USBIF_MAX_PORTNR); rc = ERROR_INVAL; goto out; } break; case LIBXL_USBCTRL_TYPE_DEVICEMODEL: if (!usbctrl->version) usbctrl->version = 2; switch (usbctrl->version) { case 1: /* uhci controller in qemu has fixed number of ports. */ if (usbctrl->ports && usbctrl->ports != 2) { LOG(ERROR, "Number of ports for USB controller of version 1 is always 2"); rc = ERROR_INVAL; goto out; } usbctrl->ports = 2; break; case 2: /* ehci controller in qemu has fixed number of ports. */ if (usbctrl->ports && usbctrl->ports != 6) { LOG(ERROR, "Number of ports for USB controller of version 2 is always 6"); rc = ERROR_INVAL; goto out; } usbctrl->ports = 6; break; case 3: if (!usbctrl->ports) usbctrl->ports = 8; /* xhci controller in qemu supports up to 15 ports. */ if (usbctrl->ports > 15) { LOG(ERROR, "Number of ports for USB controller of version 3 is limited to 15"); rc = ERROR_INVAL; goto out; } break; default: LOG(ERROR, "Illegal USB version"); rc = ERROR_INVAL; goto out; } break; default: break; } rc = libxl__resolve_domid(gc, usbctrl->backend_domname, &usbctrl->backend_domid); out: return rc; } static int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid, libxl_device_usbctrl *usbctrl, libxl__device *device) { device->backend_devid = usbctrl->devid; device->backend_domid = usbctrl->backend_domid; switch (usbctrl->type) { case LIBXL_USBCTRL_TYPE_PV: device->backend_kind = LIBXL__DEVICE_KIND_VUSB; break; case LIBXL_USBCTRL_TYPE_QUSB: device->backend_kind = LIBXL__DEVICE_KIND_QUSB; break; case LIBXL_USBCTRL_TYPE_DEVICEMODEL: device->backend_kind = LIBXL__DEVICE_KIND_NONE; break; default: assert(0); /* can't really happen. */ break; } device->devid = usbctrl->devid; device->domid = domid; device->kind = LIBXL__DEVICE_KIND_VUSB; return 0; } static const char *vusb_be_from_xs_libxl_type(libxl__gc *gc, const char *libxl_path, libxl_usbctrl_type type) { const char *be_path = NULL, *tmp; int r; if (type == LIBXL_USBCTRL_TYPE_AUTO) { r = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/type", libxl_path), &tmp); if (r || libxl_usbctrl_type_from_string(tmp, &type)) goto out; } if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) { be_path = libxl_path; goto out; } r = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/backend", libxl_path), &be_path); if (r) be_path = NULL; out: return be_path; } /* Add usbctrl information to xenstore. * * Adding a usb controller will add a new 'qusb' or 'vusb' device in xenstore, * and add corresponding frontend, backend information to it. According to * "update_json", decide whether to update json config file. */ static int libxl__device_usbctrl_add_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_usbctrl *usbctrl, bool update_json) { libxl__device *device; flexarray_t *front = NULL; flexarray_t *back; xs_transaction_t t = XBT_NULL; int i, rc; libxl_domain_config d_config; libxl_device_usbctrl usbctrl_saved; libxl__domain_userdata_lock *lock = NULL; libxl_domain_config_init(&d_config); libxl_device_usbctrl_init(&usbctrl_saved); libxl_device_usbctrl_copy(CTX, &usbctrl_saved, usbctrl); GCNEW(device); rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device); if (rc) goto out; back = flexarray_make(gc, 12, 1); if (device->backend_kind != LIBXL__DEVICE_KIND_NONE) { front = flexarray_make(gc, 4, 1); flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid)); flexarray_append_pair(back, "online", "1"); flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", usbctrl->backend_domid)); flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising)); } flexarray_append_pair(back, "type", (char *)libxl_usbctrl_type_to_string(usbctrl->type)); flexarray_append_pair(back, "usb-ver", GCSPRINTF("%d", usbctrl->version)); flexarray_append_pair(back, "num-ports", GCSPRINTF("%d", usbctrl->ports)); flexarray_append_pair(back, "port", ""); for (i = 0; i < usbctrl->ports; i++) flexarray_append_pair(back, GCSPRINTF("port/%d", i + 1), ""); if (update_json) { lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc) goto out; DEVICE_ADD(usbctrl, usbctrls, domid, &usbctrl_saved, COMPARE_USBCTRL, &d_config); rc = libxl__dm_check_start(gc, &d_config, domid); if (rc) goto out; if (usbctrl->type == LIBXL_USBCTRL_TYPE_QUSB) { if (!libxl__query_qemu_backend(gc, domid, usbctrl->backend_domid, "qusb", false)) { LOGD(ERROR, domid, "backend type not supported by device model"); rc = ERROR_FAIL; goto out; } } } for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; rc = libxl__device_exists(gc, t, device); if (rc < 0) goto out; if (rc == 1) { /* already exists in xenstore */ LOGD(ERROR, domid, "device already exists in xenstore"); rc = ERROR_DEVICE_EXISTS; goto out; } if (update_json) { rc = libxl__set_domain_configuration(gc, domid, &d_config); if (rc) goto out; } libxl__device_generic_add(gc, t, device, libxl__xs_kvs_of_flexarray(gc, back), libxl__xs_kvs_of_flexarray(gc, front), NULL); rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } out: libxl__xs_transaction_abort(gc, &t); if (lock) libxl__unlock_domain_userdata(lock); libxl_device_usbctrl_dispose(&usbctrl_saved); libxl_domain_config_dispose(&d_config); return rc; } static const char *vusb_be_from_xs_libxl(libxl__gc *gc, const char *libxl_path) { return vusb_be_from_xs_libxl_type(gc, libxl_path, LIBXL_USBCTRL_TYPE_AUTO); } static void libxl__device_usbctrl_del_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_usbctrl *usbctrl) { const char *libxl_path, *be_path; xs_transaction_t t = XBT_NULL; int rc; libxl_path = GCSPRINTF("%s/device/vusb/%d", libxl__xs_libxl_path(gc, domid), usbctrl->devid); be_path = vusb_be_from_xs_libxl_type(gc, libxl_path, usbctrl->type); for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; libxl__xs_path_cleanup(gc, t, be_path); rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } return; out: libxl__xs_transaction_abort(gc, &t); } static char *pvusb_get_device_type(libxl_usbctrl_type type) { switch (type) { case LIBXL_USBCTRL_TYPE_PV: return "vusb"; case LIBXL_USBCTRL_TYPE_QUSB: return "qusb"; default: return NULL; } } /* Send qmp commands to create a usb controller in qemu. * * Depending on the speed (usbctrl->version) we create: * - piix3-usb-uhci (version=1), always 2 ports * - usb-ehci (version=2), always 6 ports * - nec-usb-xhci (version=3), up to 15 ports */ static int libxl__device_usbctrl_add_hvm(libxl__gc *gc, uint32_t domid, libxl_device_usbctrl *usbctrl) { flexarray_t *qmp_args; qmp_args = flexarray_make(gc, 8, 1); switch (usbctrl->version) { case 1: flexarray_append_pair(qmp_args, "driver", "piix3-usb-uhci"); break; case 2: flexarray_append_pair(qmp_args, "driver", "usb-ehci"); break; case 3: flexarray_append_pair(qmp_args, "driver", "nec-usb-xhci"); flexarray_append_pair(qmp_args, "p2", GCSPRINTF("%d", usbctrl->ports)); flexarray_append_pair(qmp_args, "p3", GCSPRINTF("%d", usbctrl->ports)); break; default: assert(0); /* Should not be possible. */ break; } flexarray_append_pair(qmp_args, "id", GCSPRINTF("xenusb-%d", usbctrl->devid)); return libxl__qmp_run_command_flexarray(gc, domid, "device_add", qmp_args); } /* Send qmp commands to delete a usb controller in qemu. */ static int libxl__device_usbctrl_del_hvm(libxl__gc *gc, uint32_t domid, int devid) { flexarray_t *qmp_args; qmp_args = flexarray_make(gc, 2, 1); flexarray_append_pair(qmp_args, "id", GCSPRINTF("xenusb-%d", devid)); return libxl__qmp_run_command_flexarray(gc, domid, "device_del", qmp_args); } /* Send qmp commands to create a usb device in qemu. */ static int libxl__device_usbdev_add_hvm(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev) { flexarray_t *qmp_args; qmp_args = flexarray_make(gc, 12, 1); flexarray_append_pair(qmp_args, "id", GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus, usbdev->u.hostdev.hostaddr)); flexarray_append_pair(qmp_args, "driver", "usb-host"); flexarray_append_pair(qmp_args, "bus", GCSPRINTF("xenusb-%d.0", usbdev->ctrl)); flexarray_append_pair(qmp_args, "port", GCSPRINTF("%d", usbdev->port)); flexarray_append_pair(qmp_args, "hostbus", GCSPRINTF("%d", usbdev->u.hostdev.hostbus)); flexarray_append_pair(qmp_args, "hostaddr", GCSPRINTF("%d", usbdev->u.hostdev.hostaddr)); return libxl__qmp_run_command_flexarray(gc, domid, "device_add", qmp_args); } /* Send qmp commands to delete a usb device in qemu. */ static int libxl__device_usbdev_del_hvm(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev) { flexarray_t *qmp_args; qmp_args = flexarray_make(gc, 2, 1); flexarray_append_pair(qmp_args, "id", GCSPRINTF("xenusb-%d-%d", usbdev->u.hostdev.hostbus, usbdev->u.hostdev.hostaddr)); return libxl__qmp_run_command_flexarray(gc, domid, "device_del", qmp_args); } /* AO operation to add a usb controller. * * Generally, it does: * 1) fill in necessary usb controler information with default value * 2) write usb controller frontend/backend info to xenstore, update json * config file if necessary. * 3) wait for device connection. PVUSB frontend and backend driver will * probe xenstore paths and build connection between frontend and backend. * * Before calling this function, aodev should be properly filled: * aodev->ao, aodev->callback, aodev->update_json, ... */ static void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid, libxl_device_usbctrl *usbctrl, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); libxl__device *device; int rc; rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl); if (rc < 0) goto out; if (usbctrl->devid == -1) { usbctrl->devid = libxl__device_nextid(gc, domid, "vusb"); if (usbctrl->devid < 0) { rc = ERROR_FAIL; goto out; } } rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl, aodev->update_json); if (rc) goto out; GCNEW(device); rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device); if (rc) goto outrm; if (device->backend_kind == LIBXL__DEVICE_KIND_NONE) { rc = libxl__device_usbctrl_add_hvm(gc, domid, usbctrl); if (rc) goto outrm; goto out; } aodev->dev = device; aodev->action = LIBXL__DEVICE_ACTION_ADD; libxl__wait_device_connection(egc, aodev); return; outrm: libxl__device_usbctrl_del_xenstore(gc, domid, usbctrl); out: aodev->rc = rc; aodev->callback(egc, aodev); return; } LIBXL_DEFINE_DEVICE_ADD(usbctrl) static LIBXL_DEFINE_DEVICES_ADD(usbctrl) LIBXL_DEFINE_DEVICE_REMOVE_CUSTOM(usbctrl) static int libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, uint32_t domid, libxl_devid usbctrl, libxl_device_usbdev **usbdevs, int *num); static int libxl__device_usbdev_remove(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev); /* AO function to remove a usb controller. * * Generally, it does: * 1) check if the usb controller exists or not * 2) remove all usb devices under controller * 3) remove usb controller information from xenstore * * Before calling this function, aodev should be properly filled: * aodev->ao, aodev->dev, aodev->callback, ... */ void libxl__initiate_device_usbctrl_remove(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); libxl_device_usbdev *usbdevs = NULL; int num_usbdev = 0; int i, rc; uint32_t domid = ao->domid; int usbctrl_devid = aodev->dev->devid; libxl_device_usbctrl usbctrl; libxl_usbctrlinfo usbctrlinfo; libxl_device_usbctrl_init(&usbctrl); libxl_usbctrlinfo_init(&usbctrlinfo); usbctrl.devid = usbctrl_devid; rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); if (rc) goto out; /* Remove usb devices first */ rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, usbctrl_devid, &usbdevs, &num_usbdev); if (rc) goto out; for (i = 0; i < num_usbdev; i++) { rc = libxl__device_usbdev_remove(gc, domid, &usbdevs[i]); if (rc) { LOGD(ERROR, domid, "libxl__device_usbdev_remove failed: controller %d, " "port %d", usbdevs[i].ctrl, usbdevs[i].port); goto out; } } if (usbctrlinfo.type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) { rc = libxl__device_usbctrl_del_hvm(gc, domid, usbctrl_devid); if (!rc) libxl__device_usbctrl_del_xenstore(gc, domid, &usbctrl); goto out; } libxl_device_usbctrl_dispose(&usbctrl); libxl_usbctrlinfo_dispose(&usbctrlinfo); /* Remove usbctrl */ libxl__initiate_device_generic_remove(egc, aodev); return; out: libxl_device_usbctrl_dispose(&usbctrl); libxl_usbctrlinfo_dispose(&usbctrlinfo); aodev->rc = rc; aodev->callback(egc, aodev); return; } libxl_device_usbctrl * libxl_device_usbctrl_list(libxl_ctx *ctx, uint32_t domid, int *num) { GC_INIT(ctx); libxl_device_usbctrl *usbctrls = NULL; char *libxl_vusbs_path = NULL; char **entry = NULL; unsigned int nentries = 0; *num = 0; libxl_vusbs_path = GCSPRINTF("%s/device/vusb", libxl__xs_libxl_path(gc, domid)); entry = libxl__xs_directory(gc, XBT_NULL, libxl_vusbs_path, &nentries); if (entry && nentries) { usbctrls = libxl__zalloc(NOGC, sizeof(*usbctrls) * nentries); libxl_device_usbctrl *usbctrl; libxl_device_usbctrl *end = usbctrls + nentries; for (usbctrl = usbctrls; usbctrl < end; usbctrl++, entry++, (*num)++) { const char *tmp, *be_path, *libxl_path; int ret; libxl_device_usbctrl_init(usbctrl); usbctrl->devid = atoi(*entry); #define READ_SUBPATH(path, subpath) ({ \ ret = libxl__xs_read_checked(gc, XBT_NULL, \ GCSPRINTF("%s/" subpath, path), \ &tmp); \ if (ret) goto out; \ (char *)tmp; \ }) #define READ_SUBPATH_INT(path, subpath) ({ \ ret = libxl__xs_read_checked(gc, XBT_NULL, \ GCSPRINTF("%s/" subpath, path), \ &tmp); \ if (ret) goto out; \ tmp ? atoi(tmp) : -1; \ }) libxl_path = GCSPRINTF("%s/%s", libxl_vusbs_path, *entry); libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"), &usbctrl->type); if (usbctrl->type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) { be_path = libxl_path; ret = libxl__get_domid(gc, &usbctrl->backend_domid); } else { be_path = READ_SUBPATH(libxl_path, "backend"); if (!be_path) goto out; ret = libxl__backendpath_parse_domid(gc, be_path, &usbctrl->backend_domid); } if (ret) goto out; usbctrl->version = READ_SUBPATH_INT(be_path, "usb-ver"); usbctrl->ports = READ_SUBPATH_INT(be_path, "num-ports"); #undef READ_SUBPATH #undef READ_SUBPATH_INT } } GC_FREE; return usbctrls; out: LOGD(ERROR, domid, "Unable to list USB Controllers"); libxl_device_usbctrl_list_free(usbctrls, *num); GC_FREE; *num = 0; return NULL; } int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_usbctrl *usbctrl, libxl_usbctrlinfo *usbctrlinfo) { GC_INIT(ctx); const char *dompath, *fe_path, *be_path, *tmp; const char *libxl_dom_path, *libxl_path; int rc; usbctrlinfo->devid = usbctrl->devid; #define READ_SUBPATH(path, subpath) ({ \ rc = libxl__xs_read_mandatory(gc, XBT_NULL, \ GCSPRINTF("%s/" subpath, path), \ &tmp); \ if (rc) goto out; \ (char *)tmp; \ }) #define READ_SUBPATH_INT(path, subpath) ({ \ rc = libxl__xs_read_checked(gc, XBT_NULL, \ GCSPRINTF("%s/" subpath, path), \ &tmp); \ if (rc) goto out; \ tmp ? atoi(tmp) : -1; \ }) libxl_dom_path = libxl__xs_libxl_path(gc, domid); libxl_path = GCSPRINTF("%s/device/vusb/%d", libxl_dom_path, usbctrl->devid); libxl_usbctrl_type_from_string(READ_SUBPATH(libxl_path, "type"), &usbctrlinfo->type); if (usbctrlinfo->type != LIBXL_USBCTRL_TYPE_DEVICEMODEL) { dompath = libxl__xs_get_dompath(gc, domid); fe_path = GCSPRINTF("%s/device/vusb/%d", dompath, usbctrl->devid); be_path = READ_SUBPATH(libxl_path, "backend"); usbctrlinfo->backend = libxl__strdup(NOGC, be_path); rc = libxl__backendpath_parse_domid(gc, be_path, &usbctrl->backend_domid); if (rc) goto out; usbctrlinfo->state = READ_SUBPATH_INT(fe_path, "state"); usbctrlinfo->evtch = READ_SUBPATH_INT(fe_path, "event-channel"); usbctrlinfo->ref_urb = READ_SUBPATH_INT(fe_path, "urb-ring-ref"); usbctrlinfo->ref_conn = READ_SUBPATH_INT(fe_path, "urb-ring-ref"); usbctrlinfo->frontend = libxl__strdup(NOGC, fe_path); usbctrlinfo->frontend_id = domid; usbctrlinfo->ports = READ_SUBPATH_INT(be_path, "num-ports"); usbctrlinfo->version = READ_SUBPATH_INT(be_path, "usb-ver"); } else { usbctrlinfo->ports = READ_SUBPATH_INT(libxl_path, "num-ports"); usbctrlinfo->version = READ_SUBPATH_INT(libxl_path, "usb-ver"); rc = libxl__get_domid(gc, &usbctrl->backend_domid); if (rc) goto out; } #undef READ_SUBPATH #undef READ_SUBPATH_INT rc = 0; out: GC_FREE; return rc; } int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, uint32_t domid, int devid, libxl_device_usbctrl *usbctrl) { libxl_device_usbctrl *usbctrls; int nb = 0; int i, rc; usbctrls = libxl_device_usbctrl_list(ctx, domid, &nb); if (!usbctrls) return ERROR_FAIL; rc = ERROR_FAIL; for (i = 0; i < nb; i++) { if (devid == usbctrls[i].devid) { libxl_device_usbctrl_copy(ctx, usbctrl, &usbctrls[i]); rc = 0; break; } } libxl_device_usbctrl_list_free(usbctrls, nb); return rc; } static char *usbdev_busaddr_to_busid(libxl__gc *gc, int bus, int addr) { DIR *dir; char *busid = NULL; struct dirent *de; /* invalid hostbus or hostaddr */ if (bus < 1 || addr < 1) return NULL; dir = opendir(SYSFS_USB_DEV); if (!dir) { LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV); return NULL; } for (;;) { char *filename; void *buf; int busnum = -1; int devnum = -1; errno = 0; de = readdir(dir); if (!de && errno) { LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV); break; } if (!de) break; if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", de->d_name); if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) devnum = atoi(buf); filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", de->d_name); if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) busnum = atoi(buf); if (bus == busnum && addr == devnum) { busid = libxl__strdup(gc, de->d_name); break; } } closedir(dir); return busid; } static int usbdev_busaddr_from_busid(libxl__gc *gc, const char *busid, uint8_t *bus, uint8_t *addr) { char *filename; void *buf; filename = GCSPRINTF(SYSFS_USB_DEV "/%s/busnum", busid); if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) *bus = atoi(buf); else return ERROR_FAIL; filename = GCSPRINTF(SYSFS_USB_DEV "/%s/devnum", busid); if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) *addr = atoi(buf); else return ERROR_FAIL; return 0; } static int get_assigned_devices(libxl__gc *gc, libxl_device_usbdev **list, int *num) { char **domlist; unsigned int ndom = 0; int i, j, k; int rc; *list = NULL; *num = 0; domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &ndom); for (i = 0; i < ndom; i++) { char *libxl_vusbs_path; char **usbctrls; unsigned int nc = 0; uint32_t domid = atoi(domlist[i]); libxl_vusbs_path = GCSPRINTF("%s/device/vusb", libxl__xs_libxl_path(gc, domid)); usbctrls = libxl__xs_directory(gc, XBT_NULL, libxl_vusbs_path, &nc); for (j = 0; j < nc; j++) { libxl_device_usbdev *tmp = NULL; int nd = 0; rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, atoi(usbctrls[j]), &tmp, &nd); if (rc) goto out; if (!nd) continue; GCREALLOC_ARRAY(*list, *num + nd); for (k = 0; k < nd; k++) { libxl_device_usbdev_copy(CTX, *list + *num, tmp + k); (*num)++; } } } return 0; out: LOG(ERROR, "fail to get assigned devices"); return rc; } static bool is_usbdev_in_array(libxl_device_usbdev *usbdevs, int num, libxl_device_usbdev *usbdev) { int i; for (i = 0; i < num; i++) { if (usbdevs[i].u.hostdev.hostbus == usbdev->u.hostdev.hostbus && usbdevs[i].u.hostdev.hostaddr == usbdev->u.hostdev.hostaddr) return true; } return false; } /* check if USB device type is assignable */ static bool is_usbdev_assignable(libxl__gc *gc, libxl_device_usbdev *usbdev) { int classcode; char *filename; void *buf = NULL; char *busid = NULL; busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus, usbdev->u.hostdev.hostaddr); if (!busid) return false; filename = GCSPRINTF(SYSFS_USB_DEV "/%s/bDeviceClass", busid); if (libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) return false; classcode = atoi(buf); return classcode != USBHUB_CLASS_CODE; } /* get usb devices under certain usb controller */ static int libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, uint32_t domid, libxl_devid usbctrl, libxl_device_usbdev **usbdevs, int *num) { const char *libxl_path, *be_path, *num_devs; int n, i, rc; *usbdevs = NULL; *num = 0; libxl_path = GCSPRINTF("%s/device/vusb/%d", libxl__xs_libxl_path(gc, domid), usbctrl); be_path = vusb_be_from_xs_libxl(gc, libxl_path); if (!be_path) { rc = ERROR_FAIL; goto out; } rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/num-ports", be_path), &num_devs); if (rc) goto out; n = num_devs ? atoi(num_devs) : 0; for (i = 0; i < n; i++) { const char *busid; libxl_device_usbdev *usbdev; rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/port/%d", be_path, i + 1), &busid); if (rc) goto out; if (busid && strcmp(busid, "")) { GCREALLOC_ARRAY(*usbdevs, *num + 1); usbdev = *usbdevs + *num; (*num)++; libxl_device_usbdev_init(usbdev); usbdev->ctrl = usbctrl; usbdev->port = i + 1; usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV; rc = usbdev_busaddr_from_busid(gc, busid, &usbdev->u.hostdev.hostbus, &usbdev->u.hostdev.hostaddr); if (rc) goto out; } } rc = 0; out: return rc; } /* get all usb devices of the domain */ libxl_device_usbdev * libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num) { GC_INIT(ctx); libxl_device_usbdev *usbdevs = NULL; const char *libxl_vusbs_path; char **usbctrls; unsigned int nc = 0; int i, j; *num = 0; libxl_vusbs_path = GCSPRINTF("%s/device/vusb", libxl__xs_libxl_path(gc, domid)); usbctrls = libxl__xs_directory(gc, XBT_NULL, libxl_vusbs_path, &nc); for (i = 0; i < nc; i++) { int rc, nd = 0; libxl_device_usbdev *tmp = NULL; rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, atoi(usbctrls[i]), &tmp, &nd); if (rc || !nd) continue; usbdevs = libxl__realloc(NOGC, usbdevs, sizeof(*usbdevs) * (*num + nd)); for (j = 0; j < nd; j++) { libxl_device_usbdev_copy(ctx, usbdevs + *num, tmp + j); (*num)++; } } GC_FREE; return usbdevs; } static char *vusb_get_port_path(libxl__gc *gc, uint32_t domid, libxl_usbctrl_type type, int ctrl, int port) { char *path; if (type == LIBXL_USBCTRL_TYPE_DEVICEMODEL) path = GCSPRINTF("%s/device/vusb", libxl__xs_libxl_path(gc, domid)); else path = GCSPRINTF("%s/backend/%s/%d", libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID), pvusb_get_device_type(type), domid); return GCSPRINTF("%s/%d/port/%d", path, ctrl, port); } /* find first unused controller:port and give that to usb device */ static int libxl__device_usbdev_set_default_usbctrl(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev) { libxl_device_usbctrl *usbctrls = NULL; int numctrl = 0; int i, j, rc; usbctrls = libxl_device_usbctrl_list(CTX, domid, &numctrl); if (!numctrl || !usbctrls) { rc = ERROR_FAIL; goto out; } for (i = 0; i < numctrl; i++) { for (j = 0; j < usbctrls[i].ports; j++) { const char *path, *tmp; path = vusb_get_port_path(gc, domid, usbctrls[i].type, usbctrls[i].devid, j + 1); rc = libxl__xs_read_checked(gc, XBT_NULL, path, &tmp); if (rc) goto out; if (tmp && !strcmp(tmp, "")) { usbdev->ctrl = usbctrls[i].devid; usbdev->port = j + 1; rc = 0; goto out; } } } /* no available controller:port */ rc = ERROR_FAIL; out: libxl_device_usbctrl_list_free(usbctrls, numctrl); return rc; } /* Fill in usb information with default value. * * Generally, it does: * 1) if "controller" is not specified: * - if "port" is not specified, try to find an available controller:port, * if found, use that; otherwise, create a new controller, use this * controller and its first port * - if "port" is specified, report error. * 2) if "controller" is specified, but port is not specified: * try to find an available port under this controller, if found, use * that, otherwise, report error. * 3) if both "controller" and "port" are specified: * check the controller:port is available, if not, report error. */ static int libxl__device_usbdev_setdefault(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev, bool update_json) { int rc; if (!usbdev->type) usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV; if (usbdev->ctrl == -1) { if (usbdev->port) { LOGD(ERROR, domid, "USB controller must be specified if you specify port"); return ERROR_INVAL; } rc = libxl__device_usbdev_set_default_usbctrl(gc, domid, usbdev); /* If no existing controller to host this usb device, add a new one */ if (rc) { libxl_device_usbctrl *usbctrl; GCNEW(usbctrl); libxl_device_usbctrl_init(usbctrl); rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl); if (rc < 0) goto out; if (usbctrl->devid == -1) { usbctrl->devid = libxl__device_nextid(gc, domid, "vusb"); if (usbctrl->devid < 0) { rc = ERROR_FAIL; goto out; } } rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl, update_json); if (rc) goto out; usbdev->ctrl = usbctrl->devid; usbdev->port = 1; } } else { /* A controller was specified; look it up */ const char *libxl_path, *be_path, *tmp; libxl_path = GCSPRINTF("%s/device/vusb/%d", libxl__xs_libxl_path(gc, domid), usbdev->ctrl); be_path = vusb_be_from_xs_libxl(gc, libxl_path); if (!be_path) { rc = ERROR_FAIL; goto out; } if (usbdev->port) { /* A specific port was requested; see if it's available */ rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/port/%d", be_path, usbdev->port), &tmp); if (rc) goto out; if (tmp && strcmp(tmp, "")) { LOGD(ERROR, domid, "The controller port isn't available"); rc = ERROR_FAIL; goto out; } } else { /* No port was requested. Choose free port. */ int i, ports; rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/num-ports", be_path), &tmp); if (rc) goto out; ports = tmp ? atoi(tmp) : 0; for (i = 0; i < ports; i++) { rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/port/%d", be_path, i + 1), &tmp); if (rc) goto out; if (tmp && !strcmp(tmp, "")) { usbdev->port = i + 1; break; } } if (!usbdev->port) { LOGD(ERROR, domid, "No available port under specified controller"); rc = ERROR_FAIL; goto out; } } } rc = 0; out: return rc; } /* Add usb information to xenstore * * Adding a usb device won't create new 'qusb'/'vusb' device, but only write * the device busid to the controller:port in xenstore. */ static int libxl__device_usbdev_add_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev, libxl_usbctrl_type type, bool update_json) { char *be_path, *busid; int rc; xs_transaction_t t = XBT_NULL; libxl_domain_config d_config; libxl_device_usbdev usbdev_saved; libxl__domain_userdata_lock *lock = NULL; libxl_domain_config_init(&d_config); libxl_device_usbdev_init(&usbdev_saved); libxl_device_usbdev_copy(CTX, &usbdev_saved, usbdev); busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus, usbdev->u.hostdev.hostaddr); if (!busid) { LOGD(DEBUG, domid, "Fail to get busid of usb device"); rc = ERROR_FAIL; goto out; } if (update_json) { lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc) goto out; DEVICE_ADD(usbdev, usbdevs, domid, &usbdev_saved, COMPARE_USB, &d_config); rc = libxl__dm_check_start(gc, &d_config, domid); if (rc) goto out; } for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; if (update_json) { rc = libxl__set_domain_configuration(gc, domid, &d_config); if (rc) goto out; } be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl, usbdev->port); LOGD(DEBUG, domid, "Adding usb device %s to xenstore: controller %d, port %d", busid, usbdev->ctrl, usbdev->port); rc = libxl__xs_write_checked(gc, t, be_path, busid); if (rc) goto out; rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } rc = 0; out: if (lock) libxl__unlock_domain_userdata(lock); libxl_device_usbdev_dispose(&usbdev_saved); libxl_domain_config_dispose(&d_config); return rc; } static int libxl__device_usbdev_remove_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev, libxl_usbctrl_type type) { char *be_path; be_path = vusb_get_port_path(gc, domid, type, usbdev->ctrl, usbdev->port); LOGD(DEBUG, domid, "Removing usb device from xenstore: controller %d, port %d", usbdev->ctrl, usbdev->port); return libxl__xs_write_checked(gc, XBT_NULL, be_path, ""); } static char *usbdev_busid_from_ctrlport(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev, libxl_usbctrl_type type) { return libxl__xs_read(gc, XBT_NULL, vusb_get_port_path(gc, domid, type, usbdev->ctrl, usbdev->port)); } /* get original driver path of usb interface, stored in @drvpath */ static int usbintf_get_drvpath(libxl__gc *gc, const char *intf, char **drvpath) { char *spath, *dp = NULL; spath = GCSPRINTF(SYSFS_USB_DEV "/%s/driver", intf); /* Find the canonical path to the driver. */ dp = libxl__zalloc(gc, PATH_MAX); dp = realpath(spath, dp); if (!dp && errno != ENOENT) { LOGE(ERROR, "get realpath failed: '%s'", spath); return ERROR_FAIL; } *drvpath = dp; return 0; } static int unbind_usbintf(libxl__gc *gc, const char *intf) { char *path; int fd = -1; int rc; path = GCSPRINTF(SYSFS_USB_DEV "/%s/driver/unbind", intf); fd = open(path, O_WRONLY); if (fd < 0) { LOGE(ERROR, "open file failed: '%s'", path); rc = ERROR_FAIL; goto out; } if (libxl_write_exactly(CTX, fd, intf, strlen(intf), path, intf)) { rc = ERROR_FAIL; goto out; } rc = 0; out: if (fd >= 0) close(fd); return rc; } static int bind_usbintf(libxl__gc *gc, const char *intf, const char *drvpath) { char *bind_path, *intf_path; struct stat st; int fd = -1; int rc, r; intf_path = GCSPRINTF("%s/%s", drvpath, intf); /* check through lstat, if intf already exists under drvpath, * it's already bound, return directly; if it doesn't exist, * continue to do bind work; otherwise, return error. */ r = lstat(intf_path, &st); if (r == 0) return 0; if (r < 0 && errno != ENOENT) return ERROR_FAIL; bind_path = GCSPRINTF("%s/bind", drvpath); fd = open(bind_path, O_WRONLY); if (fd < 0) { LOGE(ERROR, "open file failed: '%s'", bind_path); rc = ERROR_FAIL; goto out; } if (libxl_write_exactly(CTX, fd, intf, strlen(intf), bind_path, intf)) { rc = ERROR_FAIL; goto out; } rc = 0; out: if (fd >= 0) close(fd); return rc; } /* Is usb interface bound to usbback? */ static int usbintf_is_assigned(libxl__gc *gc, char *intf) { char *spath; int r; struct stat st; spath = GCSPRINTF(SYSFS_USBBACK_DRIVER "/%s", intf); r = lstat(spath, &st); if (r == 0) return 1; if (r < 0 && errno == ENOENT) return 0; LOGE(ERROR, "Accessing %s", spath); return -1; } static int usbdev_get_all_interfaces(libxl__gc *gc, const char *busid, char ***intfs, int *num) { DIR *dir; char *buf; struct dirent *de; int rc; *intfs = NULL; *num = 0; buf = GCSPRINTF("%s:", busid); dir = opendir(SYSFS_USB_DEV); if (!dir) { LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV); return ERROR_FAIL; } for (;;) { errno = 0; de = readdir(dir); if (!de && errno) { LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV); rc = ERROR_FAIL; goto out; } if (!de) break; if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (!strncmp(de->d_name, buf, strlen(buf))) { GCREALLOC_ARRAY(*intfs, *num + 1); (*intfs)[*num] = libxl__strdup(gc, de->d_name); (*num)++; } } rc = 0; out: closedir(dir); return rc; } /* Encode usb interface so that it could be written to xenstore as a key. * * Since xenstore key cannot include '.' or ':', we'll change '.' to '_', * change ':' to '@'. For example, 3-1:2.1 will be encoded to 3-1@2_1. * This will be used to save original driver of USB device to xenstore. */ static char *usb_interface_xenstore_encode(libxl__gc *gc, const char *busid) { char *str = libxl__strdup(gc, busid); int i, len = strlen(str); for (i = 0; i < len; i++) { if (str[i] == '.') str[i] = '_'; if (str[i] == ':') str[i] = '@'; } return str; } /* Unbind USB device from "usbback" driver. * * If there are many interfaces under USB device, check each interface, * unbind from "usbback" driver. */ static int usbback_dev_unassign(libxl__gc *gc, const char *busid) { char **intfs = NULL; int i, num = 0; int rc; rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num); if (rc) goto out; for (i = 0; i < num; i++) { char *intf = intfs[i]; /* check if the USB interface is already bound to "usbback" */ if (usbintf_is_assigned(gc, intf) > 0) { /* unbind interface from usbback driver */ rc = unbind_usbintf(gc, intf); if (rc) { LOGE(ERROR, "Couldn't unbind %s from usbback", intf); goto out; } } } rc = 0; out: return rc; } /* rebind USB device to original driver. * * If there are many interfaces under USB device, for reach interface, * read driver_path from xenstore (if there is) and rebind to its * original driver, then remove driver_path information from xenstore. */ static int usbdev_rebind(libxl__gc *gc, const char *busid) { char **intfs = NULL; char *usbdev_encode = NULL; char *path = NULL; int i, num = 0; int rc; rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num); if (rc) goto out; usbdev_encode = usb_interface_xenstore_encode(gc, busid); for (i = 0; i < num; i++) { char *intf = intfs[i]; char *usbintf_encode = NULL; const char *drvpath; /* rebind USB interface to its originial driver */ usbintf_encode = usb_interface_xenstore_encode(gc, intf); path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path", usbdev_encode, usbintf_encode); rc = libxl__xs_read_checked(gc, XBT_NULL, path, &drvpath); if (rc) goto out; if (drvpath) { rc = bind_usbintf(gc, intf, drvpath); if (rc) { LOGE(ERROR, "Couldn't rebind %s to %s", intf, drvpath); goto out; } } } out: path = GCSPRINTF(USBBACK_INFO_PATH "/%s", usbdev_encode); libxl__xs_rm_checked(gc, XBT_NULL, path); return rc; } /* Bind USB device to "usbback" driver. * * If there are many interfaces under USB device, check each interface, * unbind from original driver and bind to "usbback" driver. */ static int usbback_dev_assign(libxl__gc *gc, const char *busid) { char **intfs = NULL; int num = 0, i; int rc; char *usbdev_encode = NULL; rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num); if (rc) return rc; usbdev_encode = usb_interface_xenstore_encode(gc, busid); for (i = 0; i < num; i++) { char *intf = intfs[i]; char *drvpath = NULL; /* already assigned to usbback */ if (usbintf_is_assigned(gc, intf) > 0) continue; rc = usbintf_get_drvpath(gc, intf, &drvpath); if (rc) goto out; if (drvpath) { /* write driver path to xenstore for later rebinding */ char *usbintf_encode = NULL; char *path; usbintf_encode = usb_interface_xenstore_encode(gc, intf); path = GCSPRINTF(USBBACK_INFO_PATH "/%s/%s/driver_path", usbdev_encode, usbintf_encode); rc = libxl__xs_write_checked(gc, XBT_NULL, path, drvpath); if (rc) goto out; /* unbind interface from original driver */ rc = unbind_usbintf(gc, intf); if (rc) goto out; } /* bind interface to usbback */ rc = bind_usbintf(gc, intf, SYSFS_USBBACK_DRIVER); if (rc) { LOG(ERROR, "Couldn't bind %s to %s", intf, SYSFS_USBBACK_DRIVER); goto out; } } return 0; out: /* some interfaces might be bound to usbback, unbind it and * rebind it to its original driver */ usbback_dev_unassign(gc, busid); usbdev_rebind(gc, busid); return rc; } static int do_usbdev_add(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev, bool update_json) { int rc; char *busid; libxl_device_usbctrl usbctrl; libxl_usbctrlinfo usbctrlinfo; libxl_device_usbctrl_init(&usbctrl); libxl_usbctrlinfo_init(&usbctrlinfo); usbctrl.devid = usbdev->ctrl; rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); if (rc) goto out; switch (usbctrlinfo.type) { case LIBXL_USBCTRL_TYPE_PV: busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus, usbdev->u.hostdev.hostaddr); if (!busid) { rc = ERROR_FAIL; goto out; } rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev, LIBXL_USBCTRL_TYPE_PV, update_json); if (rc) goto out; rc = usbback_dev_assign(gc, busid); if (rc) { libxl__device_usbdev_remove_xenstore(gc, domid, usbdev, LIBXL_USBCTRL_TYPE_PV); goto out; } break; case LIBXL_USBCTRL_TYPE_QUSB: rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev, LIBXL_USBCTRL_TYPE_QUSB, update_json); if (rc) goto out; break; case LIBXL_USBCTRL_TYPE_DEVICEMODEL: rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev, LIBXL_USBCTRL_TYPE_DEVICEMODEL, update_json); if (rc) goto out; rc = libxl__device_usbdev_add_hvm(gc, domid, usbdev); if (rc) { libxl__device_usbdev_remove_xenstore(gc, domid, usbdev, LIBXL_USBCTRL_TYPE_DEVICEMODEL); goto out; } break; default: LOGD(ERROR, domid, "Unsupported usb controller type"); rc = ERROR_FAIL; goto out; } rc = 0; out: libxl_device_usbctrl_dispose(&usbctrl); libxl_usbctrlinfo_dispose(&usbctrlinfo); return rc; } /* AO operation to add a usb device. * * Generally, it does: * 1) check if the usb device type is assignable * 2) check if the usb device is already assigned to a domain * 3) add 'busid' of the usb device to xenstore contoller/port/. * (PVUSB driver watches the xenstore changes and will detect that.) * 4) unbind usb device from original driver and bind to usbback. * If usb device has many interfaces, then: * - unbind each interface from its original driver and bind to usbback. * - store the original driver to xenstore for later rebinding when * detaching the device. * * Before calling this function, aodev should be properly filled: * aodev->ao, aodev->callback, aodev->update_json, ... */ static void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid, libxl_device_usbdev *usbdev, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); int rc; libxl_device_usbdev *assigned; int num_assigned; libxl_device_usbctrl usbctrl; libxl_usbctrlinfo usbctrlinfo; libxl_device_usbctrl_init(&usbctrl); libxl_usbctrlinfo_init(&usbctrlinfo); /* Currently only support adding USB device from Dom0 backend. * So, if USB controller is specified, check its backend domain, * if it's not Dom0, report error. */ if (usbdev->ctrl != -1) { usbctrl.devid = usbdev->ctrl; rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); if (rc) goto out; if (usbctrlinfo.backend_id != LIBXL_TOOLSTACK_DOMID) { LOGD(ERROR, domid, "Don't support adding USB device from non-Dom0 backend"); rc = ERROR_INVAL; goto out; } } /* check usb device is assignable type */ if (!is_usbdev_assignable(gc, usbdev)) { LOGD(ERROR, domid, "USB device is not assignable."); rc = ERROR_FAIL; goto out; } /* check usb device is already assigned */ rc = get_assigned_devices(gc, &assigned, &num_assigned); if (rc) { LOGD(ERROR, domid, "cannot determine if device is assigned," " refusing to continue"); goto out; } if (is_usbdev_in_array(assigned, num_assigned, usbdev)) { LOGD(ERROR, domid, "USB device already attached to a domain"); rc = ERROR_INVAL; goto out; } /* fill default values, e.g, if usbdev->ctrl and usbdev->port * not specified, choose available controller:port and fill in. */ rc = libxl__device_usbdev_setdefault(gc, domid, usbdev, aodev->update_json); if (rc) goto out; /* do actual adding usb device operation */ rc = do_usbdev_add(gc, domid, usbdev, aodev->update_json); out: libxl_device_usbctrl_dispose(&usbctrl); libxl_usbctrlinfo_dispose(&usbctrlinfo); aodev->rc = rc; aodev->callback(egc, aodev); return; } LIBXL_DEFINE_DEVICE_ADD(usbdev) static LIBXL_DEFINE_DEVICES_ADD(usbdev) static int do_usbdev_remove(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev) { int rc; char *busid; libxl_device_usbctrl usbctrl; libxl_usbctrlinfo usbctrlinfo; libxl_device_usbctrl_init(&usbctrl); libxl_usbctrlinfo_init(&usbctrlinfo); usbctrl.devid = usbdev->ctrl; rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); if (rc) goto out; switch (usbctrlinfo.type) { case LIBXL_USBCTRL_TYPE_PV: busid = usbdev_busid_from_ctrlport(gc, domid, usbdev, usbctrlinfo.type); if (!busid) { rc = ERROR_FAIL; goto out; } /* Things are done in order of: * unbind USB device from usbback, * remove USB device from xenstore, * rebind USB device to original driver. * It is to balance simplicity with robustness in case of failure: * - We unbind all interfaces before rebinding any interfaces, so * that we never get into a situation where some interfaces are * assigned to usbback and some are assigned to the original drivers. * - We also unbind the interfaces before removing the pvusb xenstore * nodes, so that if the unbind fails in the middle, the device still * shows up in xl usb-list, and the user can re-try removing it. */ rc = usbback_dev_unassign(gc, busid); if (rc) { LOGD(ERROR, domid, "Error removing device from guest." " Try running usbdev-detach again."); goto out; } rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev, LIBXL_USBCTRL_TYPE_PV); if (rc) { LOGD(ERROR, domid, "Error removing device from guest." " Try running usbdev-detach again."); goto out; } rc = usbdev_rebind(gc, busid); if (rc) { LOGD(ERROR, domid, "USB device removed from guest, but couldn't" " re-bind to domain 0. Try removing and re-inserting" " the USB device or reloading the driver modules."); goto out; } break; case LIBXL_USBCTRL_TYPE_QUSB: rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev, LIBXL_USBCTRL_TYPE_QUSB); if (rc) goto out; break; case LIBXL_USBCTRL_TYPE_DEVICEMODEL: rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev, LIBXL_USBCTRL_TYPE_DEVICEMODEL); if (rc) goto out; rc = libxl__device_usbdev_del_hvm(gc, domid, usbdev); if (rc) { libxl__device_usbdev_add_xenstore(gc, domid, usbdev, LIBXL_USBCTRL_TYPE_DEVICEMODEL, false); goto out; } break; default: LOGD(ERROR, domid, "Unsupported usb controller type"); rc = ERROR_FAIL; goto out; } rc = 0; out: libxl_device_usbctrl_dispose(&usbctrl); libxl_usbctrlinfo_dispose(&usbctrlinfo); return rc; } /* Operation to remove usb device. * * Generally, it does: * 1) check if the usb device is assigned to the domain * 2) remove the usb device from xenstore controller/port. * 3) unbind usb device from usbback and rebind to its original driver. * If usb device has many interfaces, do it to each interface. */ static int libxl__device_usbdev_remove(libxl__gc *gc, uint32_t domid, libxl_device_usbdev *usbdev) { libxl_usbctrlinfo usbctrlinfo; libxl_device_usbctrl usbctrl; int rc; if (usbdev->ctrl < 0 || usbdev->port < 1) { LOGD(ERROR, domid, "Invalid USB device"); return ERROR_FAIL; } libxl_device_usbctrl_init(&usbctrl); libxl_usbctrlinfo_init(&usbctrlinfo); usbctrl.devid = usbdev->ctrl; rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); if (rc) goto out; if (usbctrlinfo.backend_id != LIBXL_TOOLSTACK_DOMID) { LOGD(ERROR, domid, "Don't support removing USB device from non-Dom0 backend"); rc = ERROR_INVAL; goto out; } /* do actual removing usb device operation */ rc = do_usbdev_remove(gc, domid, usbdev); out: libxl_device_usbctrl_dispose(&usbctrl); libxl_usbctrlinfo_dispose(&usbctrlinfo); return rc; } int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_usbdev *usbdev, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int rc; rc = libxl__device_usbdev_remove(gc, domid, usbdev); libxl__ao_complete(egc, ao, rc); return AO_INPROGRESS; } int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, uint32_t domid, int ctrl, int port, libxl_device_usbdev *usbdev) { GC_INIT(ctx); const char *libxl_dom_path, *libxl_path, *be_path, *busid; int rc; libxl_dom_path = libxl__xs_libxl_path(gc, domid); libxl_path = GCSPRINTF("%s/device/vusb/%d", libxl_dom_path, ctrl); be_path = vusb_be_from_xs_libxl(gc, libxl_path); if (!be_path) { rc = ERROR_FAIL; goto out; } rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/port/%d", be_path, port), &busid); if (rc) goto out; if (!busid || !strcmp(busid, "")) { rc = ERROR_FAIL; goto out; } usbdev->ctrl = ctrl; usbdev->port = port; usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV; rc = usbdev_busaddr_from_busid(gc, busid, &usbdev->u.hostdev.hostbus, &usbdev->u.hostdev.hostaddr); out: GC_FREE; return rc; } static int libxl_device_usbctrl_compare(libxl_device_usbctrl *d1, libxl_device_usbctrl *d2) { return COMPARE_USBCTRL(d1, d2); } static int libxl_device_usbctrl_dm_needed(void *e, unsigned domid) { libxl_device_usbctrl *elem = e; return elem->type == LIBXL_USBCTRL_TYPE_QUSB && elem->backend_domid == domid; } static int libxl_device_usbdev_compare(libxl_device_usbdev *d1, libxl_device_usbdev *d2) { return COMPARE_USB(d1, d2); } void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_device_usbctrl_dispose(&list[i]); free(list); } void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr) { int i; for (i = 0; i < nr; i++) libxl_device_usbdev_dispose(&list[i]); free(list); } DEFINE_DEVICE_TYPE_STRUCT(usbctrl, .dm_needed = libxl_device_usbctrl_dm_needed ); DEFINE_DEVICE_TYPE_STRUCT(usbdev); /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/test_common.h0000664000175000017500000000102713256712137015427 0ustar smbsmb#ifndef TEST_COMMON_H #define TEST_COMMON_H #include "libxl.h" #include #include #include #include #include #include void test_common_setup(int level); extern libxl_ctx *ctx; void test_common_get_now(void); extern struct timeval now; void test_common_beforepoll(void); void test_common_dopoll(void); void test_common_afterpoll(void); extern int poll_nfds, poll_nfds_allocd; extern struct pollfd *poll_fds; extern int poll_timeout; #endif /*TEST_COMMON_H*/ xen-4.9.2/tools/libxl/libxl_x86_acpi.c0000664000175000017500000001667213256712137015722 0ustar smbsmb/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. */ #include "libxl_internal.h" #include "libxl_arch.h" #include #include #include "libacpi/libacpi.h" #include /* Number of pages holding ACPI tables */ #define NUM_ACPI_PAGES 16 /* Store RSDP in the last 64 bytes of BIOS RO memory */ #define RSDP_ADDRESS (0x100000 - 64) #define ACPI_INFO_PHYSICAL_ADDRESS 0xfc000000 struct libxl_acpi_ctxt { struct acpi_ctxt c; unsigned int page_size; unsigned int page_shift; /* Memory allocator */ unsigned long alloc_base_paddr; unsigned long alloc_base_vaddr; unsigned long alloc_currp; unsigned long alloc_end; }; extern const unsigned char dsdt_pvh[]; extern const unsigned int dsdt_pvh_len; /* Assumes contiguous physical space */ static unsigned long virt_to_phys(struct acpi_ctxt *ctxt, void *v) { struct libxl_acpi_ctxt *libxl_ctxt = CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c); return (((unsigned long)v - libxl_ctxt->alloc_base_vaddr) + libxl_ctxt->alloc_base_paddr); } static void *mem_alloc(struct acpi_ctxt *ctxt, uint32_t size, uint32_t align) { struct libxl_acpi_ctxt *libxl_ctxt = CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c); unsigned long s, e; /* Align to at least 16 bytes. */ if (align < 16) align = 16; s = (libxl_ctxt->alloc_currp + align) & ~((unsigned long)align - 1); e = s + size - 1; /* TODO: Reallocate memory */ if ((e < s) || (e >= libxl_ctxt->alloc_end)) return NULL; while (libxl_ctxt->alloc_currp >> libxl_ctxt->page_shift != e >> libxl_ctxt->page_shift) libxl_ctxt->alloc_currp += libxl_ctxt->page_size; libxl_ctxt->alloc_currp = e; return (void *)s; } static void acpi_mem_free(struct acpi_ctxt *ctxt, void *v, uint32_t size) { } static uint8_t acpi_lapic_id(unsigned cpu) { return cpu * 2; } static int init_acpi_config(libxl__gc *gc, struct xc_dom_image *dom, const libxl_domain_build_info *b_info, struct acpi_config *config) { xc_interface *xch = dom->xch; uint32_t domid = dom->guest_domid; xc_dominfo_t info; struct hvm_info_table *hvminfo; int i, r, rc; config->dsdt_anycpu = config->dsdt_15cpu = dsdt_pvh; config->dsdt_anycpu_len = config->dsdt_15cpu_len = dsdt_pvh_len; r = xc_domain_getinfo(xch, domid, 1, &info); if (r < 0) { LOG(ERROR, "getdomaininfo failed (rc=%d)", r); rc = ERROR_FAIL; goto out; } hvminfo = libxl__zalloc(gc, sizeof(*hvminfo)); hvminfo->apic_mode = libxl_defbool_val(b_info->u.hvm.apic); if (dom->nr_vnodes) { unsigned int *vcpu_to_vnode, *vdistance; struct xen_vmemrange *vmemrange; struct acpi_numa *numa = &config->numa; r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes, &numa->nr_vmemranges, &hvminfo->nr_vcpus, NULL, NULL, NULL); if (r) { LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r); rc = ERROR_FAIL; goto out; } vmemrange = libxl__zalloc(gc, dom->nr_vmemranges * sizeof(*vmemrange)); vdistance = libxl__zalloc(gc, dom->nr_vnodes * sizeof(*vdistance)); vcpu_to_vnode = libxl__zalloc(gc, hvminfo->nr_vcpus * sizeof(*vcpu_to_vnode)); r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes, &numa->nr_vmemranges, &hvminfo->nr_vcpus, vmemrange, vdistance, vcpu_to_vnode); if (r) { LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r); rc = ERROR_FAIL; goto out; } numa->vmemrange = vmemrange; numa->vdistance = vdistance; numa->vcpu_to_vnode = vcpu_to_vnode; } else { hvminfo->nr_vcpus = info.max_vcpu_id + 1; } for (i = 0; i < hvminfo->nr_vcpus; i++) hvminfo->vcpu_online[i / 8] |= 1 << (i & 7); config->hvminfo = hvminfo; config->lapic_base_address = LAPIC_BASE_ADDRESS; config->lapic_id = acpi_lapic_id; config->acpi_revision = 5; rc = 0; out: return rc; } int libxl__dom_load_acpi(libxl__gc *gc, const libxl_domain_build_info *b_info, struct xc_dom_image *dom) { struct acpi_config config = {0}; struct libxl_acpi_ctxt libxl_ctxt; int rc = 0, acpi_pages_num; void *acpi_pages; unsigned long page_mask; if ((b_info->type != LIBXL_DOMAIN_TYPE_HVM) || (b_info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_NONE)) goto out; libxl_ctxt.page_size = XC_DOM_PAGE_SIZE(dom); libxl_ctxt.page_shift = XC_DOM_PAGE_SHIFT(dom); page_mask = (1UL << libxl_ctxt.page_shift) - 1; libxl_ctxt.c.mem_ops.alloc = mem_alloc; libxl_ctxt.c.mem_ops.v2p = virt_to_phys; libxl_ctxt.c.mem_ops.free = acpi_mem_free; rc = init_acpi_config(gc, dom, b_info, &config); if (rc) { LOG(ERROR, "init_acpi_config failed (rc=%d)", rc); goto out; } config.rsdp = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size); config.infop = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size); /* Pages to hold ACPI tables */ acpi_pages = libxl__malloc(gc, (NUM_ACPI_PAGES + 1) * libxl_ctxt.page_size); /* * Set up allocator memory. * Start next to acpi_info page to avoid fracturing e820. */ libxl_ctxt.alloc_base_paddr = ACPI_INFO_PHYSICAL_ADDRESS + libxl_ctxt.page_size; libxl_ctxt.alloc_base_vaddr = libxl_ctxt.alloc_currp = (unsigned long)acpi_pages; libxl_ctxt.alloc_end = (unsigned long)acpi_pages + (NUM_ACPI_PAGES * libxl_ctxt.page_size); /* Build the tables. */ rc = acpi_build_tables(&libxl_ctxt.c, &config); if (rc) { LOG(ERROR, "acpi_build_tables failed with %d", rc); goto out; } /* Calculate how many pages are needed for the tables. */ acpi_pages_num = ((libxl_ctxt.alloc_currp - (unsigned long)acpi_pages) >> libxl_ctxt.page_shift) + ((libxl_ctxt.alloc_currp & page_mask) ? 1 : 0); dom->acpi_modules[0].data = (void *)config.rsdp; dom->acpi_modules[0].length = 64; dom->acpi_modules[0].guest_addr_out = RSDP_ADDRESS; dom->acpi_modules[1].data = (void *)config.infop; dom->acpi_modules[1].length = 4096; dom->acpi_modules[1].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS; dom->acpi_modules[2].data = acpi_pages; dom->acpi_modules[2].length = acpi_pages_num << libxl_ctxt.page_shift; dom->acpi_modules[2].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS + libxl_ctxt.page_size; out: return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_cfg_i.h0000664000175000017500000000402313256712137015525 0ustar smbsmb/* * libxlu_cfg_i.h - xl configuration file parsing: parser-internal declarations * * Copyright (C) 2010 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef LIBXLU_CFG_I_H #define LIBXLU_CFG_I_H #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxlu_internal.h" #include "libxlu_cfg_y.h" void xlu__cfg_set_free(XLU_ConfigSetting *set); void xlu__cfg_set_store(CfgParseContext*, char *name, XLU_ConfigValue *val, int lineno); XLU_ConfigValue *xlu__cfg_string_mk(CfgParseContext *ctx, char *atom, YYLTYPE *loc); XLU_ConfigValue *xlu__cfg_list_mk(CfgParseContext *ctx, XLU_ConfigValue *val, YYLTYPE *loc); void xlu__cfg_list_append(CfgParseContext *ctx, XLU_ConfigValue *list, XLU_ConfigValue *val); void xlu__cfg_value_free(XLU_ConfigValue *value); char *xlu__cfgl_strdup(CfgParseContext*, const char *src); char *xlu__cfgl_dequote(CfgParseContext*, const char *src); void xlu__cfg_yyerror(YYLTYPE *locp, CfgParseContext*, char const *msg); void xlu__cfgl_lexicalerror(CfgParseContext*, char const *msg); void xlu__cfgl_likely_python(CfgParseContext *ctx); /* Why oh why does bison not declare this in its autogenerated .h ? */ int xlu__cfg_yyparse(CfgParseContext *ctx); #endif /*LIBXLU_CFG_I_H*/ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_numa.c0000664000175000017500000004713013256712137015232 0ustar smbsmb/* * Copyright (C) 2012 Citrix Ltd. * Author Dario Faggioli * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include #include "libxl_internal.h" /* * What follows are helpers for generating all the k-combinations * without repetitions of a set S with n elements in it. Formally * speaking, they are subsets of k distinct elements of S and, if * S is n elements big, the number of k-combinations is equal to the * binomial coefficient C(n k)=n!/(k! * (n - k)!). * * The various subset are generated one after the other by calling * comb_init() first, and, after that, comb_next() * C(n k)-1 times. An iterator is used to store the current status * of the whole generation operation (i.e., basically, the last * combination that has been generated). As soon as all combinations * have been generated, comb_next() will start returning 0 instead of * 1. The same instance of the iterator and the same values for * n and k _must_ be used for each call (if that doesn't happen, the * result is unspecified). * * The algorithm is a well known one (see, for example, D. Knuth's "The * Art of Computer Programming - Volume 4, Fascicle 3" and it produces * the combinations in such a way that they (well, more precisely, their * indexes it the array/map representing the set) come with lexicographic * ordering. * * For example, with n = 5 and k = 3, calling comb_init() * will generate { 0, 1, 2 }, while subsequent valid calls to * comb_next() will produce the following: * { { 0, 1, 3 }, { 0, 1, 4 }, * { 0, 2, 3 }, { 0, 2, 4 }, { 0, 3, 4 }, * { 1, 2, 3 }, { 1, 2, 4 }, { 1, 3, 4 }, * { 2, 3, 4 } } * * This is used by the automatic NUMA placement logic below. */ typedef int* comb_iter_t; static int comb_init(libxl__gc *gc, comb_iter_t *it, int n, int k) { comb_iter_t new_iter; int i; if (n < k) return 0; /* First set is always { 0, 1, 2, ..., k-1 } */ GCNEW_ARRAY(new_iter, k); for (i = 0; i < k; i++) new_iter[i] = i; *it = new_iter; return 1; } static int comb_next(comb_iter_t it, int n, int k) { int i; /* * The idea here is to find the leftmost element from where * we should start incrementing the indexes of the iterator. * This means looking for the highest index that can be increased * while still producing value smaller than n-1. In the example * above, when dealing with { 0, 1, 4 }, such an element is the * second one, as the third is already equal to 4 (which actually * is n-1). * Once we found from where to start, we increment that element * and override the right-hand rest of the iterator with its * successors, thus achieving lexicographic ordering. * * Regarding the termination of the generation process, when we * manage in bringing n-k at the very first position of the iterator, * we know that is the last valid combination ( { 2, 3, 4 }, with * n - k = 5 - 2 = 2, in the example above), and thus we start * returning 0 as soon as we cross that border. */ for (i = k - 1; it[i] == n - k + i; i--) { if (i <= 0) return 0; } for (it[i]++, i++; i < k; i++) it[i] = it[i - 1] + 1; return 1; } /* NUMA automatic placement (see libxl_internal.h for details) */ /* * This function turns a k-combination iterator into a node map, * given another map, telling us which nodes should be considered. * * This means the bits that are set in suitable_nodemap and that * corresponds to the indexes of the given combination are the ones * that will be set in nodemap. * * For example, given a fully set suitable_nodemap, if the iterator * represents the combination { 0, 2, 4}, nodmeap will have bits #0, * #2 and #4 set. * On the other hand, if, say, suitable_nodemap=01011011, the same * iterator will cause bits #1, #4 and #7 of nodemap to be set. */ static void comb_get_nodemap(comb_iter_t it, libxl_bitmap *suitable_nodemap, libxl_bitmap *nodemap, int k) { int i, m = 0, n = 0; libxl_bitmap_set_none(nodemap); libxl_for_each_set_bit(i, *suitable_nodemap) { /* Check wether the n-th set bit of suitable_nodemap * matches with the m-th element of the iterator (and, * only if it does, advance to the next one) */ if (m < k && n == it[m]) { libxl_bitmap_set(nodemap, i); m++; } n++; } } /* Retrieve the number of cpus that the nodes that are part of the nodemap * span and are also set in suitable_cpumap. */ static int nodemap_to_nr_cpus(libxl_cputopology *tinfo, int nr_cpus, const libxl_bitmap *suitable_cpumap, const libxl_bitmap *nodemap) { int i, nodes_cpus = 0; for (i = 0; i < nr_cpus; i++) { if (libxl_bitmap_test(suitable_cpumap, i) && libxl_bitmap_test(nodemap, tinfo[i].node)) nodes_cpus++; } return nodes_cpus; } /* Retrieve the amount of free memory within the nodemap */ static uint32_t nodemap_to_free_memkb(libxl_numainfo *ninfo, libxl_bitmap *nodemap) { uint32_t free_memkb = 0; int i; libxl_for_each_set_bit(i, *nodemap) free_memkb += ninfo[i].free / 1024; return free_memkb; } /* Retrieve the number of vcpus able to run on the nodes in nodemap */ static int nodemap_to_nr_vcpus(libxl__gc *gc, int vcpus_on_node[], const libxl_bitmap *nodemap) { int i, nr_vcpus = 0; libxl_for_each_set_bit(i, *nodemap) nr_vcpus += vcpus_on_node[i]; return nr_vcpus; } /* Number of vcpus able to run on the cpus of the various nodes * (reported by filling the array vcpus_on_node[]). */ static int nr_vcpus_on_nodes(libxl__gc *gc, libxl_cputopology *tinfo, size_t tinfo_elements, const libxl_bitmap *suitable_cpumap, int vcpus_on_node[]) { libxl_dominfo *dinfo = NULL; libxl_bitmap dom_nodemap, nodes_counted; int nr_doms, nr_cpus; int i, j, k; dinfo = libxl_list_domain(CTX, &nr_doms); if (dinfo == NULL) return ERROR_FAIL; if (libxl_node_bitmap_alloc(CTX, &nodes_counted, 0) < 0) { libxl_dominfo_list_free(dinfo, nr_doms); return ERROR_FAIL; } if (libxl_node_bitmap_alloc(CTX, &dom_nodemap, 0) < 0) { libxl_bitmap_dispose(&nodes_counted); libxl_dominfo_list_free(dinfo, nr_doms); return ERROR_FAIL; } for (i = 0; i < nr_doms; i++) { libxl_vcpuinfo *vinfo = NULL; int nr_dom_vcpus = 0; libxl_cpupoolinfo cpupool_info; int cpupool; libxl_cpupoolinfo_init(&cpupool_info); cpupool = libxl__domain_cpupool(gc, dinfo[i].domid); if (cpupool < 0) goto next; if (libxl_cpupool_info(CTX, &cpupool_info, cpupool)) goto next; vinfo = libxl_list_vcpu(CTX, dinfo[i].domid, &nr_dom_vcpus, &nr_cpus); if (vinfo == NULL) goto next; /* Retrieve the domain's node-affinity map */ libxl_domain_get_nodeaffinity(CTX, dinfo[i].domid, &dom_nodemap); for (j = 0; j < nr_dom_vcpus; j++) { /* * For each vcpu of each domain, it must have both vcpu-affinity * and node-affinity to (a pcpu belonging to) a certain node to * cause an increment in the corresponding element of the array. * * Note that we also need to check whether the cpu actually * belongs to the domain's cpupool (the cpupool of the domain * being checked). In fact, it could be that the vcpu has affinity * with cpus in suitable_cpumask, but that are not in its own * cpupool, and we don't want to consider those! */ libxl_bitmap_set_none(&nodes_counted); libxl_for_each_set_bit(k, vinfo[j].cpumap) { if (k >= tinfo_elements) break; int node = tinfo[k].node; if (libxl_bitmap_test(suitable_cpumap, k) && libxl_bitmap_test(&cpupool_info.cpumap, k) && libxl_bitmap_test(&dom_nodemap, node) && !libxl_bitmap_test(&nodes_counted, node)) { libxl_bitmap_set(&nodes_counted, node); vcpus_on_node[node]++; } } } next: libxl_cpupoolinfo_dispose(&cpupool_info); libxl_vcpuinfo_list_free(vinfo, nr_dom_vcpus); } libxl_bitmap_dispose(&dom_nodemap); libxl_bitmap_dispose(&nodes_counted); libxl_dominfo_list_free(dinfo, nr_doms); return 0; } /* * This function tries to figure out if the host has a consistent number * of cpus along all its NUMA nodes. In fact, if that is the case, we can * calculate the minimum number of nodes needed for a domain by just * dividing its total number of vcpus by this value computed here. * However, we are not allowed to assume that all the nodes have the * same number of cpus. Therefore, in case discrepancies among different * nodes are found, this function just returns 0, for the caller to know * it shouldn't rely on this 'optimization', and sort out things in some * other way (by doing something basic, like starting trying with * candidates with just one node). */ static int count_cpus_per_node(libxl_cputopology *tinfo, int nr_cpus, int nr_nodes) { int cpus_per_node = 0; int j, i; /* This makes sense iff # of PCPUs is the same for all nodes */ for (j = 0; j < nr_nodes; j++) { int curr_cpus = 0; for (i = 0; i < nr_cpus; i++) { if (tinfo[i].node == j) curr_cpus++; } /* So, if the above does not hold, turn the whole thing off! */ cpus_per_node = cpus_per_node == 0 ? curr_cpus : cpus_per_node; if (cpus_per_node != curr_cpus) return 0; } return cpus_per_node; } /* * Looks for the placement candidates that satisfyies some specific * conditions and return the best one according to the provided * comparison function. */ int libxl__get_numa_candidate(libxl__gc *gc, uint64_t min_free_memkb, int min_cpus, int min_nodes, int max_nodes, const libxl_bitmap *suitable_cpumap, libxl__numa_candidate_cmpf numa_cmpf, libxl__numa_candidate *cndt_out, int *cndt_found) { libxl__numa_candidate new_cndt; libxl_cputopology *tinfo = NULL; libxl_numainfo *ninfo = NULL; int nr_nodes = 0, nr_suit_nodes, nr_cpus = 0; libxl_bitmap suitable_nodemap, nodemap; int *vcpus_on_node, rc = 0; libxl_bitmap_init(&nodemap); libxl_bitmap_init(&suitable_nodemap); libxl__numa_candidate_init(&new_cndt); /* Get platform info and prepare the map for testing the combinations */ ninfo = libxl_get_numainfo(CTX, &nr_nodes); if (ninfo == NULL) return ERROR_FAIL; if (nr_nodes <= 1) { *cndt_found = 0; goto out; } GCNEW_ARRAY(vcpus_on_node, nr_nodes); /* * The good thing about this solution is that it is based on heuristics * (implemented in numa_cmpf() ), but we at least can evaluate it on * all the possible placement candidates. That can happen because the * number of nodes present in current NUMA systems is quite small. * In fact, even if a sum of binomials is involved, if the system has * up to 16 nodes it "only" takes 65535 steps. This is fine, as the * number of nodes the biggest NUMA systems provide at the time of this * writing is 8 (and it will probably continue to be so for a while). * However, computanional complexity would explode on systems bigger * than that, and it's really important we avoid trying to run this * on monsters with 32, 64 or more nodes (if they ever pop into being). * Therefore, here it comes a safety catch that disables the algorithm * for the cases when it wouldn't work well. */ if (nr_nodes > 16) { /* Log we did nothing and return 0, as no real error occurred */ LOG(WARN, "System has %d NUMA nodes, which is too big for the " "placement algorithm to work effectively: skipping it. " "Consider manually pinning the vCPUs and/or looking at " "cpupools for manually partitioning the system.", nr_nodes); *cndt_found = 0; goto out; } tinfo = libxl_get_cpu_topology(CTX, &nr_cpus); if (tinfo == NULL) { rc = ERROR_FAIL; goto out; } rc = libxl_node_bitmap_alloc(CTX, &nodemap, 0); if (rc) goto out; rc = libxl__numa_candidate_alloc(gc, &new_cndt); if (rc) goto out; /* Allocate and prepare the map of the node that can be utilized for * placement, basing on the map of suitable cpus. */ rc = libxl_node_bitmap_alloc(CTX, &suitable_nodemap, 0); if (rc) goto out; rc = libxl_cpumap_to_nodemap(CTX, suitable_cpumap, &suitable_nodemap); if (rc) goto out; /* * Later on, we will try to figure out how many vcpus are runnable on * each candidate (as a part of choosing the best one of them). That * requires going through all the vcpus of all the domains and check * their affinities. So, instead of doing that for each candidate, * let's count here the number of vcpus runnable on each node, so that * all we have to do later is summing up the right elements of the * vcpus_on_node array. */ rc = nr_vcpus_on_nodes(gc, tinfo, nr_cpus, suitable_cpumap, vcpus_on_node); if (rc) goto out; /* * If the minimum number of NUMA nodes is not explicitly specified * (i.e., min_nodes == 0), we try to figure out a sensible number of nodes * from where to start generating candidates, if possible (or just start * from 1 otherwise). The maximum number of nodes should not exceed the * number of existent NUMA nodes on the host, or the candidate generation * won't work properly. */ if (!min_nodes) { int cpus_per_node; cpus_per_node = count_cpus_per_node(tinfo, nr_cpus, nr_nodes); if (cpus_per_node == 0) min_nodes = 1; else min_nodes = (min_cpus + cpus_per_node - 1) / cpus_per_node; } /* We also need to be sure we do not exceed the number of * nodes we are allowed to use. */ nr_suit_nodes = libxl_bitmap_count_set(&suitable_nodemap); if (min_nodes > nr_suit_nodes) min_nodes = nr_suit_nodes; if (!max_nodes || max_nodes > nr_suit_nodes) max_nodes = nr_suit_nodes; if (min_nodes > max_nodes) { LOG(ERROR, "Inconsistent minimum or maximum number of guest nodes"); rc = ERROR_INVAL; goto out; } /* This is up to the caller to be disposed */ rc = libxl__numa_candidate_alloc(gc, cndt_out); if (rc) goto out; /* * Consider all the combinations with sizes in [min_nodes, max_nodes] * (see comb_init() and comb_next()). Note that, since the fewer the * number of nodes the better, it is guaranteed that any candidate * found during the i-eth step will be better than any other one we * could find during the (i+1)-eth and all the subsequent steps (they * all will have more nodes). It's thus pointless to keep going if * we already found something. */ *cndt_found = 0; while (min_nodes <= max_nodes && *cndt_found == 0) { comb_iter_t comb_iter; int comb_ok; /* * And here it is. Each step of this cycle generates a combination of * nodes as big as min_nodes mandates. Each of these combinations is * checked against the constraints provided by the caller (namely, * amount of free memory and number of cpus) and it can concur to * become our best placement iff it passes the check. */ for (comb_ok = comb_init(gc, &comb_iter, nr_suit_nodes, min_nodes); comb_ok; comb_ok = comb_next(comb_iter, nr_suit_nodes, min_nodes)) { uint64_t nodes_free_memkb; int nodes_cpus; /* Get the nodemap for the combination, only considering * suitable nodes. */ comb_get_nodemap(comb_iter, &suitable_nodemap, &nodemap, min_nodes); /* If there is not enough memory in this combination, skip it * and go generating the next one... */ nodes_free_memkb = nodemap_to_free_memkb(ninfo, &nodemap); if (min_free_memkb && nodes_free_memkb < min_free_memkb) continue; /* And the same applies if this combination is short in cpus */ nodes_cpus = nodemap_to_nr_cpus(tinfo, nr_cpus, suitable_cpumap, &nodemap); if (min_cpus && nodes_cpus < min_cpus) continue; /* * Conditions are met, we can compare this candidate with the * current best one (if any). */ libxl__numa_candidate_put_nodemap(gc, &new_cndt, &nodemap); new_cndt.nr_vcpus = nodemap_to_nr_vcpus(gc, vcpus_on_node, &nodemap); new_cndt.free_memkb = nodes_free_memkb; new_cndt.nr_nodes = libxl_bitmap_count_set(&nodemap); new_cndt.nr_cpus = nodes_cpus; /* * Check if the new candidate we is better the what we found up * to now by means of the comparison function. If no comparison * function is provided, just return as soon as we find our first * candidate. */ if (*cndt_found == 0 || numa_cmpf(&new_cndt, cndt_out) < 0) { *cndt_found = 1; LOG(DEBUG, "New best NUMA placement candidate found: " "nr_nodes=%d, nr_cpus=%d, nr_vcpus=%d, " "free_memkb=%"PRIu64"", new_cndt.nr_nodes, new_cndt.nr_cpus, new_cndt.nr_vcpus, new_cndt.free_memkb / 1024); libxl__numa_candidate_put_nodemap(gc, cndt_out, &nodemap); cndt_out->nr_vcpus = new_cndt.nr_vcpus; cndt_out->free_memkb = new_cndt.free_memkb; cndt_out->nr_nodes = new_cndt.nr_nodes; cndt_out->nr_cpus = new_cndt.nr_cpus; if (numa_cmpf == NULL) break; } } min_nodes++; } if (*cndt_found == 0) LOG(NOTICE, "NUMA placement failed, performance might be affected"); out: libxl_bitmap_dispose(&nodemap); libxl_bitmap_dispose(&suitable_nodemap); libxl__numa_candidate_dispose(&new_cndt); libxl_numainfo_list_free(ninfo, nr_nodes); libxl_cputopology_list_free(tinfo, nr_cpus); return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_event.h0000664000175000017500000006471313256712137015426 0ustar smbsmb/* * Copyright (C) 2011 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef LIBXL_EVENT_H #define LIBXL_EVENT_H #include #include #include /*======================================================================*/ /* * Domain event handling - getting Xen events from libxl * * (Callers inside libxl may not call libxl_event_check or _wait.) */ #define LIBXL_EVENTMASK_ALL (~(unsigned long)0) typedef int libxl_event_predicate(const libxl_event*, void *user); /* Return value is 0 if the event is unwanted or non-0 if it is. * Predicates are not allowed to fail. */ int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r, uint64_t typemask, libxl_event_predicate *predicate, void *predicate_user) LIBXL_EXTERNAL_CALLERS_ONLY; /* Searches for an event, already-happened, which matches typemask * and predicate. predicate==0 matches any event. * libxl_event_check returns the event, which must then later be * freed by the caller using libxl_event_free. * * Returns ERROR_NOT_READY if no such event has happened. */ int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r, uint64_t typemask, libxl_event_predicate *predicate, void *predicate_user) LIBXL_EXTERNAL_CALLERS_ONLY; /* Like libxl_event_check but blocks if no suitable events are * available, until some are. Uses libxl_osevent_beforepoll/ * _afterpoll so may be inefficient if very many domains are being * handled by a single program. */ void libxl_event_free(libxl_ctx *ctx, libxl_event *event); /* Alternatively or additionally, the application may also use this: */ typedef struct libxl_event_hooks { uint64_t event_occurs_mask; void (*event_occurs)(void *user, #ifndef LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG const #endif libxl_event *event); void (*disaster)(void *user, libxl_event_type type, const char *msg, int errnoval); } libxl_event_hooks; void libxl_event_register_callbacks(libxl_ctx *ctx, const libxl_event_hooks *hooks, void *user); /* * Arranges that libxl will henceforth call event_occurs for any * events whose type is set in event_occurs_mask, rather than * queueing the event for retrieval by libxl_event_check/wait. * Events whose bit is clear in mask are not affected. * * event becomes owned by the application and must be freed, either * by event_occurs or later. * * event_occurs may be NULL if mask is 0. * * libxl_event_register_callback also provides a way for libxl to * report to the application that there was a problem reporting * events; this can occur due to lack of host memory during event * handling, or other wholly unrecoverable errors from system calls * made by libxl. This will not happen for frivolous reasons - only * if the system, or the Xen components of it, are badly broken. * * msg and errnoval will describe the action that libxl was trying * to do, and type specifies the type of libxl events which may be * missing. type may be 0 in which case events of all types may be * missing. * * disaster may be NULL. If it is, or if _register_callbacks has * not been called, errors of this kind are fatal to the entire * application: libxl will print messages to its logs and to stderr * and call exit(-1). * * If disaster returns, it may be the case that some or all future * libxl calls will return errors; likewise it may be the case that * no more events (of the specified type, if applicable) can be * produced. An application which supplies a disaster function * should normally react either by exiting, or by (when it has * returned to its main event loop) shutting down libxl with * libxl_ctx_free and perhaps trying to restart it with * libxl_ctx_init. * * In any case before calling disaster, libxl will have logged a * message with level XTL_CRITICAL. * * Reentrancy: it IS permitted to call libxl from within * event_occurs. It is NOT permitted to call libxl from within * disaster. The event_occurs and disaster callbacks may occur on * any thread in which the application calls libxl. * * libxl_event_register_callbacks may be called as many times, with * different parameters, as the application likes; the most recent * call determines the libxl behaviour. However it is NOT safe to * call _register_callbacks concurrently with, or reentrantly from, * any other libxl function, nor while any event-generation * facilities are enabled. */ /* * Events are only generated if they have been requested. * The following functions request the generation of specific events. * * Each set of functions for controlling event generation has this form: * * typedef struct libxl__evgen_FOO libxl__evgen_FOO; * int libxl_evenable_FOO(libxl_ctx *ctx, FURTHER PARAMETERS, * libxl_ev_user user, libxl__evgen_FOO **evgen_out); * void libxl_evdisable_FOO(libxl_ctx *ctx, libxl__evgen_FOO *evgen); * * The evenable function arranges that the events (as described in the * doc comment for the individual function) will start to be generated * by libxl. On success, *evgen_out is set to a non-null pointer to * an opaque struct. * * The user value is returned in the generated events and may be * used by the caller for whatever it likes. The type ev_user is * guaranteed to be an unsigned integer type which is at least * as big as uint64_t and is also guaranteed to be big enough to * contain any intptr_t value. * * If it becomes desirable to stop generation of the relevant events, * or to reclaim the resources in libxl associated with the evgen * structure, the same evgen value should be passed to the evdisable * function. However, note that events which occurred prior to the * evdisable call may still be returned. * * The caller may enable identical events more than once. If they do * so, each actual occurrence will generate several events to be * returned by libxl_event_check, with the appropriate user value(s). * Aside from this, each occurrence of each event is returned by * libxl_event_check exactly once. * * An evgen is associated with the libxl_ctx used for its creation. * After libxl_ctx_free, all corresponding evgen handles become * invalid and must no longer be passed to evdisable. * * Applications should ensure that they eventually retrieve every * event using libxl_event_check or libxl_event_wait, since events * which occur but are not retreived by the application will be queued * inside libxl indefinitely. libxl_event_check/_wait may be O(n) * where n is the number of queued events which do not match the * criteria specified in the arguments to check/wait. */ typedef struct libxl__evgen_domain_death libxl_evgen_domain_death; int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid, libxl_ev_user, libxl_evgen_domain_death **evgen_out); void libxl_evdisable_domain_death(libxl_ctx *ctx, libxl_evgen_domain_death*); /* Arranges for the generation of DOMAIN_SHUTDOWN and DOMAIN_DEATH * events. A domain which is destroyed before it shuts down * may generate only a DEATH event. */ typedef struct libxl__evgen_disk_eject libxl_evgen_disk_eject; int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t domid, const char *vdev, libxl_ev_user, libxl_evgen_disk_eject **evgen_out); void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject*); /* Arranges for the generation of DISK_EJECT events. A copy of the * string *vdev will be made for libxl's internal use, and a pointer * to this (or some other) copy will be returned as the vdev * member of event.u. */ /*======================================================================*/ /* * OS event handling - passing low-level OS events to libxl * * Event-driven programs must use these facilities to allow libxl * to become aware of readability/writeability of file descriptors * and the occurrence of timeouts. * * There are two approaches available. The first is appropriate for * simple programs handling reasonably small numbers of domains: * * for (;;) { * libxl_osevent_beforepoll(...) * poll(); * libxl_osevent_afterpoll(...); * for (;;) { * r = libxl_event_check(...); * if (r==ERROR_NOT_READY) break; * if (r) goto error_out; * do something with the event; * } * } * * The second approach uses libxl_osevent_register_hooks and is * suitable for programs which are already using a callback-based * event library. * * An application may freely mix the two styles of interaction. * * (Callers inside libxl may not call libxl_osevent_... functions.) */ struct pollfd; /* The caller should provide beforepoll with some space for libxl's * fds, and tell libxl how much space is available by setting *nfds_io. * fds points to the start of this space (and fds may be a pointer into * a larger array, for example, if the application has some fds of * its own that it is interested in). * * On return *nfds_io will in any case have been updated by libxl * according to how many fds libxl wants to poll on. * * If the space was sufficient, libxl fills in fds[0..] suitably for poll(2), updates *timeout_upd if needed, * and returns ok. * * If space was insufficient, fds[0..] is undefined on * return; *nfds_io on return will be greater than the value on * entry; *timeout_upd may or may not have been updated; and * libxl_osevent_beforepoll returns ERROR_BUFERFULL. In this case * the application needs to make more space (enough space for * *nfds_io struct pollfd) and then call beforepoll again, before * entering poll(2). Typically this will involve calling realloc. * * The application may call beforepoll with fds==NULL and * *nfds_io==0 in order to find out how much space is needed. * * *timeout_upd is as for poll(2): it's in milliseconds, and * negative values mean no timeout (infinity). * libxl_osevent_beforepoll will only reduce the timeout, naturally. */ int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io, struct pollfd *fds, int *timeout_upd, struct timeval now) LIBXL_EXTERNAL_CALLERS_ONLY; /* nfds and fds[0..nfds] must be from the most recent call to * _beforepoll, as modified by poll. (It is therefore not possible * to have multiple threads simultaneously polling using this * interface.) * * This function actually performs all of the IO and other actions, * and generates events (libxl_event), which are implied by either * (a) the time of day or (b) both (i) the returned information from * _beforepoll, and (ii) the results from poll specified in * fds[0..nfds-1]. Generated events can then be retrieved by * libxl_event_check. */ void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds, struct timeval now) LIBXL_EXTERNAL_CALLERS_ONLY; typedef struct libxl_osevent_hooks { int (*fd_register)(void *user, int fd, void **for_app_registration_out, short events, void *for_libxl); int (*fd_modify)(void *user, int fd, void **for_app_registration_update, short events); void (*fd_deregister)(void *user, int fd, void *for_app_registration); int (*timeout_register)(void *user, void **for_app_registration_out, struct timeval abs, void *for_libxl); int (*timeout_modify)(void *user, void **for_app_registration_update, struct timeval abs) /* only ever called with abs={0,0}, meaning ASAP */; void (*timeout_deregister)(void *user, void *for_app_registration) /* will never be called */; } libxl_osevent_hooks; /* The application which calls register_fd_hooks promises to * maintain a register of fds and timeouts that libxl is interested * in, and make calls into libxl (libxl_osevent_occurred_*) * when those fd events and timeouts occur. This is more efficient * than _beforepoll/_afterpoll if there are many fds (which can * happen if the same libxl application is managing many domains). * * For an fd event, events is as for poll(). register or modify may * be called with events==0, in which case it must still work * normally, just not generate any events. * * For a timeout event, milliseconds is as for poll(). * Specifically, negative values of milliseconds mean NO TIMEOUT. * This is used by libxl to temporarily disable a timeout. * * If the register or modify hook succeeds it may update * *for_app_registration_out/_update and must then return 0. * On entry to register, *for_app_registration_out is always NULL. * * A registration or modification hook may fail, in which case it * must leave the registration state of the fd or timeout unchanged. * It may then either return ERROR_OSEVENT_REG_FAIL or any positive * int. The value returned will be passed up through libxl and * eventually returned back to the application. When register * fails, any value stored into *for_registration_out is ignored by * libxl; when modify fails, any changed value stored into * *for_registration_update is honoured by libxl and will be passed * to future modify or deregister calls. * * libxl may want to register more than one callback for any one fd; * in that case: (i) each such registration will have at least one bit * set in revents which is unique to that registration; (ii) if an * event occurs which is relevant for multiple registrations the * application's event system may call libxl_osevent_occurred_fd * for one, some, or all of those registrations. * * If fd_modify is used, it is permitted for the application's event * system to still make calls to libxl_osevent_occurred_fd for the * "old" set of requested events; these will be safely ignored by * libxl. * * libxl will remember the value stored in *for_app_registration_out * (or *for_app_registration_update) by a successful call to * register (or modify), and pass it to subsequent calls to modify * or deregister. * * Note that the application must cope with a call from libxl to * timeout_modify racing with its own call to * libxl__osevent_occurred_timeout. libxl guarantees that * timeout_modify will only be called with abs={0,0} but the * application must still ensure that libxl's attempt to cause the * timeout to occur immediately is safely ignored even the timeout is * actually already in the process of occurring. * * timeout_deregister is not used because it forms part of a * deprecated unsafe mode of use of the API. * * osevent_register_hooks may be called only once for each libxl_ctx. * libxl may make calls to register/modify/deregister from within * any libxl function (indeed, it will usually call register from * register_event_hooks). Conversely, the application MUST NOT make * the event occurrence calls (libxl_osevent_occurred_*) into libxl * reentrantly from within libxl (for example, from within the * register/modify functions). * * Lock hierarchy: the register/modify/deregister functions may be * called with locks held. These locks (the "libxl internal locks") * are inside the libxl_ctx. Therefore, if those register functions * acquire any locks of their own ("caller register locks") outside * libxl, to avoid deadlock one of the following must hold for each * such caller register lock: * (a) "acquire libxl internal locks before caller register lock": * No libxl function may be called with the caller register * lock held. * (b) "acquire caller register lock before libxl internal locks": * No libxl function may be called _without_ the caller * register lock held. * Of these we would normally recommend (a). * * The value *hooks is not copied and must outlast the libxl_ctx. */ void libxl_osevent_register_hooks(libxl_ctx *ctx, const libxl_osevent_hooks *hooks, void *user); /* It is NOT legal to call _occurred_ reentrantly within any libxl * function. Specifically it is NOT legal to call it from within * a register callback. Conversely, libxl MAY call register/deregister * from within libxl_event_occurred_call_*. */ void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl, int fd, short events, short revents) LIBXL_EXTERNAL_CALLERS_ONLY; /* Implicitly, on entry to this function the timeout has been * deregistered. If _occurred_timeout is called, libxl will not * call timeout_deregister; if it wants to requeue the timeout it * will call timeout_register again. */ void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl) LIBXL_EXTERNAL_CALLERS_ONLY; /*======================================================================*/ /* * Subprocess handling. * * Unfortunately the POSIX interface makes this very awkward. * * There are two possible arrangements for collecting statuses from * wait/waitpid. * * For naive programs: * * libxl will keep a SIGCHLD handler installed whenever it has an * active (unreaped) child. It will reap all children with * wait(); any children it does not recognise will be passed to * the application via an optional callback (and will result in * logged warnings if no callback is provided or the callback * denies responsibility for the child). * * libxl may have children whenever: * * - libxl is performing an operation which can be made * asynchronous; ie one taking a libxl_asyncop_how, even * if NULL is passed indicating that the operation is * synchronous; or * * - events of any kind are being generated, as requested * by libxl_evenable_.... * * A multithreaded application which is naive in this sense may * block SIGCHLD on some of its threads, but there must be at * least one thread that has SIGCHLD unblocked. libxl will not * modify the blocking flag for SIGCHLD (except that it may create * internal service threads with all signals blocked). * * A naive program must only have at any one time only * one libxl context which might have children. * * For programs which run their own children alongside libxl's: * * A program which does this must call libxl_childproc_setmode. * There are three options: * * libxl_sigchld_owner_libxl: * * While any libxl operation which might use child processes * is running, works like libxl_sigchld_owner_libxl_always; * but, deinstalls the handler the rest of the time. * * In this mode, the application, while it uses any libxl * operation which might create or use child processes (see * above): * - Must not have any child processes running. * - Must not install a SIGCHLD handler. * - Must not reap any children. * * This is the default (i.e. if setmode is not called, or 0 is * passed for hooks). * * libxl_sigchld_owner_mainloop: * * The application must install a SIGCHLD handler and reap (at * least) all of libxl's children and pass their exit status to * libxl by calling libxl_childproc_exited. (If the application * has multiple libxl ctx's, it must call libxl_childproc_exited * on each ctx.) * * libxl_sigchld_owner_libxl_always: * * The application expects this libxl ctx to reap all of the * process's children, and provides a callback to be notified of * their exit statuses. The application must have only one * libxl_ctx configured this way. * * libxl_sigchld_owner_libxl_always_selective_reap: * * The application expects to reap all of its own children * synchronously, and does not use SIGCHLD. libxl is to install * a SIGCHLD handler. The application may have multiple * libxl_ctxs configured this way; in which case all of its ctxs * must be so configured. */ typedef enum { /* libxl owns SIGCHLD whenever it has a child, and reaps * all children, including those not spawned by libxl. */ libxl_sigchld_owner_libxl, /* Application promises to discover when SIGCHLD occurs and call * libxl_childproc_exited or libxl_childproc_sigchld_occurred (but * NOT from within a signal handler). libxl will not itself * arrange to (un)block or catch SIGCHLD. */ libxl_sigchld_owner_mainloop, /* libxl owns SIGCHLD all the time, and the application is * relying on libxl's event loop for reaping its children too. */ libxl_sigchld_owner_libxl_always, /* libxl owns SIGCHLD all the time, but it must only reap its own * children. The application will reap its own children * synchronously with waitpid, without the assistance of SIGCHLD. */ libxl_sigchld_owner_libxl_always_selective_reap, } libxl_sigchld_owner; typedef struct { libxl_sigchld_owner chldowner; /* All of these are optional: */ /* Called by libxl instead of fork. Should behave exactly like * fork, including setting errno etc. May NOT reenter into libxl. * Application may use this to discover pids of libxl's children, * for example. */ pid_t (*fork_replacement)(void *user); /* With libxl_sigchld_owner_libxl, called by libxl when it has * reaped a pid. (Not permitted with _owner_mainloop.) * * Should return 0 if the child was recognised by the application * (or if the application does not keep those kind of records), * ERROR_UNKNOWN_CHILD if the application knows that the child is not * the application's; if it returns another error code it is a * disaster as described for libxl_event_register_callbacks. * (libxl will report unexpected children to its error log.) * * If not supplied, the application is assumed not to start * any children of its own. * * This function is NOT called from within the signal handler. * Rather it will be called from inside a libxl's event handling * code and thus only when libxl is running, for example from * within libxl_event_wait. (libxl uses the self-pipe trick * to implement this.) * * childproc_exited_callback may call back into libxl, but it * is best to avoid making long-running libxl calls as that might * stall the calling event loop while the nested operation * completes. */ int (*reaped_callback)(pid_t, int status, void *user); } libxl_childproc_hooks; /* hooks may be 0 in which is equivalent to &{ libxl_sigchld_owner_libxl, 0, 0 } * * May not be called when libxl might have any child processes, or the * behaviour is undefined. So it is best to call this at * initialisation. */ void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks, void *user); /* * This function is for an application which owns SIGCHLD and which * reaps all of the process's children, and dispatches the exit status * to the correct place inside the application. * * May be called only by an application which has called setmode with * chldowner == libxl_sigchld_owner_mainloop. If pid was a process started * by this instance of libxl, returns 0 after doing whatever * processing is appropriate. Otherwise silently returns * ERROR_UNKNOWN_CHILD. No other error returns are possible. * * May NOT be called from within a signal handler which might * interrupt any libxl operation. The application will almost * certainly need to use the self-pipe trick (or a working pselect or * ppoll) to implement this. */ int libxl_childproc_reaped(libxl_ctx *ctx, pid_t, int status) LIBXL_EXTERNAL_CALLERS_ONLY; /* * This function is for an application which owns SIGCHLD but which * doesn't keep track of all of its own children in a manner suitable * for reaping all of them and then dispatching them. * * Such an the application must notify libxl, by calling this * function, that a SIGCHLD occurred. libxl will then check all its * children, reap any that are ready, and take any action necessary - * but it will not reap anything else. * * May be called only by an application which has called setmode with * chldowner == libxl_sigchld_owner_mainloop. * * May NOT be called from within a signal handler which might * interrupt any libxl operation (just like libxl_childproc_reaped). */ void libxl_childproc_sigchld_occurred(libxl_ctx *ctx) LIBXL_EXTERNAL_CALLERS_ONLY; /* * An application which initialises a libxl_ctx in a parent process * and then forks a child which does not quickly exec, must * instead libxl_postfork_child_noexec in the child. One call * on any existing (or specially made) ctx is sufficient; after * this all previously existing libxl_ctx's are invalidated and * must not be used - or even freed. It is harmless to call this * postfork function and then exec anyway. * * Until libxl_postfork_child_noexec has returned: * - No other libxl calls may be made. * - If any libxl ctx was configured handle the process's SIGCHLD, * the child may not create further (grand)child processes, nor * manipulate SIGCHLD. * * libxl_postfork_child_noexec may not reclaim all the resources * associated with the libxl ctx. This includes but is not limited * to: ordinary memory; files on disk and in /var/run; file * descriptors; memory mapped into the process from domains being * managed (grant maps); Xen event channels. Use of libxl in * processes which fork long-lived children is not recommended for * this reason. libxl_postfork_child_noexec is provided so that * an application can make further libxl calls in a child which * is going to exec or exit soon. */ void libxl_postfork_child_noexec(libxl_ctx *ctx); #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_test_fdevent.h0000664000175000017500000000060713256712137016767 0ustar smbsmb#ifndef TEST_FDEVENT_H #define TEST_FDEVENT_H #include int libxl_test_fdevent(libxl_ctx *ctx, int fd, short events, libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; /* This operation waits for one of the poll events to occur on fd, and * then completes successfully. (Or, it can be aborted.) */ #endif /*TEST_FDEVENT_H*/ xen-4.9.2/tools/libxl/libxl_libfdt_compat.h0000664000175000017500000000677013256712137017113 0ustar smbsmb/* * Copyright (C) 2006 David Gibson, IBM Corporation. * * This file is part of libxl, and was originally taken from libfdt. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Additionally, this particular file is dual licensed. That is, * alternatively, at your option: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Note that this applies only to this file, and other files with a * similar notice. Also, note that when the same code is distributed * along with the rest of libxl, you must comply with the terms of the * LGPLv2.1 for the whole of libxl including this file. * * The intent is to permit, in particular, upstream libfdt to * incorporate improvements to this file within upstream libfdt. At * the time of writing, upstream libfdt is dual licensed: 2-clause BSD * (as above) and GPLv2-or-later. The 2-clause BSD licence is * compatible with both GPLv2-or-later and LGPLv2.1-only; this permits * copying in both directions, and the optional licence upgrade to a * copyleft licence by libdft upstream or the Xen Project, * respectively. */ #ifndef LIBXL_LIBFDT_COMPAT_H #define LIBXL_LIBFDT_COMPAT_H #include "libxl_internal.h" #include #if !HAVE_DECL_FDT_FIRST_SUBNODE _hidden int fdt_first_subnode(const void *fdt, int offset); #endif #if !HAVE_DECL_FDT_NEXT_SUBNODE _hidden int fdt_next_subnode(const void *fdt, int offset); #endif #if !HAVE_DECL_FDT_PROPERTY_U32 static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) { uint32_t tmp = cpu_to_fdt32(val); return fdt_property(fdt, name, &tmp, sizeof(tmp)); } #endif #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_disk.c0000664000175000017500000000500113256712137015400 0ustar smbsmb#include "libxl_osdeps.h" /* must come before any other headers */ #include "libxlu_internal.h" #include "libxlu_disk_l.h" #include "libxlu_disk_i.h" #include "libxlu_cfg_i.h" void xlu__disk_err(DiskParseContext *dpc, const char *erroneous, const char *message) { fprintf(dpc->cfg->report, "%s: config parsing error in disk specification: %s" "%s%s%s" " in `%s'\n", dpc->cfg->config_source, message, erroneous?": near `":"", erroneous?erroneous:"", erroneous?"'":"", dpc->spec); if (!dpc->err) dpc->err= EINVAL; } static int dpc_prep(DiskParseContext *dpc, const char *spec) { int e; dpc->spec = spec; e = xlu__disk_yylex_init_extra(dpc, &dpc->scanner); if (e) goto fail; dpc->buf = xlu__disk_yy_scan_bytes(spec, strlen(spec), dpc->scanner); if (!dpc->buf) { e = ENOMEM; goto fail; } return 0; fail: fprintf(dpc->cfg->report, "cannot init disk scanner: %s\n", strerror(errno)); return e; } static void dpc_dispose(DiskParseContext *dpc) { if (dpc->buf) { xlu__disk_yy_delete_buffer(dpc->buf, dpc->scanner); dpc->buf = 0; } if (dpc->scanner) { xlu__disk_yylex_destroy(dpc->scanner); dpc->scanner = 0; } } int xlu_disk_parse(XLU_Config *cfg, int nspecs, const char *const *specs, libxl_device_disk *disk) { DiskParseContext dpc; int i, e; memset(&dpc,0,sizeof(dpc)); dpc.cfg = cfg; dpc.scanner = 0; dpc.disk = disk; disk->readwrite = 1; for (i=0; iformat == LIBXL_DISK_FORMAT_UNKNOWN) { disk->format = LIBXL_DISK_FORMAT_RAW; } if (disk->is_cdrom) { disk->removable = 1; disk->readwrite = 0; if (!disk->pdev_path || !strcmp(disk->pdev_path, "")) disk->format = LIBXL_DISK_FORMAT_EMPTY; } if (!disk->vdev) { xlu__disk_err(&dpc,0, "no vdev specified"); goto x_err; } if (!disk->pdev_path && !disk->removable) { xlu__disk_err(&dpc,0,"no target specified (and device not removable)"); goto x_err; } x_err: dpc_dispose(&dpc); return dpc.err; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/test_fdderegrace.c0000664000175000017500000000240313256712137016364 0ustar smbsmb#include "test_common.h" #include "libxl_test_fdevent.h" int main(int argc, char **argv) { int rc, i; libxl_asyncop_how how; libxl_event *event; test_common_setup(XTL_DEBUG); how.callback = NULL; how.u.for_event = 1; int fd = open("/dev/null", O_RDONLY); assert(fd > 0); rc = libxl_test_fdevent(ctx, fd, POLLIN, &how); assert(!rc); test_common_beforepoll(); rc = libxl_ao_abort(ctx, &how); assert(!rc); rc = libxl_event_check(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0); assert(!rc); assert(event); assert(event->for_user == how.u.for_event); assert(event->type == LIBXL_EVENT_TYPE_OPERATION_COMPLETE); assert(event->u.operation_complete.rc == ERROR_ABORTED); close(fd); test_common_dopoll(); for (i=0; i * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len, uint32_t *ssidref) { int rc; rc = xc_flask_context_to_sid(ctx->xch, buf, len, ssidref); return rc; } int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref, char **buf, size_t *len) { int rc; char tmp[XC_PAGE_SIZE]; rc = xc_flask_sid_to_context(ctx->xch, ssidref, tmp, sizeof(tmp)); if (!rc) { *len = strlen(tmp); *buf = strdup(tmp); } return rc; } int libxl_flask_getenforce(libxl_ctx *ctx) { int rc; rc = xc_flask_getenforce(ctx->xch); return rc; } int libxl_flask_setenforce(libxl_ctx *ctx, int mode) { int rc; rc = xc_flask_setenforce(ctx->xch, mode); return rc; } int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size) { int rc; rc = xc_flask_load(ctx->xch, policy, size); return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_pci.c0000664000175000017500000002034313256712137015227 0ustar smbsmb#include "libxl_osdeps.h" /* must come before any other headers */ #include "libxlu_internal.h" #include "libxlu_disk_l.h" #include "libxlu_disk_i.h" #include "libxlu_cfg_i.h" #define XLU__PCI_ERR(_c, _x, _a...) \ if((_c) && (_c)->report) fprintf((_c)->report, _x, ##_a) static int hex_convert(const char *str, unsigned int *val, unsigned int mask) { unsigned long ret; char *end; ret = strtoul(str, &end, 16); if ( end == str || *end != '\0' ) return -1; if ( ret & ~mask ) return -1; *val = (unsigned int)ret & mask; return 0; } static int pcidev_struct_fill(libxl_device_pci *pcidev, unsigned int domain, unsigned int bus, unsigned int dev, unsigned int func, unsigned int vdevfn) { pcidev->domain = domain; pcidev->bus = bus; pcidev->dev = dev; pcidev->func = func; pcidev->vdevfn = vdevfn; return 0; } #define STATE_DOMAIN 0 #define STATE_BUS 1 #define STATE_DEV 2 #define STATE_FUNC 3 #define STATE_VSLOT 4 #define STATE_OPTIONS_K 6 #define STATE_OPTIONS_V 7 #define STATE_TERMINAL 8 #define STATE_TYPE 9 #define STATE_RDM_STRATEGY 10 #define STATE_RESERVE_POLICY 11 int xlu_pci_parse_bdf(XLU_Config *cfg, libxl_device_pci *pcidev, const char *str) { unsigned state = STATE_DOMAIN; unsigned dom, bus, dev, func, vslot = 0; char *buf2, *tok, *ptr, *end, *optkey = NULL; if ( NULL == (buf2 = ptr = strdup(str)) ) return ERROR_NOMEM; for(tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) { switch(state) { case STATE_DOMAIN: if ( *ptr == ':' ) { state = STATE_BUS; *ptr = '\0'; if ( hex_convert(tok, &dom, 0xffff) ) goto parse_error; tok = ptr + 1; } break; case STATE_BUS: if ( *ptr == ':' ) { state = STATE_DEV; *ptr = '\0'; if ( hex_convert(tok, &bus, 0xff) ) goto parse_error; tok = ptr + 1; }else if ( *ptr == '.' ) { state = STATE_FUNC; *ptr = '\0'; if ( dom & ~0xff ) goto parse_error; bus = dom; dom = 0; if ( hex_convert(tok, &dev, 0xff) ) goto parse_error; tok = ptr + 1; } break; case STATE_DEV: if ( *ptr == '.' ) { state = STATE_FUNC; *ptr = '\0'; if ( hex_convert(tok, &dev, 0xff) ) goto parse_error; tok = ptr + 1; } break; case STATE_FUNC: if ( *ptr == '\0' || *ptr == '@' || *ptr == ',' ) { switch( *ptr ) { case '\0': state = STATE_TERMINAL; break; case '@': state = STATE_VSLOT; break; case ',': state = STATE_OPTIONS_K; break; } *ptr = '\0'; if ( !strcmp(tok, "*") ) { pcidev->vfunc_mask = LIBXL_PCI_FUNC_ALL; }else{ if ( hex_convert(tok, &func, 0x7) ) goto parse_error; pcidev->vfunc_mask = (1 << 0); } tok = ptr + 1; } break; case STATE_VSLOT: if ( *ptr == '\0' || *ptr == ',' ) { state = ( *ptr == ',' ) ? STATE_OPTIONS_K : STATE_TERMINAL; *ptr = '\0'; if ( hex_convert(tok, &vslot, 0xff) ) goto parse_error; tok = ptr + 1; } break; case STATE_OPTIONS_K: if ( *ptr == '=' ) { state = STATE_OPTIONS_V; *ptr = '\0'; optkey = tok; tok = ptr + 1; } break; case STATE_OPTIONS_V: if ( *ptr == ',' || *ptr == '\0' ) { state = (*ptr == ',') ? STATE_OPTIONS_K : STATE_TERMINAL; *ptr = '\0'; if ( !strcmp(optkey, "msitranslate") ) { pcidev->msitranslate = atoi(tok); }else if ( !strcmp(optkey, "power_mgmt") ) { pcidev->power_mgmt = atoi(tok); }else if ( !strcmp(optkey, "permissive") ) { pcidev->permissive = atoi(tok); }else if ( !strcmp(optkey, "seize") ) { pcidev->seize = atoi(tok); } else if (!strcmp(optkey, "rdm_policy")) { if (!strcmp(tok, "strict")) { pcidev->rdm_policy = LIBXL_RDM_RESERVE_POLICY_STRICT; } else if (!strcmp(tok, "relaxed")) { pcidev->rdm_policy = LIBXL_RDM_RESERVE_POLICY_RELAXED; } else { XLU__PCI_ERR(cfg, "%s is not an valid PCI RDM property" " policy: 'strict' or 'relaxed'.", tok); goto parse_error; } } else { XLU__PCI_ERR(cfg, "Unknown PCI BDF option: %s", optkey); } tok = ptr + 1; } default: break; } } if ( tok != ptr || state != STATE_TERMINAL ) goto parse_error; /* Just a pretty way to fill in the values */ pcidev_struct_fill(pcidev, dom, bus, dev, func, vslot << 3); free(buf2); return 0; parse_error: free(buf2); return ERROR_INVAL; } int xlu_rdm_parse(XLU_Config *cfg, libxl_rdm_reserve *rdm, const char *str) { unsigned state = STATE_TYPE; char *buf2, *tok, *ptr, *end; if (NULL == (buf2 = ptr = strdup(str))) return ERROR_NOMEM; for (tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) { switch(state) { case STATE_TYPE: if (*ptr == '=') { state = STATE_RDM_STRATEGY; *ptr = '\0'; if (strcmp(tok, "strategy")) { XLU__PCI_ERR(cfg, "Unknown RDM state option: %s", tok); goto parse_error; } tok = ptr + 1; } break; case STATE_RDM_STRATEGY: if (*ptr == '\0' || *ptr == ',') { state = STATE_RESERVE_POLICY; *ptr = '\0'; if (!strcmp(tok, "host")) { rdm->strategy = LIBXL_RDM_RESERVE_STRATEGY_HOST; } else { XLU__PCI_ERR(cfg, "Unknown RDM strategy option: %s", tok); goto parse_error; } tok = ptr + 1; } break; case STATE_RESERVE_POLICY: if (*ptr == '=') { state = STATE_OPTIONS_V; *ptr = '\0'; if (strcmp(tok, "policy")) { XLU__PCI_ERR(cfg, "Unknown RDM property value: %s", tok); goto parse_error; } tok = ptr + 1; } break; case STATE_OPTIONS_V: if (*ptr == ',' || *ptr == '\0') { state = STATE_TERMINAL; *ptr = '\0'; if (!strcmp(tok, "strict")) { rdm->policy = LIBXL_RDM_RESERVE_POLICY_STRICT; } else if (!strcmp(tok, "relaxed")) { rdm->policy = LIBXL_RDM_RESERVE_POLICY_RELAXED; } else { XLU__PCI_ERR(cfg, "Unknown RDM property policy value: %s", tok); goto parse_error; } tok = ptr + 1; } default: break; } } if (tok != ptr || state != STATE_TERMINAL) goto parse_error; free(buf2); return 0; parse_error: free(buf2); return ERROR_INVAL; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_qmp.c0000664000175000017500000010225513256712137015067 0ustar smbsmb/* * Copyright (C) 2011 Citrix Ltd. * Author Anthony PERARD * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * This file implement a client for QMP (QEMU Monitor Protocol). For the * Specification, see in the QEMU repository. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include #include #include "_libxl_list.h" #include "libxl_internal.h" /* #define DEBUG_RECEIVED */ #ifdef DEBUG_RECEIVED # define DEBUG_REPORT_RECEIVED(dom, buf, len) \ LOGD(DEBUG, dom, "received: '%.*s'", len, buf) #else # define DEBUG_REPORT_RECEIVED(dom, buf, len) ((void)0) #endif /* * QMP types & constant */ #define QMP_RECEIVE_BUFFER_SIZE 4096 #define PCI_PT_QDEV_ID "pci-pt-%02x_%02x.%01x" typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp, const libxl__json_object *tree, void *opaque); typedef struct qmp_request_context { int rc; } qmp_request_context; typedef struct callback_id_pair { int id; qmp_callback_t callback; void *opaque; qmp_request_context *context; LIBXL_STAILQ_ENTRY(struct callback_id_pair) next; } callback_id_pair; struct libxl__qmp_handler { struct sockaddr_un addr; int qmp_fd; bool connected; time_t timeout; /* wait_for_id will be used by the synchronous send function */ int wait_for_id; char buffer[QMP_RECEIVE_BUFFER_SIZE + 1]; libxl__yajl_ctx *yajl_ctx; libxl_ctx *ctx; uint32_t domid; int last_id_used; LIBXL_STAILQ_HEAD(callback_list, callback_id_pair) callback_list; }; static int qmp_send(libxl__qmp_handler *qmp, const char *cmd, libxl__json_object *args, qmp_callback_t callback, void *opaque, qmp_request_context *context); static const int QMP_SOCKET_CONNECT_TIMEOUT = 5; /* * QMP callbacks functions */ static int store_serial_port_info(libxl__qmp_handler *qmp, const char *chardev, int port) { GC_INIT(qmp->ctx); char *path = NULL; int ret = 0; if (!(chardev && strncmp("pty:", chardev, 4) == 0)) { return 0; } path = libxl__xs_get_dompath(gc, qmp->domid); path = GCSPRINTF("%s/serial/%d/tty", path, port); ret = libxl__xs_printf(gc, XBT_NULL, path, "%s", chardev + 4); GC_FREE; return ret; } static int register_serials_chardev_callback(libxl__qmp_handler *qmp, const libxl__json_object *o, void *unused) { const libxl__json_object *obj = NULL; const libxl__json_object *label = NULL; const char *s = NULL; int i = 0; const char *chardev = NULL; int ret = 0; for (i = 0; (obj = libxl__json_array_get(o, i)); i++) { if (!libxl__json_object_is_map(obj)) continue; label = libxl__json_map_get("label", obj, JSON_STRING); s = libxl__json_object_get_string(label); if (s && strncmp("serial", s, strlen("serial")) == 0) { const libxl__json_object *filename = NULL; char *endptr = NULL; int port_number; filename = libxl__json_map_get("filename", obj, JSON_STRING); chardev = libxl__json_object_get_string(filename); s += strlen("serial"); port_number = strtol(s, &endptr, 10); if (*s == 0 || *endptr != 0) { LIBXL__LOGD(qmp->ctx, LIBXL__LOG_ERROR, qmp->domid, "Invalid serial port number: %s", s); return -1; } ret = store_serial_port_info(qmp, chardev, port_number); if (ret) { LIBXL__LOGD_ERRNO(qmp->ctx, LIBXL__LOG_ERROR, qmp->domid, "Failed to store serial port information" " in xenstore"); return ret; } } }; return ret; } static int qmp_write_domain_console_item(libxl__gc *gc, int domid, const char *item, const char *value) { char *path; path = libxl__xs_get_dompath(gc, domid); path = GCSPRINTF("%s/console/%s", path, item); return libxl__xs_printf(gc, XBT_NULL, path, "%s", value); } static int qmp_register_vnc_callback(libxl__qmp_handler *qmp, const libxl__json_object *o, void *unused) { GC_INIT(qmp->ctx); const libxl__json_object *obj; const char *addr, *port; int rc = -1; if (!libxl__json_object_is_map(o)) { goto out; } obj = libxl__json_map_get("enabled", o, JSON_BOOL); if (!obj || !libxl__json_object_get_bool(obj)) { rc = 0; goto out; } obj = libxl__json_map_get("host", o, JSON_STRING); addr = libxl__json_object_get_string(obj); obj = libxl__json_map_get("service", o, JSON_STRING); port = libxl__json_object_get_string(obj); if (!addr || !port) { LOGD(ERROR, qmp->domid, "Failed to retreive VNC connect information."); goto out; } rc = qmp_write_domain_console_item(gc, qmp->domid, "vnc-listen", addr); if (!rc) rc = qmp_write_domain_console_item(gc, qmp->domid, "vnc-port", port); out: GC_FREE; return rc; } static int qmp_capabilities_callback(libxl__qmp_handler *qmp, const libxl__json_object *o, void *unused) { qmp->connected = true; return 0; } /* * QMP commands */ static int enable_qmp_capabilities(libxl__qmp_handler *qmp) { return qmp_send(qmp, "qmp_capabilities", NULL, qmp_capabilities_callback, NULL, NULL); } /* * Helpers */ static libxl__qmp_message_type qmp_response_type(libxl__qmp_handler *qmp, const libxl__json_object *o) { libxl__qmp_message_type type; libxl__json_map_node *node = NULL; int i = 0; for (i = 0; (node = libxl__json_map_node_get(o, i)); i++) { if (libxl__qmp_message_type_from_string(node->map_key, &type) == 0) return type; } return LIBXL__QMP_MESSAGE_TYPE_INVALID; } static callback_id_pair *qmp_get_callback_from_id(libxl__qmp_handler *qmp, const libxl__json_object *o) { const libxl__json_object *id_object = libxl__json_map_get("id", o, JSON_INTEGER); int id = -1; callback_id_pair *pp = NULL; if (id_object) { id = libxl__json_object_get_integer(id_object); LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) { if (pp->id == id) { return pp; } } } return NULL; } static void qmp_handle_error_response(libxl__gc *gc, libxl__qmp_handler *qmp, const libxl__json_object *resp) { callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp); resp = libxl__json_map_get("error", resp, JSON_MAP); resp = libxl__json_map_get("desc", resp, JSON_STRING); if (pp) { if (pp->callback) { int rc = pp->callback(qmp, NULL, pp->opaque); if (pp->context) { pp->context->rc = rc; } } if (pp->id == qmp->wait_for_id) { /* tell that the id have been processed */ qmp->wait_for_id = 0; } LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next); free(pp); } LOGD(ERROR, qmp->domid, "received an error message from QMP server: %s", libxl__json_object_get_string(resp)); } static int qmp_handle_response(libxl__gc *gc, libxl__qmp_handler *qmp, const libxl__json_object *resp) { libxl__qmp_message_type type = LIBXL__QMP_MESSAGE_TYPE_INVALID; type = qmp_response_type(qmp, resp); LOGD(DEBUG, qmp->domid, "message type: %s", libxl__qmp_message_type_to_string(type)); switch (type) { case LIBXL__QMP_MESSAGE_TYPE_QMP: /* On the greeting message from the server, enable QMP capabilities */ return enable_qmp_capabilities(qmp); case LIBXL__QMP_MESSAGE_TYPE_RETURN: { callback_id_pair *pp = qmp_get_callback_from_id(qmp, resp); if (pp) { if (pp->callback) { int rc = pp->callback(qmp, libxl__json_map_get("return", resp, JSON_ANY), pp->opaque); if (pp->context) { pp->context->rc = rc; } } if (pp->id == qmp->wait_for_id) { /* tell that the id have been processed */ qmp->wait_for_id = 0; } LIBXL_STAILQ_REMOVE(&qmp->callback_list, pp, callback_id_pair, next); free(pp); } return 0; } case LIBXL__QMP_MESSAGE_TYPE_ERROR: qmp_handle_error_response(gc, qmp, resp); return -1; case LIBXL__QMP_MESSAGE_TYPE_EVENT: return 0; case LIBXL__QMP_MESSAGE_TYPE_INVALID: return -1; } return 0; } /* * Handler functions */ static libxl__qmp_handler *qmp_init_handler(libxl__gc *gc, uint32_t domid) { libxl__qmp_handler *qmp = NULL; qmp = calloc(1, sizeof (libxl__qmp_handler)); if (qmp == NULL) { LOGED(ERROR, domid, "Failed to allocate qmp_handler"); return NULL; } qmp->ctx = CTX; qmp->domid = domid; qmp->timeout = 5; LIBXL_STAILQ_INIT(&qmp->callback_list); return qmp; } static int qmp_open(libxl__qmp_handler *qmp, const char *qmp_socket_path, int timeout) { int ret = -1; int i = 0; qmp->qmp_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (qmp->qmp_fd < 0) { goto out; } ret = libxl_fd_set_nonblock(qmp->ctx, qmp->qmp_fd, 1); if (ret) { ret = -1; goto out; } ret = libxl_fd_set_cloexec(qmp->ctx, qmp->qmp_fd, 1); if (ret) { ret = -1; goto out; } if (sizeof (qmp->addr.sun_path) <= strlen(qmp_socket_path)) { ret = -1; goto out; } memset(&qmp->addr, 0, sizeof (qmp->addr)); qmp->addr.sun_family = AF_UNIX; strncpy(qmp->addr.sun_path, qmp_socket_path, sizeof (qmp->addr.sun_path)-1); do { ret = connect(qmp->qmp_fd, (struct sockaddr *) &qmp->addr, sizeof (qmp->addr)); if (ret == 0) break; if (errno == ENOENT || errno == ECONNREFUSED) { /* ENOENT : Socket may not have shown up yet * ECONNREFUSED : Leftover socket hasn't been removed yet */ continue; } ret = -1; goto out; } while ((++i / 5 <= timeout) && (usleep(200 * 1000) <= 0)); out: if (ret == -1 && qmp->qmp_fd > -1) close(qmp->qmp_fd); return ret; } static void qmp_close(libxl__qmp_handler *qmp) { callback_id_pair *pp = NULL; callback_id_pair *tmp = NULL; close(qmp->qmp_fd); LIBXL_STAILQ_FOREACH(pp, &qmp->callback_list, next) { free(tmp); tmp = pp; } free(tmp); } static int qmp_next(libxl__gc *gc, libxl__qmp_handler *qmp) { ssize_t rd; char *s = NULL; char *s_end = NULL; char *incomplete = NULL; size_t incomplete_size = 0; int rc = 0; do { fd_set rfds; int ret = 0; struct timeval timeout = { .tv_sec = qmp->timeout, .tv_usec = 0, }; FD_ZERO(&rfds); FD_SET(qmp->qmp_fd, &rfds); ret = select(qmp->qmp_fd + 1, &rfds, NULL, NULL, &timeout); if (ret == 0) { LOGD(ERROR, qmp->domid, "timeout"); return -1; } else if (ret < 0) { if (errno == EINTR) continue; LOGED(ERROR, qmp->domid, "Select error"); return -1; } rd = read(qmp->qmp_fd, qmp->buffer, QMP_RECEIVE_BUFFER_SIZE); if (rd == 0) { LOGD(ERROR, qmp->domid, "Unexpected end of socket"); return -1; } else if (rd < 0) { LOGED(ERROR, qmp->domid, "Socket read error"); return rd; } qmp->buffer[rd] = '\0'; DEBUG_REPORT_RECEIVED(qmp->domid, qmp->buffer, rd); do { char *end = NULL; if (incomplete) { size_t current_pos = s - incomplete; incomplete = libxl__realloc(gc, incomplete, incomplete_size + rd + 1); strncat(incomplete + incomplete_size, qmp->buffer, rd); s = incomplete + current_pos; incomplete_size += rd; s_end = incomplete + incomplete_size; } else { incomplete = libxl__strndup(gc, qmp->buffer, rd); incomplete_size = rd; s = incomplete; s_end = s + rd; rd = 0; } end = strstr(s, "\r\n"); if (end) { libxl__json_object *o = NULL; *end = '\0'; o = libxl__json_parse(gc, s); if (o) { rc = qmp_handle_response(gc, qmp, o); } else { LOGD(ERROR, qmp->domid, "Parse error of : %s", s); return -1; } s = end + 2; } else { break; } } while (s < s_end); } while (s < s_end); return rc; } static char *qmp_send_prepare(libxl__gc *gc, libxl__qmp_handler *qmp, const char *cmd, libxl__json_object *args, qmp_callback_t callback, void *opaque, qmp_request_context *context) { const unsigned char *buf = NULL; char *ret = NULL; libxl_yajl_length len = 0; yajl_gen_status s; yajl_gen hand; callback_id_pair *elm = NULL; hand = libxl_yajl_gen_alloc(NULL); if (!hand) { return NULL; } yajl_gen_map_open(hand); libxl__yajl_gen_asciiz(hand, "execute"); libxl__yajl_gen_asciiz(hand, cmd); libxl__yajl_gen_asciiz(hand, "id"); yajl_gen_integer(hand, ++qmp->last_id_used); if (args) { libxl__yajl_gen_asciiz(hand, "arguments"); libxl__json_object_to_yajl_gen(gc, hand, args); } yajl_gen_map_close(hand); s = yajl_gen_get_buf(hand, &buf, &len); if (s) { LOGD(ERROR, qmp->domid, "Failed to generate a qmp command"); goto out; } elm = malloc(sizeof (callback_id_pair)); if (elm == NULL) { LOGED(ERROR, qmp->domid, "Failed to allocate a QMP callback"); goto out; } elm->id = qmp->last_id_used; elm->callback = callback; elm->opaque = opaque; elm->context = context; LIBXL_STAILQ_INSERT_TAIL(&qmp->callback_list, elm, next); ret = libxl__strndup(gc, (const char*)buf, len); LOGD(DEBUG, qmp->domid, "next qmp command: '%s'", buf); out: yajl_gen_free(hand); return ret; } static int qmp_send(libxl__qmp_handler *qmp, const char *cmd, libxl__json_object *args, qmp_callback_t callback, void *opaque, qmp_request_context *context) { char *buf = NULL; int rc = -1; GC_INIT(qmp->ctx); buf = qmp_send_prepare(gc, qmp, cmd, args, callback, opaque, context); if (buf == NULL) { goto out; } if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, buf, strlen(buf), "QMP command", "QMP socket")) goto out; if (libxl_write_exactly(qmp->ctx, qmp->qmp_fd, "\r\n", 2, "CRLF", "QMP socket")) goto out; rc = qmp->last_id_used; out: GC_FREE; return rc; } static int qmp_synchronous_send(libxl__qmp_handler *qmp, const char *cmd, libxl__json_object *args, qmp_callback_t callback, void *opaque, int ask_timeout) { int id = 0; int ret = 0; GC_INIT(qmp->ctx); qmp_request_context context = { .rc = 0 }; id = qmp_send(qmp, cmd, args, callback, opaque, &context); if (id <= 0) { return -1; } qmp->wait_for_id = id; while (qmp->wait_for_id == id) { if ((ret = qmp_next(gc, qmp)) < 0) { break; } } if (qmp->wait_for_id != id && ret == 0) { ret = context.rc; } GC_FREE; return ret; } static void qmp_free_handler(libxl__qmp_handler *qmp) { free(qmp); } /* * QMP Parameters Helpers */ static void qmp_parameters_common_add(libxl__gc *gc, libxl__json_object **param, const char *name, libxl__json_object *obj) { libxl__json_map_node *arg = NULL; if (!*param) { *param = libxl__json_object_alloc(gc, JSON_MAP); } GCNEW(arg); arg->map_key = libxl__strdup(gc, name); arg->obj = obj; flexarray_append((*param)->u.map, arg); } static void qmp_parameters_add_string(libxl__gc *gc, libxl__json_object **param, const char *name, const char *argument) { libxl__json_object *obj; obj = libxl__json_object_alloc(gc, JSON_STRING); obj->u.string = libxl__strdup(gc, argument); qmp_parameters_common_add(gc, param, name, obj); } static void qmp_parameters_add_bool(libxl__gc *gc, libxl__json_object **param, const char *name, bool b) { libxl__json_object *obj; obj = libxl__json_object_alloc(gc, JSON_BOOL); obj->u.b = b; qmp_parameters_common_add(gc, param, name, obj); } static void qmp_parameters_add_integer(libxl__gc *gc, libxl__json_object **param, const char *name, const int i) { libxl__json_object *obj; obj = libxl__json_object_alloc(gc, JSON_INTEGER); obj->u.i = i; qmp_parameters_common_add(gc, param, name, obj); } #define QMP_PARAMETERS_SPRINTF(args, name, format, ...) \ qmp_parameters_add_string(gc, args, name, GCSPRINTF(format, __VA_ARGS__)) /* * API */ libxl__qmp_handler *libxl__qmp_initialize(libxl__gc *gc, uint32_t domid) { int ret = 0; libxl__qmp_handler *qmp = NULL; char *qmp_socket; qmp = qmp_init_handler(gc, domid); if (!qmp) return NULL; qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid); if ((ret = qmp_open(qmp, qmp_socket, QMP_SOCKET_CONNECT_TIMEOUT)) < 0) { LOGED(ERROR, domid, "Connection error"); qmp_free_handler(qmp); return NULL; } LOGD(DEBUG, domid, "connected to %s", qmp_socket); /* Wait for the response to qmp_capabilities */ while (!qmp->connected) { if ((ret = qmp_next(gc, qmp)) < 0) { break; } } if (!qmp->connected) { LOGD(ERROR, domid, "Failed to connect to QMP"); libxl__qmp_close(qmp); return NULL; } return qmp; } void libxl__qmp_close(libxl__qmp_handler *qmp) { if (!qmp) return; qmp_close(qmp); qmp_free_handler(qmp); } void libxl__qmp_cleanup(libxl__gc *gc, uint32_t domid) { char *qmp_socket; qmp_socket = GCSPRINTF("%s/qmp-libxl-%d", libxl__run_dir_path(), domid); if (unlink(qmp_socket) == -1) { if (errno != ENOENT) { LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket); } } qmp_socket = GCSPRINTF("%s/qmp-libxenstat-%d", libxl__run_dir_path(), domid); if (unlink(qmp_socket) == -1) { if (errno != ENOENT) { LOGED(ERROR, domid, "Failed to remove QMP socket file %s", qmp_socket); } } } int libxl__qmp_query_serial(libxl__qmp_handler *qmp) { return qmp_synchronous_send(qmp, "query-chardev", NULL, register_serials_chardev_callback, NULL, qmp->timeout); } static int qmp_query_vnc(libxl__qmp_handler *qmp) { return qmp_synchronous_send(qmp, "query-vnc", NULL, qmp_register_vnc_callback, NULL, qmp->timeout); } static int pci_add_callback(libxl__qmp_handler *qmp, const libxl__json_object *response, void *opaque) { libxl_device_pci *pcidev = opaque; const libxl__json_object *bus = NULL; GC_INIT(qmp->ctx); int i, j, rc = -1; char *asked_id = GCSPRINTF(PCI_PT_QDEV_ID, pcidev->bus, pcidev->dev, pcidev->func); for (i = 0; (bus = libxl__json_array_get(response, i)); i++) { const libxl__json_object *devices = NULL; const libxl__json_object *device = NULL; const libxl__json_object *o = NULL; const char *id = NULL; devices = libxl__json_map_get("devices", bus, JSON_ARRAY); for (j = 0; (device = libxl__json_array_get(devices, j)); j++) { o = libxl__json_map_get("qdev_id", device, JSON_STRING); id = libxl__json_object_get_string(o); if (id && strcmp(asked_id, id) == 0) { int dev_slot, dev_func; o = libxl__json_map_get("slot", device, JSON_INTEGER); if (!o) goto out; dev_slot = libxl__json_object_get_integer(o); o = libxl__json_map_get("function", device, JSON_INTEGER); if (!o) goto out; dev_func = libxl__json_object_get_integer(o); pcidev->vdevfn = PCI_DEVFN(dev_slot, dev_func); rc = 0; goto out; } } } out: GC_FREE; return rc; } static int qmp_run_command(libxl__gc *gc, int domid, const char *cmd, libxl__json_object *args, qmp_callback_t callback, void *opaque) { libxl__qmp_handler *qmp = NULL; int rc = 0; qmp = libxl__qmp_initialize(gc, domid); if (!qmp) return ERROR_FAIL; rc = qmp_synchronous_send(qmp, cmd, args, callback, opaque, qmp->timeout); libxl__qmp_close(qmp); return rc; } int libxl__qmp_run_command_flexarray(libxl__gc *gc, int domid, const char *cmd, flexarray_t *array) { libxl__json_object *args = NULL; int i; void *name, *value; for (i = 0; i < array->count; i += 2) { flexarray_get(array, i, &name); flexarray_get(array, i + 1, &value); qmp_parameters_add_string(gc, &args, (char *)name, (char *)value); } return qmp_run_command(gc, domid, cmd, args, NULL, NULL); } int libxl__qmp_pci_add(libxl__gc *gc, int domid, libxl_device_pci *pcidev) { libxl__qmp_handler *qmp = NULL; libxl__json_object *args = NULL; char *hostaddr = NULL; int rc = 0; qmp = libxl__qmp_initialize(gc, domid); if (!qmp) return -1; hostaddr = GCSPRINTF("%04x:%02x:%02x.%01x", pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func); if (!hostaddr) return -1; qmp_parameters_add_string(gc, &args, "driver", "xen-pci-passthrough"); QMP_PARAMETERS_SPRINTF(&args, "id", PCI_PT_QDEV_ID, pcidev->bus, pcidev->dev, pcidev->func); qmp_parameters_add_string(gc, &args, "hostaddr", hostaddr); if (pcidev->vdevfn) { QMP_PARAMETERS_SPRINTF(&args, "addr", "%x.%x", PCI_SLOT(pcidev->vdevfn), PCI_FUNC(pcidev->vdevfn)); } /* * Version of QEMU prior to the XSA-131 fix did not support this * property and were effectively always in permissive mode. The * fix for XSA-131 switched the default to be restricted by * default and added the permissive property. * * Therefore in order to support both old and new QEMU we only set * the permissive flag if it is true. Users of older QEMU have no * reason to set the flag so this is ok. */ if (pcidev->permissive) qmp_parameters_add_bool(gc, &args, "permissive", true); rc = qmp_synchronous_send(qmp, "device_add", args, NULL, NULL, qmp->timeout); if (rc == 0) { rc = qmp_synchronous_send(qmp, "query-pci", NULL, pci_add_callback, pcidev, qmp->timeout); } libxl__qmp_close(qmp); return rc; } static int qmp_device_del(libxl__gc *gc, int domid, char *id) { libxl__json_object *args = NULL; qmp_parameters_add_string(gc, &args, "id", id); return qmp_run_command(gc, domid, "device_del", args, NULL, NULL); } int libxl__qmp_pci_del(libxl__gc *gc, int domid, libxl_device_pci *pcidev) { char *id = NULL; id = GCSPRINTF(PCI_PT_QDEV_ID, pcidev->bus, pcidev->dev, pcidev->func); return qmp_device_del(gc, domid, id); } int libxl__qmp_system_wakeup(libxl__gc *gc, int domid) { return qmp_run_command(gc, domid, "system_wakeup", NULL, NULL, NULL); } int libxl__qmp_save(libxl__gc *gc, int domid, const char *filename) { libxl__json_object *args = NULL; qmp_parameters_add_string(gc, &args, "filename", (char *)filename); return qmp_run_command(gc, domid, "xen-save-devices-state", args, NULL, NULL); } int libxl__qmp_restore(libxl__gc *gc, int domid, const char *state_file) { libxl__json_object *args = NULL; qmp_parameters_add_string(gc, &args, "filename", state_file); return qmp_run_command(gc, domid, "xen-load-devices-state", args, NULL, NULL); } static int qmp_change(libxl__gc *gc, libxl__qmp_handler *qmp, char *device, char *target, char *arg) { libxl__json_object *args = NULL; int rc = 0; qmp_parameters_add_string(gc, &args, "device", device); qmp_parameters_add_string(gc, &args, "target", target); if (arg) { qmp_parameters_add_string(gc, &args, "arg", arg); } rc = qmp_synchronous_send(qmp, "change", args, NULL, NULL, qmp->timeout); return rc; } int libxl__qmp_stop(libxl__gc *gc, int domid) { return qmp_run_command(gc, domid, "stop", NULL, NULL, NULL); } int libxl__qmp_resume(libxl__gc *gc, int domid) { return qmp_run_command(gc, domid, "cont", NULL, NULL, NULL); } int libxl__qmp_set_global_dirty_log(libxl__gc *gc, int domid, bool enable) { libxl__json_object *args = NULL; qmp_parameters_add_bool(gc, &args, "enable", enable); return qmp_run_command(gc, domid, "xen-set-global-dirty-log", args, NULL, NULL); } int libxl__qmp_insert_cdrom(libxl__gc *gc, int domid, const libxl_device_disk *disk) { libxl__json_object *args = NULL; int dev_number = libxl__device_disk_dev_number(disk->vdev, NULL, NULL); QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", dev_number); if (disk->format == LIBXL_DISK_FORMAT_EMPTY) { return qmp_run_command(gc, domid, "eject", args, NULL, NULL); } else { qmp_parameters_add_string(gc, &args, "target", disk->pdev_path); return qmp_run_command(gc, domid, "change", args, NULL, NULL); } } int libxl__qmp_cpu_add(libxl__gc *gc, int domid, int idx) { libxl__json_object *args = NULL; qmp_parameters_add_integer(gc, &args, "id", idx); return qmp_run_command(gc, domid, "cpu-add", args, NULL, NULL); } static int query_cpus_callback(libxl__qmp_handler *qmp, const libxl__json_object *response, void *opaque) { libxl_bitmap *map = opaque; unsigned int i; const libxl__json_object *cpu = NULL; int rc; GC_INIT(qmp->ctx); libxl_bitmap_set_none(map); for (i = 0; (cpu = libxl__json_array_get(response, i)); i++) { unsigned int idx; const libxl__json_object *o; o = libxl__json_map_get("CPU", cpu, JSON_INTEGER); if (!o) { LOGD(ERROR, qmp->domid, "Failed to retrieve CPU index."); rc = ERROR_FAIL; goto out; } idx = libxl__json_object_get_integer(o); libxl_bitmap_set(map, idx); } rc = 0; out: GC_FREE; return rc; } int libxl__qmp_query_cpus(libxl__gc *gc, int domid, libxl_bitmap *map) { return qmp_run_command(gc, domid, "query-cpus", NULL, query_cpus_callback, map); } int libxl__qmp_nbd_server_start(libxl__gc *gc, int domid, const char *host, const char *port) { libxl__json_object *args = NULL; libxl__json_object *addr = NULL; libxl__json_object *data = NULL; /* 'addr': { * 'type': 'inet', * 'data': { * 'host': '$nbd_host', * 'port': '$nbd_port' * } * } */ qmp_parameters_add_string(gc, &data, "host", host); qmp_parameters_add_string(gc, &data, "port", port); qmp_parameters_add_string(gc, &addr, "type", "inet"); qmp_parameters_common_add(gc, &addr, "data", data); qmp_parameters_common_add(gc, &args, "addr", addr); return qmp_run_command(gc, domid, "nbd-server-start", args, NULL, NULL); } int libxl__qmp_nbd_server_add(libxl__gc *gc, int domid, const char *disk) { libxl__json_object *args = NULL; qmp_parameters_add_string(gc, &args, "device", disk); qmp_parameters_add_bool(gc, &args, "writable", true); return qmp_run_command(gc, domid, "nbd-server-add", args, NULL, NULL); } int libxl__qmp_start_replication(libxl__gc *gc, int domid, bool primary) { libxl__json_object *args = NULL; qmp_parameters_add_bool(gc, &args, "enable", true); qmp_parameters_add_bool(gc, &args, "primary", primary); return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL); } int libxl__qmp_query_xen_replication_status(libxl__gc *gc, int domid) { return qmp_run_command(gc, domid, "query-xen-replication-status", NULL, NULL, NULL); } int libxl__qmp_colo_do_checkpoint(libxl__gc *gc, int domid) { return qmp_run_command(gc, domid, "xen-colo-do-checkpoint", NULL, NULL, NULL); } int libxl__qmp_stop_replication(libxl__gc *gc, int domid, bool primary) { libxl__json_object *args = NULL; qmp_parameters_add_bool(gc, &args, "enable", false); qmp_parameters_add_bool(gc, &args, "primary", primary); return qmp_run_command(gc, domid, "xen-set-replication", args, NULL, NULL); } int libxl__qmp_nbd_server_stop(libxl__gc *gc, int domid) { return qmp_run_command(gc, domid, "nbd-server-stop", NULL, NULL, NULL); } int libxl__qmp_x_blockdev_change(libxl__gc *gc, int domid, const char *parent, const char *child, const char *node) { libxl__json_object *args = NULL; qmp_parameters_add_string(gc, &args, "parent", parent); if (child) qmp_parameters_add_string(gc, &args, "child", child); if (node) qmp_parameters_add_string(gc, &args, "node", node); return qmp_run_command(gc, domid, "x-blockdev-change", args, NULL, NULL); } static int hmp_callback(libxl__qmp_handler *qmp, const libxl__json_object *response, void *opaque) { char **output = opaque; GC_INIT(qmp->ctx); int rc; rc = 0; if (!output) goto out; *output = NULL; if (libxl__json_object_is_string(response)) { *output = libxl__strdup(NOGC, libxl__json_object_get_string(response)); goto out; } LOG(ERROR, "Response has unexpected format"); rc = ERROR_FAIL; out: GC_FREE; return rc; } int libxl__qmp_hmp(libxl__gc *gc, int domid, const char *command_line, char **output) { libxl__json_object *args = NULL; qmp_parameters_add_string(gc, &args, "command-line", command_line); return qmp_run_command(gc, domid, "human-monitor-command", args, hmp_callback, output); } int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid, const char *command_line, char **output) { GC_INIT(ctx); int rc; rc = libxl__qmp_hmp(gc, domid, command_line, output); GC_FREE; return rc; } int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid, const libxl_domain_config *guest_config) { const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config); libxl__qmp_handler *qmp = NULL; int ret = 0; qmp = libxl__qmp_initialize(gc, domid); if (!qmp) return -1; ret = libxl__qmp_query_serial(qmp); if (!ret && vnc && vnc->passwd) { ret = qmp_change(gc, qmp, "vnc", "password", vnc->passwd); qmp_write_domain_console_item(gc, domid, "vnc-pass", vnc->passwd); } if (!ret) { ret = qmp_query_vnc(qmp); } libxl__qmp_close(qmp); return ret; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_internal.c0000664000175000017500000003503113256712137016103 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" void libxl__alloc_failed(libxl_ctx *ctx, const char *func, size_t nmemb, size_t size) { #define M "libxl: FATAL ERROR: memory allocation failure" #define M_SIZE M " (%s, %lu x %lu)\n" #define M_NSIZE M " (%s)\n" if (size) { libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID, M_SIZE, func, (unsigned long)nmemb, (unsigned long)size); fprintf(stderr, M_SIZE, func, (unsigned long)nmemb, (unsigned long)size); } else { libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID, M_NSIZE, func); fprintf(stderr, M_NSIZE, func); } fflush(stderr); _exit(-1); #undef M_NSIZE #undef M_SIZE #undef M } void libxl__ptr_add(libxl__gc *gc, void *ptr) { int i; if (!libxl__gc_is_real(gc)) return; if (!ptr) return; /* fast case: we have space in the array for storing the pointer */ for (i = 0; i < gc->alloc_maxsize; i++) { if (!gc->alloc_ptrs[i]) { gc->alloc_ptrs[i] = ptr; return; } } int new_maxsize = gc->alloc_maxsize * 2 + 25; assert(new_maxsize < INT_MAX / sizeof(void*) / 2); gc->alloc_ptrs = realloc(gc->alloc_ptrs, new_maxsize * sizeof(void *)); if (!gc->alloc_ptrs) libxl__alloc_failed(CTX, __func__, new_maxsize, sizeof(void*)); gc->alloc_ptrs[gc->alloc_maxsize++] = ptr; while (gc->alloc_maxsize < new_maxsize) gc->alloc_ptrs[gc->alloc_maxsize++] = 0; return; } void libxl__free_all(libxl__gc *gc) { void *ptr; int i; assert(libxl__gc_is_real(gc)); for (i = 0; i < gc->alloc_maxsize; i++) { ptr = gc->alloc_ptrs[i]; gc->alloc_ptrs[i] = NULL; free(ptr); } free(gc->alloc_ptrs); gc->alloc_ptrs = 0; gc->alloc_maxsize = 0; } void *libxl__malloc(libxl__gc *gc, size_t size) { void *ptr = malloc(size); if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1); libxl__ptr_add(gc, ptr); return ptr; } void *libxl__zalloc(libxl__gc *gc, size_t size) { void *ptr = calloc(size, 1); if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1); libxl__ptr_add(gc, ptr); return ptr; } void *libxl__calloc(libxl__gc *gc, size_t nmemb, size_t size) { void *ptr = calloc(nmemb, size); if (!ptr) libxl__alloc_failed(CTX, __func__, nmemb, size); libxl__ptr_add(gc, ptr); return ptr; } void *libxl__realloc(libxl__gc *gc, void *ptr, size_t new_size) { void *new_ptr = realloc(ptr, new_size); int i = 0; if (new_ptr == NULL && new_size != 0) libxl__alloc_failed(CTX, __func__, new_size, 1); if (ptr == NULL) { libxl__ptr_add(gc, new_ptr); } else if (new_ptr != ptr && libxl__gc_is_real(gc)) { for (i = 0; ; i++) { assert(i < gc->alloc_maxsize); if (gc->alloc_ptrs[i] == ptr) { gc->alloc_ptrs[i] = new_ptr; break; } } } return new_ptr; } char *libxl__vsprintf(libxl__gc *gc, const char *fmt, va_list ap) { char *s; va_list aq; int ret; va_copy(aq, ap); ret = vsnprintf(NULL, 0, fmt, aq); va_end(aq); assert(ret >= 0); s = libxl__zalloc(gc, ret + 1); va_copy(aq, ap); ret = vsnprintf(s, ret + 1, fmt, aq); va_end(aq); return s; } char *libxl__sprintf(libxl__gc *gc, const char *fmt, ...) { char *s; va_list ap; va_start(ap, fmt); s = libxl__vsprintf(gc, fmt, ap); va_end(ap); return s; } char *libxl__strdup(libxl__gc *gc, const char *c) { char *s; if (!c) return NULL; s = strdup(c); if (!s) libxl__alloc_failed(CTX, __func__, strlen(c), 1); libxl__ptr_add(gc, s); return s; } char *libxl__strndup(libxl__gc *gc, const char *c, size_t n) { char *s; if (!c) return NULL; s = strndup(c, n); if (!s) libxl__alloc_failed(CTX, __func__, n, 1); libxl__ptr_add(gc, s); return s; } char *libxl__dirname(libxl__gc *gc, const char *s) { char *c = strrchr(s, '/'); if (!c) return NULL; return libxl__strndup(gc, s, c - s); } void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval, const char *file, int line, const char *func, uint32_t domid, const char *fmt, va_list ap) { /* WARNING this function may not call any libxl-provided * memory allocation function, as those may * call libxl__alloc_failed which calls libxl__logv. */ char *enomem = "[out of memory formatting log message]"; char *base = NULL; int rc, esave; char fileline[256]; char domain[256]; esave = errno; rc = vasprintf(&base, fmt, ap); if (rc<0) { base = enomem; goto x; } fileline[0] = 0; if (file) snprintf(fileline, sizeof(fileline), "%s:%d",file,line); fileline[sizeof(fileline)-1] = 0; domain[0] = 0; if (domid != INVALID_DOMID) snprintf(domain, sizeof(domain), "Domain %"PRIu32":", domid); x: xtl_log(ctx->lg, msglevel, errnoval, "libxl", "%s%s%s%s%s" "%s", fileline, func&&file?":":"", func?func:"", func||file?": ":"", domain, base); if (base != enomem) free(base); errno = esave; } void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval, const char *file, int line, const char *func, uint32_t domid, const char *fmt, ...) { va_list ap; va_start(ap, fmt); libxl__logv(ctx, msglevel, errnoval, file, line, func, domid, fmt, ap); va_end(ap); } char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path) { if (s[0] == '/') return libxl__strdup(gc, s); return GCSPRINTF("%s/%s", path, s); } int libxl__file_reference_map(libxl__file_reference *f) { struct stat st_buf; int ret, fd; void *data; if (f->mapped) return 0; fd = open(f->path, O_RDONLY); if (fd < 0) return ERROR_FAIL; ret = fstat(fd, &st_buf); if (ret < 0) goto out; ret = -1; data = mmap(NULL, st_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == MAP_FAILED) goto out; f->mapped = 1; f->data = data; f->size = st_buf.st_size; ret = 0; out: close(fd); return ret == 0 ? 0 : ERROR_FAIL; } int libxl__file_reference_unmap(libxl__file_reference *f) { int ret; if (!f->mapped) return 0; ret = munmap(f->data, f->size); if (ret == 0) { f->mapped = 0; f->data = NULL; f->size = 0; return 0; } return ERROR_FAIL; } _hidden int libxl__parse_mac(const char *s, libxl_mac mac) { const char *tok; char *endptr; int i; for (i = 0, tok = s; *tok && (i < 6); ++i, tok = endptr) { mac[i] = strtol(tok, &endptr, 16); if (endptr != (tok + 2) || (*endptr != '\0' && *endptr != ':') ) return ERROR_INVAL; if (*endptr == ':') endptr++; } if ( i != 6 ) return ERROR_INVAL; return 0; } _hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b) { int i; for (i = 0; i<6; i++) { if ((*a)[i] != (*b)[i]) return (*a)[i] - (*b)[i]; } return 0; } _hidden int libxl__mac_is_default(libxl_mac *mac) { return (!(*mac)[0] && !(*mac)[1] && !(*mac)[2] && !(*mac)[3] && !(*mac)[4] && !(*mac)[5]); } _hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock) { GC_INIT(ctx); pthread_mutexattr_t attr; int rc = 0; if (pthread_mutexattr_init(&attr) != 0) { LOGE(ERROR, "Failed to init mutex attributes"); rc = ERROR_FAIL; goto out; } if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) { LOGE(ERROR, "Failed to set mutex attributes"); rc = ERROR_FAIL; goto out; } if (pthread_mutex_init(lock, &attr) != 0) { LOGE(ERROR, "Failed to init mutex"); rc = ERROR_FAIL; goto out; } out: pthread_mutexattr_destroy(&attr); GC_FREE; return rc; } int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid) { char *path = NULL; char *dm_version = NULL; libxl_device_model_version value; path = libxl__xs_libxl_path(gc, domid); path = GCSPRINTF("%s/dm-version", path); dm_version = libxl__xs_read(gc, XBT_NULL, path); if (!dm_version) { return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL; } if (libxl_device_model_version_from_string(dm_version, &value) < 0) { LOGD(ERROR, domid, "fatal: %s contain a wrong value (%s)", path, dm_version); return -1; } return value; } /* Portability note: this lock utilises flock(2) so a proper implementation of * flock(2) is required. */ libxl__domain_userdata_lock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid) { libxl__domain_userdata_lock *lock = NULL; const char *lockfile; int fd; struct stat stab, fstab; lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l"); if (!lockfile) goto out; lock = libxl__zalloc(NOGC, sizeof(libxl__domain_userdata_lock)); lock->path = libxl__strdup(NOGC, lockfile); while (true) { libxl__carefd_begin(); fd = open(lockfile, O_RDWR|O_CREAT, 0666); if (fd < 0) LOGED(ERROR, domid, "cannot open lockfile %s, errno=%d", lockfile, errno); lock->carefd = libxl__carefd_opened(CTX, fd); if (fd < 0) goto out; /* Lock the file in exclusive mode, wait indefinitely to * acquire the lock */ while (flock(fd, LOCK_EX)) { switch (errno) { case EINTR: /* Signal received, retry */ continue; default: /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */ LOGED(ERROR, domid, "unexpected error while trying to lock %s, fd=%d, errno=%d", lockfile, fd, errno); goto out; } } if (fstat(fd, &fstab)) { LOGED(ERROR, domid, "cannot fstat %s, fd=%d, errno=%d", lockfile, fd, errno); goto out; } if (stat(lockfile, &stab)) { if (errno != ENOENT) { LOGED(ERROR, domid, "cannot stat %s, errno=%d", lockfile, errno); goto out; } } else { if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino) break; } libxl__carefd_close(lock->carefd); } /* Check the domain is still there, if not we should release the * lock and clean up. */ if (libxl_domain_info(CTX, NULL, domid)) goto out; return lock; out: if (lock) libxl__unlock_domain_userdata(lock); return NULL; } void libxl__unlock_domain_userdata(libxl__domain_userdata_lock *lock) { /* It's important to unlink the file before closing fd to avoid * the following race (if close before unlink): * * P1 LOCK P2 UNLOCK * fd1 = open(lockfile) * close(fd2) * flock(fd1) * fstat and stat check success * unlink(lockfile) * return lock * * In above case P1 thinks it has got hold of the lock but * actually lock is released by P2 (lockfile unlinked). */ if (lock->path) unlink(lock->path); if (lock->carefd) libxl__carefd_close(lock->carefd); free(lock->path); free(lock); } int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid, libxl_domain_config *d_config) { uint8_t *data = NULL; int rc, len; rc = libxl__userdata_retrieve(gc, domid, "libxl-json", &data, &len); if (rc) { LOGEVD(ERROR, rc, domid, "failed to retrieve domain configuration"); rc = ERROR_FAIL; goto out; } if (len == 0) { /* No logging, not necessary an error from caller's PoV. */ rc = ERROR_JSON_CONFIG_EMPTY; goto out; } rc = libxl_domain_config_from_json(CTX, d_config, (const char *)data); out: free(data); return rc; } int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid, libxl_domain_config *d_config) { char *d_config_json; int rc; d_config_json = libxl_domain_config_to_json(CTX, d_config); if (!d_config_json) { LOGED(ERROR, domid, "failed to convert domain configuration to JSON"); rc = ERROR_FAIL; goto out; } rc = libxl__userdata_store(gc, domid, "libxl-json", (const uint8_t *)d_config_json, strlen(d_config_json) + 1 /* include '\0' */); if (rc) { LOGEVD(ERROR, rc, domid, "failed to store domain configuration"); rc = ERROR_FAIL; goto out; } out: free(d_config_json); return rc; } void libxl__update_domain_configuration(libxl__gc *gc, libxl_domain_config *dst, const libxl_domain_config *src) { int i, idx, num; const struct libxl_device_type *dt; for (idx = 0;; idx++) { dt = device_type_tbl[idx]; if (!dt) break; num = *libxl__device_type_get_num(dt, src); if (!dt->update_config || !num) continue; for (i = 0; i < num; i++) dt->update_config(gc, libxl__device_type_get_elem(dt, dst, i), libxl__device_type_get_elem(dt, src, i)); } /* update guest UUID */ libxl_uuid_copy(CTX, &dst->c_info.uuid, &src->c_info.uuid); /* video ram */ dst->b_info.video_memkb = src->b_info.video_memkb; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_cpupool.c0000664000175000017500000002474013256712137015755 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" /* Returns: * 0 - success * ERROR_FAIL + errno == ENOENT - no entry found * ERROR_$FOO + errno != ENOENT - other failure */ static int cpupool_info(libxl__gc *gc, libxl_cpupoolinfo *info, uint32_t poolid, bool exact /* exactly poolid or >= poolid */) { xc_cpupoolinfo_t *xcinfo; int rc = ERROR_FAIL; xcinfo = xc_cpupool_getinfo(CTX->xch, poolid); if (xcinfo == NULL) { if (exact || errno != ENOENT) LOGE(ERROR, "failed to get info for cpupool%d", poolid); return ERROR_FAIL; } if (exact && xcinfo->cpupool_id != poolid) { LOG(ERROR, "got info for cpupool%d, wanted cpupool%d\n", xcinfo->cpupool_id, poolid); goto out; } info->poolid = xcinfo->cpupool_id; info->pool_name = libxl_cpupoolid_to_name(CTX, info->poolid); if (!info->pool_name) { rc = ERROR_FAIL; goto out; } info->sched = xcinfo->sched_id; info->n_dom = xcinfo->n_dom; rc = libxl_cpu_bitmap_alloc(CTX, &info->cpumap, 0); if (rc) goto out; memcpy(info->cpumap.map, xcinfo->cpumap, info->cpumap.size); rc = 0; out: xc_cpupool_infofree(CTX->xch, xcinfo); return rc; } int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid) { GC_INIT(ctx); int rc = cpupool_info(gc, info, poolid, true); GC_FREE; return rc; } libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx *ctx, int *nb_pool_out) { GC_INIT(ctx); libxl_cpupoolinfo info, *ptr; int i; uint32_t poolid; ptr = NULL; poolid = 0; for (i = 0;; i++) { libxl_cpupoolinfo_init(&info); if (cpupool_info(gc, &info, poolid, false)) { libxl_cpupoolinfo_dispose(&info); if (errno != ENOENT) goto out; break; } ptr = libxl__realloc(NOGC, ptr, (i+1) * sizeof(libxl_cpupoolinfo)); ptr[i] = info; poolid = info.poolid + 1; /* Don't dispose of info because it will be returned to caller */ } *nb_pool_out = i; GC_FREE; return ptr; out: libxl_cpupoolinfo_list_free(ptr, i); *nb_pool_out = 0; GC_FREE; return NULL; } int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap) { int ncpus; ncpus = libxl_get_max_cpus(ctx); if (ncpus < 0) return ncpus; cpumap->map = xc_cpupool_freeinfo(ctx->xch); if (cpumap->map == NULL) return ERROR_FAIL; cpumap->size = (ncpus + 7) / 8; return 0; } int libxl_cpupool_create(libxl_ctx *ctx, const char *name, libxl_scheduler sched, libxl_bitmap cpumap, libxl_uuid *uuid, uint32_t *poolid) { GC_INIT(ctx); int rc; int i; xs_transaction_t t; char *uuid_string; uint32_t xcpoolid; /* Accept '0' as 'any poolid' for backwards compatibility */ if ( *poolid == LIBXL_CPUPOOL_POOLID_ANY || *poolid == 0 ) xcpoolid = XC_CPUPOOL_POOLID_ANY; else xcpoolid = *poolid; uuid_string = libxl__uuid2string(gc, *uuid); if (!uuid_string) { GC_FREE; return ERROR_NOMEM; } rc = xc_cpupool_create(ctx->xch, &xcpoolid, sched); if (rc) { LOGEV(ERROR, rc, "Could not create cpupool"); GC_FREE; return ERROR_FAIL; } *poolid = xcpoolid; libxl_for_each_bit(i, cpumap) if (libxl_bitmap_test(&cpumap, i)) { rc = xc_cpupool_addcpu(ctx->xch, *poolid, i); if (rc) { LOGEV(ERROR, rc, "Error moving cpu to cpupool"); libxl_cpupool_destroy(ctx, *poolid); GC_FREE; return ERROR_FAIL; } } for (;;) { t = xs_transaction_start(ctx->xsh); xs_mkdir(ctx->xsh, t, GCSPRINTF("/local/pool/%d", *poolid)); libxl__xs_printf(gc, t, GCSPRINTF("/local/pool/%d/uuid", *poolid), "%s", uuid_string); libxl__xs_printf(gc, t, GCSPRINTF("/local/pool/%d/name", *poolid), "%s", name); if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN)) { GC_FREE; return 0; } } } int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid) { GC_INIT(ctx); int rc, i; xc_cpupoolinfo_t *info; xs_transaction_t t; libxl_bitmap cpumap; info = xc_cpupool_getinfo(ctx->xch, poolid); if (info == NULL) { GC_FREE; return ERROR_NOMEM; } rc = ERROR_INVAL; if ((info->cpupool_id != poolid) || (info->n_dom)) goto out; rc = libxl_cpu_bitmap_alloc(ctx, &cpumap, 0); if (rc) goto out; memcpy(cpumap.map, info->cpumap, cpumap.size); libxl_for_each_bit(i, cpumap) if (libxl_bitmap_test(&cpumap, i)) { rc = xc_cpupool_removecpu(ctx->xch, poolid, i); if (rc) { LOGEV(ERROR, rc, "Error removing cpu from cpupool"); rc = ERROR_FAIL; goto out1; } } rc = xc_cpupool_destroy(ctx->xch, poolid); if (rc) { LOGEV(ERROR, rc, "Could not destroy cpupool"); rc = ERROR_FAIL; goto out1; } for (;;) { t = xs_transaction_start(ctx->xsh); xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF("/local/pool/%d", poolid)); if (xs_transaction_end(ctx->xsh, t, 0) || (errno != EAGAIN)) break; } rc = 0; out1: libxl_bitmap_dispose(&cpumap); out: xc_cpupool_infofree(ctx->xch, info); GC_FREE; return rc; } int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid) { GC_INIT(ctx); xs_transaction_t t; xc_cpupoolinfo_t *info; int rc; info = xc_cpupool_getinfo(ctx->xch, poolid); if (info == NULL) { GC_FREE; return ERROR_NOMEM; } rc = ERROR_INVAL; if (info->cpupool_id != poolid) goto out; rc = 0; for (;;) { t = xs_transaction_start(ctx->xsh); libxl__xs_printf(gc, t, GCSPRINTF("/local/pool/%d/name", poolid), "%s", name); if (xs_transaction_end(ctx->xsh, t, 0)) break; if (errno == EAGAIN) continue; rc = ERROR_FAIL; break; } out: xc_cpupool_infofree(ctx->xch, info); GC_FREE; return rc; } int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu) { GC_INIT(ctx); int rc = 0; rc = xc_cpupool_addcpu(ctx->xch, poolid, cpu); if (rc) { LOGE(ERROR, "Error moving cpu %d to cpupool", cpu); rc = ERROR_FAIL; } GC_FREE; return rc; } int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid, const libxl_bitmap *cpumap) { int c, ncpus = 0, rc = 0; libxl_for_each_set_bit(c, *cpumap) { if (!libxl_cpupool_cpuadd(ctx, poolid, c)) ncpus++; } if (ncpus != libxl_bitmap_count_set(cpumap)) rc = ERROR_FAIL; return rc; } int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus) { int rc = 0; int cpu, nr; libxl_bitmap freemap; libxl_cputopology *topology; if (libxl_get_freecpus(ctx, &freemap)) { return ERROR_FAIL; } topology = libxl_get_cpu_topology(ctx, &nr); if (!topology) { rc = ERROR_FAIL; goto out; } *cpus = 0; for (cpu = 0; cpu < nr; cpu++) { if (libxl_bitmap_test(&freemap, cpu) && (topology[cpu].node == node) && !libxl_cpupool_cpuadd(ctx, poolid, cpu)) { (*cpus)++; } libxl_cputopology_dispose(&topology[cpu]); } free(topology); out: libxl_bitmap_dispose(&freemap); return rc; } int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu) { GC_INIT(ctx); int rc = 0; rc = xc_cpupool_removecpu(ctx->xch, poolid, cpu); if (rc) { LOGE(ERROR, "Error removing cpu %d from cpupool", cpu); rc = ERROR_FAIL; } GC_FREE; return rc; } int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid, const libxl_bitmap *cpumap) { int c, ncpus = 0, rc = 0; libxl_for_each_set_bit(c, *cpumap) { if (!libxl_cpupool_cpuremove(ctx, poolid, c)) ncpus++; } if (ncpus != libxl_bitmap_count_set(cpumap)) rc = ERROR_FAIL; return rc; } int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus) { int ret = 0; int n_pools; int p; int cpu, nr_cpus; libxl_cputopology *topology; libxl_cpupoolinfo *poolinfo; poolinfo = libxl_list_cpupool(ctx, &n_pools); if (!poolinfo) { return ERROR_NOMEM; } topology = libxl_get_cpu_topology(ctx, &nr_cpus); if (!topology) { ret = ERROR_FAIL; goto out; } *cpus = 0; for (p = 0; p < n_pools; p++) { if (poolinfo[p].poolid == poolid) { for (cpu = 0; cpu < nr_cpus; cpu++) { if ((topology[cpu].node == node) && libxl_bitmap_test(&poolinfo[p].cpumap, cpu) && !libxl_cpupool_cpuremove(ctx, poolid, cpu)) { (*cpus)++; } } } } libxl_cputopology_list_free(topology, nr_cpus); out: libxl_cpupoolinfo_list_free(poolinfo, n_pools); return ret; } int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid) { GC_INIT(ctx); int rc; rc = xc_cpupool_movedomain(ctx->xch, poolid, domid); if (rc) { LOGEVD(ERROR, rc, domid, "Error moving domain to cpupool"); GC_FREE; return ERROR_FAIL; } GC_FREE; return 0; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_colo_save.c0000664000175000017500000005224613256712137016250 0ustar smbsmb/* * Copyright (C) 2016 FUJITSU LIMITED * Author: Wen Congyang * Yang Hongyang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" extern const libxl__checkpoint_device_instance_ops colo_save_device_nic; extern const libxl__checkpoint_device_instance_ops colo_save_device_qdisk; static const libxl__checkpoint_device_instance_ops *colo_ops[] = { &colo_save_device_nic, &colo_save_device_qdisk, NULL, }; /* ================= helper functions ================= */ static int init_device_subkind(libxl__checkpoint_devices_state *cds) { /* init device subkind-specific state in the libxl ctx */ int rc; STATE_AO_GC(cds->ao); rc = init_subkind_colo_nic(cds); if (rc) goto out; rc = init_subkind_qdisk(cds); if (rc) { cleanup_subkind_colo_nic(cds); goto out; } rc = 0; out: return rc; } static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds) { /* cleanup device subkind-specific state in the libxl ctx */ STATE_AO_GC(cds->ao); cleanup_subkind_colo_nic(cds); cleanup_subkind_qdisk(cds); } /* ================= colo: setup save environment ================= */ static void colo_save_setup_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void colo_save_setup_failed(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); /* * checkpoint callbacks are called in the following order: * 1. suspend * 2. checkpoint * 3. resume * 4. wait checkpoint */ static void libxl__colo_save_domain_suspend_callback(void *data); static void libxl__colo_save_domain_checkpoint_callback(void *data); static void libxl__colo_save_domain_resume_callback(void *data); static void libxl__colo_save_domain_wait_checkpoint_callback(void *data); void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css) { libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = &dss->cds; libxl__srm_save_autogen_callbacks *const callbacks = &dss->sws.shs.callbacks.save.a; libxl_device_nic *nics; STATE_AO_GC(dss->ao); if (dss->type != LIBXL_DOMAIN_TYPE_HVM) { LOGD(ERROR, dss->domid, "COLO only supports hvm now"); goto out; } css->send_fd = dss->fd; css->recv_fd = dss->recv_fd; css->svm_running = false; css->paused = true; css->qdisk_setuped = false; css->qdisk_used = false; libxl__ev_child_init(&css->child); css->cps.is_userspace_proxy = libxl_defbool_val(dss->remus->userspace_colo_proxy); if (dss->remus->netbufscript) css->colo_proxy_script = libxl__strdup(gc, dss->remus->netbufscript); else css->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup", libxl__xen_script_dir_path()); cds->ops = colo_ops; cds->callback = colo_save_setup_done; cds->ao = ao; cds->domid = dss->domid; cds->concrete_data = css; /* If enable userspace proxy mode, we don't need VIF */ if (css->cps.is_userspace_proxy) { cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD); /* Use this args we can connect to qemu colo-compare */ nics = libxl_device_nic_list(CTX, cds->domid, &cds->num_nics); css->cps.checkpoint_host = nics->colo_checkpoint_host; css->cps.checkpoint_port = nics->colo_checkpoint_port; } else { cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) | (1 << LIBXL__DEVICE_KIND_VBD); } css->srs.ao = ao; css->srs.fd = css->recv_fd; css->srs.back_channel = true; libxl__stream_read_start(egc, &css->srs); css->cps.ao = ao; if (colo_proxy_setup(&css->cps)) { LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest"); goto out; } if (init_device_subkind(cds)) goto out; callbacks->suspend = libxl__colo_save_domain_suspend_callback; callbacks->checkpoint = libxl__colo_save_domain_checkpoint_callback; callbacks->postcopy = libxl__colo_save_domain_resume_callback; callbacks->wait_checkpoint = libxl__colo_save_domain_wait_checkpoint_callback; libxl__checkpoint_devices_setup(egc, &dss->cds); return; out: dss->callback(egc, dss, ERROR_FAIL); } static void colo_save_setup_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_save_state *css = cds->concrete_data; libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); EGC_GC; if (!rc) { libxl__domain_save(egc, dss); return; } LOGD(ERROR, dss->domid, "COLO: failed to setup device for guest"); cds->callback = colo_save_setup_failed; libxl__checkpoint_devices_teardown(egc, cds); } static void colo_save_setup_failed(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_save_state *css = cds->concrete_data; libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); STATE_AO_GC(cds->ao); if (rc) LOGD(ERROR, cds->domid, "COLO: failed to teardown device after setup failed" " for guest, rc %d", rc); cleanup_device_subkind(cds); dss->callback(egc, dss, rc); } /* ================= colo: teardown save environment ================= */ static void colo_teardown_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); void libxl__colo_save_teardown(libxl__egc *egc, libxl__colo_save_state *css, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); EGC_GC; LOGD(WARN, dss->domid, "COLO: Domain suspend terminated with rc %d," " teardown COLO devices...", rc); libxl__stream_read_abort(egc, &css->srs, 1); if (css->qdisk_setuped) { libxl__qmp_stop_replication(gc, dss->domid, true); css->qdisk_setuped = false; } dss->cds.callback = colo_teardown_done; libxl__checkpoint_devices_teardown(egc, &dss->cds); return; } static void colo_teardown_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_save_state *css = cds->concrete_data; libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); cleanup_device_subkind(cds); colo_proxy_teardown(&css->cps); dss->callback(egc, dss, rc); } static void colo_common_write_stream_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc); static void colo_common_read_stream_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc); /* ===================== colo: suspend primary vm ===================== */ static void colo_read_svm_suspended_done(libxl__egc *egc, libxl__colo_save_state *css, int id); /* * Do the following things when suspending primary vm: * 1. suspend primary vm * 2. do postsuspend * 3. read CHECKPOINT_SVM_SUSPENDED * 4. read secondary vm's dirty pages */ static void colo_suspend_primary_vm_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int ok); static void colo_postsuspend_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void libxl__colo_save_domain_suspend_callback(void *data) { libxl__save_helper_state *shs = data; libxl__egc *egc = shs->egc; libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs); libxl__domain_save_state *dss = sws->dss; /* Convenience aliases */ libxl__domain_suspend_state *dsps = &dss->dsps; dsps->callback_common_done = colo_suspend_primary_vm_done; libxl__domain_suspend(egc, dsps); } static void colo_suspend_primary_vm_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps); EGC_GC; if (rc) { LOGD(ERROR, dss->domid, "cannot suspend primary vm"); goto out; } /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = &dss->cds; cds->callback = colo_postsuspend_cb; libxl__checkpoint_devices_postsuspend(egc, cds); return; out: dss->rc = rc; libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc); } static void colo_postsuspend_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_save_state *css = cds->concrete_data; libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); EGC_GC; if (rc) { LOGD(ERROR, dss->domid, "postsuspend fails"); goto out; } if (!css->svm_running) { rc = 0; goto out; } /* * read CHECKPOINT_SVM_SUSPENDED */ css->callback = colo_read_svm_suspended_done; css->srs.checkpoint_callback = colo_common_read_stream_done; libxl__stream_read_checkpoint_state(egc, &css->srs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc); } static void colo_read_svm_suspended_done(libxl__egc *egc, libxl__colo_save_state *css, int id) { int ok = 0; libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); EGC_GC; if (id != CHECKPOINT_SVM_SUSPENDED) { LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id, CHECKPOINT_SVM_SUSPENDED); goto out; } if (!css->paused && libxl__qmp_query_xen_replication_status(gc, dss->domid)) { LOGD(ERROR, dss->domid, "replication error occurs when primary vm is running"); goto out; } ok = 1; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok); } /* ===================== colo: send tailbuf ========================== */ static void libxl__colo_save_domain_checkpoint_callback(void *data) { libxl__save_helper_state *shs = data; libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs); libxl__domain_save_state *dss = sws->dss; /* Convenience aliases */ libxl__colo_save_state *const css = &dss->css; /* write emulator xenstore data, emulator context, and checkpoint end */ css->callback = NULL; dss->sws.checkpoint_callback = colo_common_write_stream_done; libxl__stream_write_start_checkpoint(shs->egc, &dss->sws); } /* ===================== colo: resume primary vm ===================== */ /* * Do the following things when resuming primary vm: * 1. read CHECKPOINT_SVM_READY * 2. do preresume * 3. resume primary vm * 4. read CHECKPOINT_SVM_RESUMED */ static void colo_read_svm_ready_done(libxl__egc *egc, libxl__colo_save_state *css, int id); static void colo_preresume_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void colo_read_svm_resumed_done(libxl__egc *egc, libxl__colo_save_state *css, int id); static void libxl__colo_save_domain_resume_callback(void *data) { libxl__save_helper_state *shs = data; libxl__egc *egc = shs->egc; libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs); libxl__domain_save_state *dss = sws->dss; /* Convenience aliases */ libxl__colo_save_state *const css = &dss->css; EGC_GC; /* read CHECKPOINT_SVM_READY */ css->callback = colo_read_svm_ready_done; css->srs.checkpoint_callback = colo_common_read_stream_done; libxl__stream_read_checkpoint_state(egc, &css->srs); } static void colo_read_svm_ready_done(libxl__egc *egc, libxl__colo_save_state *css, int id) { libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); EGC_GC; if (id != CHECKPOINT_SVM_READY) { LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id, CHECKPOINT_SVM_READY); goto out; } colo_proxy_preresume(&css->cps); css->svm_running = true; dss->cds.callback = colo_preresume_cb; libxl__checkpoint_devices_preresume(egc, &dss->cds); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0); } static void colo_preresume_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_save_state *css = cds->concrete_data; libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); EGC_GC; if (rc) { LOGD(ERROR, dss->domid, "preresume fails"); goto out; } if (css->qdisk_used && !css->qdisk_setuped) { if (libxl__qmp_start_replication(gc, dss->domid, true)) { LOGD(ERROR, dss->domid, "starting replication fails"); goto out; } css->qdisk_setuped = true; } if (!css->paused) { if (libxl__qmp_colo_do_checkpoint(gc, dss->domid)) { LOGD(ERROR, dss->domid, "doing checkpoint fails"); goto out; } } /* Resumes the domain and the device model */ if (libxl__domain_resume(gc, dss->domid, /* Fast Suspend */1)) { LOGD(ERROR, dss->domid, "cannot resume primary vm"); goto out; } /* * The guest should be paused before doing colo because there is * no disk migration. */ if (css->paused) { rc = libxl_domain_unpause(CTX, dss->domid); if (rc) { LOGD(ERROR, dss->domid, "cannot unpause primary vm"); goto out; } css->paused = false; } /* read CHECKPOINT_SVM_RESUMED */ css->callback = colo_read_svm_resumed_done; css->srs.checkpoint_callback = colo_common_read_stream_done; libxl__stream_read_checkpoint_state(egc, &css->srs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0); } static void colo_read_svm_resumed_done(libxl__egc *egc, libxl__colo_save_state *css, int id) { int ok = 0; libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); EGC_GC; if (id != CHECKPOINT_SVM_RESUMED) { LOGD(ERROR, dss->domid, "invalid section: %d, expected: %d", id, CHECKPOINT_SVM_RESUMED); goto out; } colo_proxy_postresume(&css->cps); ok = 1; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok); } /* ===================== colo: wait new checkpoint ===================== */ static void colo_start_new_checkpoint(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css); static void colo_proxy_async_call_done(libxl__egc *egc, libxl__ev_child *child, int pid, int status); static void colo_proxy_wait_for_checkpoint(libxl__egc *egc, libxl__colo_save_state *css) { libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); ASYNC_CALL(egc, dss->cds.ao, &css->child, css, colo_proxy_async_wait_for_checkpoint, colo_proxy_async_call_done); } static void colo_proxy_async_wait_for_checkpoint(libxl__colo_save_state *css) { int req; req = colo_proxy_checkpoint(&css->cps, COLO_PROXY_CHECKPOINT_TIMEOUT); if (req < 0) { /* some error happens */ _exit(1); } else { /* req == 0: no checkpoint is needed, do a checkpoint every 5s */ /* req > 0: net packets is not consistent, we need to start a * checkpoint */ _exit(0); } } static void colo_proxy_async_call_done(libxl__egc *egc, libxl__ev_child *child, int pid, int status) { libxl__colo_save_state *css = CONTAINER_OF(child, *css, child); libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); EGC_GC; if (status) { LOGD(ERROR, dss->domid, "failed to wait for new checkpoint"); colo_start_new_checkpoint(egc, &dss->cds, ERROR_FAIL); return; } colo_start_new_checkpoint(egc, &dss->cds, 0); } /* * Do the following things: * 1. do commit * 2. wait for a new checkpoint * 3. write CHECKPOINT_NEW */ static void colo_device_commit_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void libxl__colo_save_domain_wait_checkpoint_callback(void *data) { libxl__save_helper_state *shs = data; libxl__stream_write_state *sws = CONTAINER_OF(shs, *sws, shs); libxl__domain_save_state *dss = sws->dss; libxl__egc *egc = dss->sws.shs.egc; /* Convenience aliases */ libxl__checkpoint_devices_state *const cds = &dss->cds; cds->callback = colo_device_commit_cb; libxl__checkpoint_devices_commit(egc, cds); } static void colo_device_commit_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_save_state *css = cds->concrete_data; libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); EGC_GC; if (rc) { LOGD(ERROR, dss->domid, "commit fails"); goto out; } colo_proxy_wait_for_checkpoint(egc, css); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0); } static void colo_start_new_checkpoint(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_save_state *css = cds->concrete_data; libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_NEW }; if (rc) goto out; /* write CHECKPOINT_NEW */ css->callback = NULL; dss->sws.checkpoint_callback = colo_common_write_stream_done; libxl__stream_write_checkpoint_state(egc, &dss->sws, &srcs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, 0); } /* ===================== colo: common callback ===================== */ static void colo_common_write_stream_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(stream, *dss, sws); int ok; /* Convenience aliases */ libxl__colo_save_state *const css = &dss->css; EGC_GC; if (rc < 0) { /* TODO: it may be a internal error, but we don't know */ LOGD(ERROR, dss->domid, "sending data fails"); ok = 0; goto out; } if (!css->callback) { /* Everythins is OK */ ok = 1; goto out; } css->callback(egc, css, 0); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok); } static void colo_common_read_stream_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc) { libxl__colo_save_state *css = CONTAINER_OF(stream, *css, srs); libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); int ok; EGC_GC; if (rc < 0) { /* TODO: it may be a internal error, but we don't know */ LOGD(ERROR, dss->domid, "reading data fails"); ok = 0; goto out; } if (!css->callback) { /* Everythins is OK */ ok = 1; goto out; } /* rc contains the id */ css->callback(egc, css, rc); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, ok); } xen-4.9.2/tools/libxl/libxl_osdeps.h0000664000175000017500000000664713256712137015604 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * This header must be included first, before any system headers, * so that _GNU_SOURCE takes effect properly. */ #ifndef LIBXL_OSDEP #define LIBXL_OSDEP #define _GNU_SOURCE #if defined(__NetBSD__) #define SYSFS_USB_DEV "/sys/bus/usb/devices" #define SYSFS_USBBACK_DRIVER "/kern/xen/usb" #define SYSFS_PCI_DEV "/sys/bus/pci/devices" #define SYSFS_PCIBACK_DRIVER "/kern/xen/pci" #define NETBACK_NIC_NAME "xvif%ui%d" #include #include #elif defined(__OpenBSD__) #include #elif defined(__linux__) #define SYSFS_USB_DEV "/sys/bus/usb/devices" #define SYSFS_USBBACK_DRIVER "/sys/bus/usb/drivers/usbback" #define SYSFS_PCI_DEV "/sys/bus/pci/devices" #define SYSFS_PCIBACK_DRIVER "/sys/bus/pci/drivers/pciback" #define NETBACK_NIC_NAME "vif%u.%d" #include #include #include #elif defined(__sun__) #include #elif defined(__FreeBSD__) #define SYSFS_USB_DEV "/dev/null" #define SYSFS_USBBACK_DRIVER "/dev/null" #define SYSFS_PCI_DEV "/dev/null" #define SYSFS_PCIBACK_DRIVER "/dev/null" #define NETBACK_NIC_NAME "xnb%u.%d" #include #include #include /* * FreeBSD doesn't have ENODATA errno ATM, so privcmd always translates * ENODATA into ENOENT. */ #ifndef ENODATA #define ENODATA ENOENT #endif #endif #ifndef SYSFS_USBBACK_DRIVER #error define SYSFS_USBBACK_DRIVER for your platform #endif #ifndef SYSFS_USB_DEV #error define SYSFS_USB_DEV for your platform #endif #ifndef SYSFS_PCIBACK_DRIVER #error define SYSFS_PCIBACK_DRIVER for your platform #endif #ifndef SYSFS_PCI_DEV #error define SYSFS_PCI_DEV for your platform #endif #ifdef NEED_OWN_ASPRINTF #include int asprintf(char **buffer, char *fmt, ...); int vasprintf(char **buffer, const char *fmt, va_list ap); #endif /*NEED_OWN_ASPRINTF*/ #ifndef htobe32 /* glibc < 2.9 */ # include # if __BYTE_ORDER == __LITTLE_ENDIAN # define htobe16(x) __bswap_16(x) # define htole16(x) (x) # define be16toh(x) __bswap_16(x) # define le16toh(x) (x) # define htobe32(x) __bswap_32(x) # define htole32(x) (x) # define be32toh(x) __bswap_32(x) # define le32toh(x) (x) # define htobe64(x) __bswap_64(x) # define htole64(x) (x) # define be64toh(x) __bswap_64(x) # define le64toh(x) (x) # else # define htobe16(x) (x) # define htole16(x) __bswap_16(x) # define be16toh(x) (x) # define le16toh(x) __bswap_16(x) # define htobe32(x) (x) # define htole32(x) __bswap_32(x) # define be32toh(x) (x) # define le32toh(x) __bswap_32(x) # define htobe64(x) (x) # define htole64(x) __bswap_64(x) # define be64toh(x) (x) # define le64toh(x) __bswap_64(x) # endif #endif #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_colo.h0000664000175000017500000001226313256712137015232 0ustar smbsmb/* * Copyright (C) 2016 FUJITSU LIMITED * Author: Wen Congyang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef LIBXL_COLO_H #define LIBXL_COLO_H #include "libxl_internal.h" /* Maximum time(5s) to wait for colo proxy checkpoit */ #define COLO_PROXY_CHECKPOINT_TIMEOUT 5000000 #define ASYNC_CALL(egc, ao, child, param, func, callback) do { \ int pid = -1; \ STATE_AO_GC(ao); \ \ pid = libxl__ev_child_fork(gc, child, callback); \ if (pid == -1) { \ LOGD(ERROR, ao->domid, "unable to fork"); \ goto out; \ } \ \ if (!pid) { \ /* child */ \ func(param); \ /* notreached */ \ abort(); \ } \ \ return; \ out: \ callback(egc, child, -1, 1); \ } while (0) enum { LIBXL_COLO_SETUPED, LIBXL_COLO_SUSPENDED, LIBXL_COLO_RESUMED, }; struct libxl__colo_device_nic { int devid; const char *vif; }; struct libxl__colo_qdisk { bool setuped; }; struct libxl__colo_proxy_state { /* set by caller of colo_proxy_setup */ struct libxl__ao *ao; int sock_fd; int index; /* * Private, True means use userspace colo proxy * False means use kernel colo proxy. */ bool is_userspace_proxy; const char *checkpoint_host; const char *checkpoint_port; }; struct libxl__colo_save_state { int send_fd; int recv_fd; char *colo_proxy_script; /* private */ libxl__stream_read_state srs; void (*callback)(libxl__egc *, libxl__colo_save_state *, int); bool svm_running; bool paused; /* private, used by qdisk block replication */ bool qdisk_used; bool qdisk_setuped; /* private, used by colo-proxy */ libxl__colo_proxy_state cps; libxl__ev_child child; }; typedef void libxl__colo_callback(struct libxl__egc *egc, libxl__colo_restore_state *crs, int rc); struct libxl__colo_restore_state { /* must set by caller of libxl__colo_(setup|teardown) */ struct libxl__ao *ao; uint32_t domid; int send_back_fd; int recv_fd; int hvm; libxl__colo_callback *callback; char *colo_proxy_script; /* private, colo restore checkpoint state */ libxl__domain_create_cb *saved_cb; void *crcs; /* private, used by qdisk block replication */ bool qdisk_used; bool qdisk_setuped; const char *host; const char *port; /* private, used by colo-proxy */ libxl__colo_proxy_state cps; }; int init_subkind_qdisk(struct libxl__checkpoint_devices_state *cds); void cleanup_subkind_qdisk(struct libxl__checkpoint_devices_state *cds); int init_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds); void cleanup_subkind_colo_nic(struct libxl__checkpoint_devices_state *cds); extern void libxl__colo_restore_setup(struct libxl__egc *egc, libxl__colo_restore_state *crs); extern void libxl__colo_restore_teardown(struct libxl__egc *egc, void *dcs_void, int ret, int retval, int errnoval); extern void libxl__colo_save_setup(struct libxl__egc *egc, struct libxl__colo_save_state *css); extern void libxl__colo_save_teardown(struct libxl__egc *egc, struct libxl__colo_save_state *css, int rc); extern int colo_proxy_setup(libxl__colo_proxy_state *cps); extern void colo_proxy_teardown(libxl__colo_proxy_state *cps); extern void colo_proxy_preresume(libxl__colo_proxy_state *cps); extern void colo_proxy_postresume(libxl__colo_proxy_state *cps); extern int colo_proxy_checkpoint(libxl__colo_proxy_state *cps, unsigned int timeout_us); #endif xen-4.9.2/tools/libxl/libxlu_cfg.c0000664000175000017500000003714313256712137015221 0ustar smbsmb/* * libxlu_cfg.c - xl configuration file parsing: setup and helper functions * * Copyright (C) 2010 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include #include "libxlu_internal.h" #include "libxlu_cfg_y.h" #include "libxlu_cfg_l.h" #include "libxlu_cfg_i.h" XLU_Config *xlu_cfg_init(FILE *report, const char *report_source) { XLU_Config *cfg; cfg= malloc(sizeof(*cfg)); if (!cfg) return 0; cfg->report= report; cfg->config_source= strdup(report_source); if (!cfg->config_source) { free(cfg); return 0; } cfg->settings= 0; return cfg; } static int ctx_prep(CfgParseContext *ctx, XLU_Config *cfg) { int e; ctx->cfg= cfg; ctx->err= 0; ctx->lexerrlineno= -1; ctx->likely_python= 0; ctx->scanner= 0; e= xlu__cfg_yylex_init_extra(ctx, &ctx->scanner); if (e) { fprintf(cfg->report,"%s: unable to create scanner: %s\n", cfg->config_source, strerror(e)); return e; } return 0; } static void ctx_dispose(CfgParseContext *ctx) { if (ctx->scanner) xlu__cfg_yylex_destroy(ctx->scanner); } static void parse(CfgParseContext *ctx) { /* On return, ctx.err will be updated with the error status. */ int r; xlu__cfg_yyset_lineno(1, ctx->scanner); r= xlu__cfg_yyparse(ctx); if (r) assert(ctx->err); if (ctx->err && ctx->likely_python) { fputs( "warning: Config file looks like it contains Python code.\n" "warning: Arbitrary Python is no longer supported.\n" "warning: See http://wiki.xen.org/wiki/PythonInXlConfig\n", ctx->cfg->report); } } int xlu_cfg_readfile(XLU_Config *cfg, const char *real_filename) { FILE *f = 0; int e; CfgParseContext ctx; e = ctx_prep(&ctx, cfg); if (e) { ctx.err= e; goto xe; } f= fopen(real_filename, "r"); if (!f) { ctx.err = errno; fprintf(cfg->report,"%s: unable to open configuration file: %s\n", real_filename, strerror(e)); goto xe; } xlu__cfg_yyrestart(f, ctx.scanner); parse(&ctx); xe: ctx_dispose(&ctx); if (f) fclose(f); return ctx.err; } int xlu_cfg_readdata(XLU_Config *cfg, const char *data, int length) { int e; YY_BUFFER_STATE buf= 0; CfgParseContext ctx; e= ctx_prep(&ctx, cfg); if (e) { ctx.err= e; goto xe; } buf = xlu__cfg_yy_scan_bytes(data, length, ctx.scanner); if (!buf) { fprintf(cfg->report,"%s: unable to allocate scanner buffer\n", cfg->config_source); ctx.err= ENOMEM; goto xe; } parse(&ctx); xe: if (buf) xlu__cfg_yy_delete_buffer(buf, ctx.scanner); ctx_dispose(&ctx); return ctx.err; } void xlu__cfg_value_free(XLU_ConfigValue *value) { int i; if (!value) return; switch (value->type) { case XLU_STRING: free(value->u.string); break; case XLU_LIST: for (i = 0; i < value->u.list.nvalues; i++) xlu__cfg_value_free(value->u.list.values[i]); free(value->u.list.values); } free(value); } void xlu__cfg_set_free(XLU_ConfigSetting *set) { if (!set) return; free(set->name); xlu__cfg_value_free(set->value); free(set); } void xlu_cfg_destroy(XLU_Config *cfg) { XLU_ConfigSetting *set, *set_next; if (!cfg) return; for (set= cfg->settings; set; set= set_next) { set_next= set->next; xlu__cfg_set_free(set); } free(cfg->config_source); free(cfg); } static XLU_ConfigSetting *find(const XLU_Config *cfg, const char *n) { XLU_ConfigSetting *set; for (set= cfg->settings; set; set= set->next) if (!strcmp(set->name, n)) return set; return 0; } static int find_atom(const XLU_Config *cfg, const char *n, XLU_ConfigSetting **set_r, int dont_warn) { XLU_ConfigSetting *set; set= find(cfg,n); if (!set) return ESRCH; if (set->value->type!=XLU_STRING) { if (!dont_warn) fprintf(cfg->report, "%s:%d: warning: parameter `%s' is" " a list but should be a single value\n", cfg->config_source, set->lineno, n); return EINVAL; } *set_r= set; return 0; } enum XLU_ConfigValueType xlu_cfg_value_type(const XLU_ConfigValue *value) { return value->type; } int xlu_cfg_value_get_string(const XLU_Config *cfg, XLU_ConfigValue *value, char **value_r, int dont_warn) { if (value->type != XLU_STRING) { if (!dont_warn) fprintf(cfg->report, "%s:%d:%d: warning: value is not a string\n", cfg->config_source, value->loc.first_line, value->loc.first_column); *value_r = NULL; return EINVAL; } *value_r = value->u.string; return 0; } int xlu_cfg_value_get_list(const XLU_Config *cfg, XLU_ConfigValue *value, XLU_ConfigList **value_r, int dont_warn) { if (value->type != XLU_LIST) { if (!dont_warn) fprintf(cfg->report, "%s:%d:%d: warning: value is not a list\n", cfg->config_source, value->loc.first_line, value->loc.first_column); *value_r = NULL; return EINVAL; } *value_r = &value->u.list; return 0; } XLU_ConfigValue *xlu_cfg_get_listitem2(const XLU_ConfigList *list, int entry) { if (entry < 0 || entry >= list->nvalues) return NULL; return list->values[entry]; } int xlu_cfg_get_string(const XLU_Config *cfg, const char *n, const char **value_r, int dont_warn) { XLU_ConfigSetting *set; int e; e= find_atom(cfg,n,&set,dont_warn); if (e) return e; *value_r= set->value->u.string; return 0; } int xlu_cfg_replace_string(const XLU_Config *cfg, const char *n, char **value_r, int dont_warn) { XLU_ConfigSetting *set; int e; e= find_atom(cfg,n,&set,dont_warn); if (e) return e; free(*value_r); *value_r= strdup(set->value->u.string); return 0; } int xlu_cfg_get_long(const XLU_Config *cfg, const char *n, long *value_r, int dont_warn) { long l; XLU_ConfigSetting *set; int e; char *ep; e= find_atom(cfg,n,&set,dont_warn); if (e) return e; errno= 0; l= strtol(set->value->u.string, &ep, 0); e= errno; if (errno) { e= errno; assert(e==EINVAL || e==ERANGE); if (!dont_warn) fprintf(cfg->report, "%s:%d: warning: parameter `%s' could not be parsed" " as a number: %s\n", cfg->config_source, set->lineno, n, strerror(e)); return e; } if (*ep || ep==set->value->u.string) { if (!dont_warn) fprintf(cfg->report, "%s:%d: warning: parameter `%s' is not a valid number\n", cfg->config_source, set->lineno, n); return EINVAL; } *value_r= l; return 0; } int xlu_cfg_get_defbool(const XLU_Config *cfg, const char *n, libxl_defbool *b, int dont_warn) { int ret; long l; ret = xlu_cfg_get_long(cfg, n, &l, dont_warn); if (ret) return ret; libxl_defbool_set(b, !!l); return 0; } int xlu_cfg_get_list(const XLU_Config *cfg, const char *n, XLU_ConfigList **list_r, int *entries_r, int dont_warn) { XLU_ConfigSetting *set; set= find(cfg,n); if (!set) return ESRCH; if (set->value->type!=XLU_LIST) { if (!dont_warn) { fprintf(cfg->report, "%s:%d: warning: parameter `%s' is a single value" " but should be a list\n", cfg->config_source, set->lineno, n); } return EINVAL; } if (list_r) *list_r= &set->value->u.list; if (entries_r) *entries_r= set->value->u.list.nvalues; return 0; } int xlu_cfg_get_list_as_string_list(const XLU_Config *cfg, const char *n, libxl_string_list *psl, int dont_warn) { int i, rc, nr; XLU_ConfigList *list; libxl_string_list sl; rc = xlu_cfg_get_list(cfg, n, &list, &nr, dont_warn); if (rc) return rc; sl = malloc(sizeof(char*)*(nr + 1)); if (sl == NULL) return ENOMEM; for (i=0; i= list->nvalues) return 0; if (list->values[entry]->type != XLU_STRING) return 0; return list->values[entry]->u.string; } XLU_ConfigValue *xlu__cfg_string_mk(CfgParseContext *ctx, char *atom, YYLTYPE *loc) { XLU_ConfigValue *value = NULL; if (ctx->err) goto x; value = malloc(sizeof(*value)); if (!value) goto xe; value->type = XLU_STRING; value->u.string = atom; memcpy(&value->loc, loc, sizeof(*loc)); return value; xe: ctx->err= errno; x: free(value); free(atom); return NULL; } XLU_ConfigValue *xlu__cfg_list_mk(CfgParseContext *ctx, XLU_ConfigValue *val, YYLTYPE *loc) { XLU_ConfigValue *value = NULL; XLU_ConfigValue **values = NULL; if (ctx->err) goto x; values = malloc(sizeof(*values)); if (!values) goto xe; values[0] = val; value = malloc(sizeof(*value)); if (!value) goto xe; value->type = XLU_LIST; value->u.list.nvalues = !!val; value->u.list.avalues = 1; value->u.list.values = values; memcpy(&value->loc, loc, sizeof(*loc)); return value; xe: ctx->err= errno; x: free(value); free(values); xlu__cfg_value_free(val); return NULL; } void xlu__cfg_list_append(CfgParseContext *ctx, XLU_ConfigValue *list, XLU_ConfigValue *val) { if (ctx->err) return; assert(val); assert(list->type == XLU_LIST); if (list->u.list.nvalues >= list->u.list.avalues) { int new_avalues; XLU_ConfigValue **new_values = NULL; if (list->u.list.avalues > INT_MAX / 100) { ctx->err = ERANGE; xlu__cfg_value_free(val); return; } new_avalues = list->u.list.avalues * 4; new_values = realloc(list->u.list.values, sizeof(*new_values) * new_avalues); if (!new_values) { ctx->err = errno; xlu__cfg_value_free(val); return; } list->u.list.avalues = new_avalues; list->u.list.values = new_values; } list->u.list.values[list->u.list.nvalues] = val; list->u.list.nvalues++; } void xlu__cfg_set_store(CfgParseContext *ctx, char *name, XLU_ConfigValue *val, int lineno) { XLU_ConfigSetting *set; if (ctx->err) return; assert(name); set = malloc(sizeof(*set)); if (!set) { ctx->err = errno; return; } set->name= name; set->value = val; set->lineno= lineno; set->next= ctx->cfg->settings; ctx->cfg->settings= set; } char *xlu__cfgl_strdup(CfgParseContext *ctx, const char *src) { char *result; if (ctx->err) return 0; result= strdup(src); if (!result) ctx->err= errno; return result; } char *xlu__cfgl_dequote(CfgParseContext *ctx, const char *src) { char *result; const char *p; char *q; int len, c, nc; if (ctx->err) return 0; len= strlen(src); assert(len>=2 && src[0]==src[len-1]); result= malloc(len-1); if (!result) { ctx->err= errno; return 0; } q= result; for (p= src+1; p < src+len-1; ) { c= *p++; if (c=='\\') { assert(p < src+len-1); nc= *p++; if (nc=='"' || nc=='\'' || nc=='\\') { *q++= nc; } else if (nc=='a') { *q++= '\007'; } else if (nc=='b') { *q++= '\010'; } else if (nc=='f') { *q++= '\014'; } else if (nc=='n') { *q++= '\n'; } else if (nc=='r') { *q++= '\r'; } else if (nc=='t') { *q++= '\t'; } else if (nc=='v') { *q++= '\013'; } else if (nc=='x') { #define NUMERIC_CHAR(minlen,maxlen,base,basetext) do{ \ char numbuf[(maxlen)+1], *ep; \ unsigned long val; \ \ strncpy(numbuf,p,(maxlen)); \ numbuf[(maxlen)]= 0; \ val= strtoul(numbuf, &ep, (base)); \ if (ep <= numbuf+(minlen)) { \ xlu__cfgl_lexicalerror(ctx,"invalid digit after" \ " backslash " basetext "numerical character escape" \ " in quoted string"); \ ctx->err= EINVAL; \ goto x; \ } \ p += (ep - numbuf); \ }while(0) p++; NUMERIC_CHAR(2,2,16,"hex"); } else if (nc>='0' && nc<='7') { NUMERIC_CHAR(1,3,10,"octal"); } else { xlu__cfgl_lexicalerror(ctx, "invalid character after backlash in quoted string"); ctx->err= EINVAL; goto x; } assert(p <= src+len-1); } else { *q++= c; } } x: *q++= 0; return result; } void xlu__cfgl_lexicalerror(CfgParseContext *ctx, char const *msg) { YYLTYPE loc; loc.first_line= xlu__cfg_yyget_lineno(ctx->scanner); xlu__cfg_yyerror(&loc, ctx, msg); ctx->lexerrlineno= loc.first_line; } void xlu__cfg_yyerror(YYLTYPE *loc, CfgParseContext *ctx, char const *msg) { const char *text, *newline; int len, lineno; lineno= loc->first_line; if (lineno <= ctx->lexerrlineno) return; text= xlu__cfg_yyget_text(ctx->scanner); len= xlu__cfg_yyget_leng(ctx->scanner); newline= ""; if (len>0 && text[len-1]=='\n') { len--; lineno--; if (!len) { newline= ""; } } while (len>0 && (text[len-1]=='\t' || text[len-1]==' ')) { len--; } fprintf(ctx->cfg->report, "%s:%d: config parsing error near %s%.*s%s%s: %s\n", ctx->cfg->config_source, lineno, len?"`":"", len, text, len?"'":"", newline, msg); if (!ctx->err) ctx->err= EINVAL; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_cfg_l.l0000664000175000017500000000607513256712137015545 0ustar smbsmb/* -*- fundamental -*- */ /* * libxlu_cfg_l.l - xl configuration file parsing: lexer * * Copyright (C) 2010 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ %{ #include "libxlu_cfg_i.h" #define ctx ((CfgParseContext*)yyextra) #define YY_NO_INPUT #define GOT(x) do{ \ yylloc->first_line= yylineno; \ return (x); \ }while(0) /* Some versions of flex have a bug (Fedora bugzilla 612465) which causes * it to fail to declare these functions, which it defines. So declare * them ourselves. Hopefully we won't have to simultaneously support * a flex version which declares these differently somehow. */ int xlu__cfg_yyget_column(yyscan_t yyscanner); void xlu__cfg_yyset_column(int column_no, yyscan_t yyscanner); %} %option warn %option nodefault %option batch %option 8bit %option yylineno %option noyywrap %option bison-bridge %option bison-locations %option reentrant %option prefix="xlu__cfg_yy" %option nounput %x lexerr %% [a-z][._0-9a-z]* { yylval->string= xlu__cfgl_strdup(ctx,yytext); GOT(IDENT); } [0-9][0-9a-fx]* { yylval->string= xlu__cfgl_strdup(ctx,yytext); GOT(NUMBER); } [ \t] , { GOT(','); } \[ { GOT('['); } \] { GOT(']'); } \= { GOT('='); } \; { GOT(';'); } \n|\#.*\n { yylloc->first_line= yylineno-1; return NEWLINE; } \'([^\'\\\n]|\\.)*\' { yylval->string= xlu__cfgl_dequote(ctx,yytext); GOT(STRING); } \"([^\"\\\n]|\\.)*\" { yylval->string= xlu__cfgl_dequote(ctx,yytext); GOT(STRING); } [+-.():] { ctx->likely_python= 1; BEGIN(lexerr); yymore(); } . { BEGIN(lexerr); yymore(); } [^ \t\n]*|[ \t] { xlu__cfgl_lexicalerror(ctx,"lexical error"); BEGIN(0); } \n { xlu__cfgl_lexicalerror(ctx,"lexical error"); BEGIN(0); GOT(NEWLINE); } xen-4.9.2/tools/libxl/libxl_colo_restore.c0000664000175000017500000010133513256712137016767 0ustar smbsmb/* * Copyright (C) 2016 FUJITSU LIMITED * Author: Wen Congyang * Yang Hongyang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include "libxl_sr_stream_format.h" typedef struct libxl__colo_restore_checkpoint_state libxl__colo_restore_checkpoint_state; struct libxl__colo_restore_checkpoint_state { libxl__domain_suspend_state dsps; libxl__logdirty_switch lds; libxl__colo_restore_state *crs; libxl__stream_write_state sws; int status; bool preresume; /* used for teardown */ int teardown_devices; int saved_rc; char *state_file; void (*callback)(libxl__egc *, libxl__colo_restore_checkpoint_state *, int); }; extern const libxl__checkpoint_device_instance_ops colo_restore_device_nic; extern const libxl__checkpoint_device_instance_ops colo_restore_device_qdisk; static const libxl__checkpoint_device_instance_ops *colo_restore_ops[] = { &colo_restore_device_nic, &colo_restore_device_qdisk, NULL, }; /* ===================== colo: common functions ===================== */ static void colo_enable_logdirty(libxl__colo_restore_state *crs, libxl__egc *egc) { libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); libxl__colo_restore_checkpoint_state *crcs = crs->crcs; /* Convenience aliases */ const uint32_t domid = crs->domid; libxl__logdirty_switch *const lds = &crcs->lds; EGC_GC; /* we need to know which pages are dirty to restore the guest */ if (xc_shadow_control(CTX->xch, domid, XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY, NULL, 0, NULL, 0, NULL) < 0) { LOGD(ERROR, domid, "cannot enable secondary vm's logdirty"); lds->callback(egc, lds, ERROR_FAIL); return; } if (crs->hvm) { libxl__domain_common_switch_qemu_logdirty(egc, domid, 1, lds); return; } lds->callback(egc, lds, 0); } static void colo_disable_logdirty(libxl__colo_restore_state *crs, libxl__egc *egc) { libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); libxl__colo_restore_checkpoint_state *crcs = crs->crcs; /* Convenience aliases */ const uint32_t domid = crs->domid; libxl__logdirty_switch *const lds = &crcs->lds; EGC_GC; /* we need to know which pages are dirty to restore the guest */ if (xc_shadow_control(CTX->xch, domid, XEN_DOMCTL_SHADOW_OP_OFF, NULL, 0, NULL, 0, NULL) < 0) LOGD(WARN, domid, "cannot disable secondary vm's logdirty"); if (crs->hvm) { libxl__domain_common_switch_qemu_logdirty(egc, domid, 0, lds); return; } lds->callback(egc, lds, 0); } static void colo_resume_vm(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs, int restore_device_model) { libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs); int rc; /* Convenience aliases */ libxl__colo_restore_state *const crs = crcs->crs; EGC_GC; if (!crs->saved_cb) { /* TODO: sync mmu for hvm? */ if (restore_device_model) { rc = libxl__qmp_restore(gc, crs->domid, crcs->state_file); if (rc) { LOGD(ERROR, crs->domid, "cannot restore device model for secondary vm"); crcs->callback(egc, crcs, rc); return; } } rc = libxl__domain_resume(gc, crs->domid, 0); if (rc) LOGD(ERROR, crs->domid, "cannot resume secondary vm"); crcs->callback(egc, crcs, rc); return; } libxl__xc_domain_restore_done(egc, dcs, 0, 0, 0); return; } static int init_device_subkind(libxl__checkpoint_devices_state *cds) { /* init device subkind-specific state in the libxl ctx */ int rc; STATE_AO_GC(cds->ao); rc = init_subkind_colo_nic(cds); if (rc) goto out; rc = init_subkind_qdisk(cds); if (rc) { cleanup_subkind_colo_nic(cds); goto out; } rc = 0; out: return rc; } static void cleanup_device_subkind(libxl__checkpoint_devices_state *cds) { /* cleanup device subkind-specific state in the libxl ctx */ STATE_AO_GC(cds->ao); cleanup_subkind_colo_nic(cds); cleanup_subkind_qdisk(cds); } /* ================ colo: setup restore environment ================ */ static void libxl__colo_domain_create_cb(libxl__egc *egc, libxl__domain_create_state *dcs, int rc, uint32_t domid); static int init_dsps(libxl__domain_suspend_state *dsps) { int rc = ERROR_FAIL; libxl_domain_type type; STATE_AO_GC(dsps->ao); libxl__xswait_init(&dsps->pvcontrol); libxl__ev_evtchn_init(&dsps->guest_evtchn); libxl__ev_xswatch_init(&dsps->guest_watch); libxl__ev_time_init(&dsps->guest_timeout); type = libxl__domain_type(gc, dsps->domid); if (type == LIBXL_DOMAIN_TYPE_INVALID) goto out; dsps->type = type; dsps->guest_evtchn.port = -1; dsps->guest_evtchn_lockfd = -1; dsps->guest_responded = 0; dsps->dm_savefile = libxl__device_model_savefile(gc, dsps->domid); /* Secondary vm is not created, so we cannot get evtchn port */ rc = 0; out: return rc; } /* * checkpoint callbacks are called in the following order: * 1. resume * 2. wait checkpoint * 3. suspend * 4. checkpoint */ static void libxl__colo_restore_domain_resume_callback(void *data); static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data); static void libxl__colo_restore_domain_suspend_callback(void *data); static void libxl__colo_restore_domain_checkpoint_callback(void *data); void libxl__colo_restore_setup(libxl__egc *egc, libxl__colo_restore_state *crs) { libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); libxl__colo_restore_checkpoint_state *crcs; int rc = ERROR_FAIL; /* Convenience aliases */ libxl__srm_restore_autogen_callbacks *const callbacks = &dcs->srs.shs.callbacks.restore.a; const int domid = crs->domid; STATE_AO_GC(crs->ao); GCNEW(crcs); crs->crcs = crcs; crcs->crs = crs; crs->qdisk_setuped = false; crs->qdisk_used = false; if (dcs->colo_proxy_script) crs->colo_proxy_script = libxl__strdup(gc, dcs->colo_proxy_script); else crs->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup", libxl__xen_script_dir_path()); /* setup dsps */ crcs->dsps.ao = ao; crcs->dsps.domid = domid; if (init_dsps(&crcs->dsps)) goto out; callbacks->postcopy = libxl__colo_restore_domain_resume_callback; callbacks->wait_checkpoint = libxl__colo_restore_domain_wait_checkpoint_callback; callbacks->suspend = libxl__colo_restore_domain_suspend_callback; callbacks->checkpoint = libxl__colo_restore_domain_checkpoint_callback; /* * Secondary vm is running in colo mode, so we need to call * libxl__xc_domain_restore_done() to create secondary vm. * But we will exit in domain_create_cb(). So replace the * callback here. */ crs->saved_cb = dcs->callback; dcs->callback = libxl__colo_domain_create_cb; crcs->state_file = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid); crcs->status = LIBXL_COLO_SETUPED; libxl__logdirty_init(&crcs->lds); crcs->lds.ao = ao; crcs->sws.fd = crs->send_back_fd; crcs->sws.ao = ao; crcs->sws.back_channel = true; dcs->cds.concrete_data = crs; libxl__stream_write_start(egc, &crcs->sws); rc = 0; out: crs->callback(egc, crs, rc); return; } static void libxl__colo_domain_create_cb(libxl__egc *egc, libxl__domain_create_state *dcs, int rc, uint32_t domid) { libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs; crcs->callback(egc, crcs, rc); } /* ================ colo: teardown restore environment ================ */ static void colo_restore_teardown_devices_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs); static void do_failover_done(libxl__egc *egc, libxl__colo_restore_checkpoint_state* crcs, int rc); static void colo_disable_logdirty_done(libxl__egc *egc, libxl__logdirty_switch *lds, int rc); static void libxl__colo_restore_teardown_done(libxl__egc *egc, libxl__colo_restore_state *crs, int rc); void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void, int ret, int retval, int errnoval) { libxl__domain_create_state *dcs = dcs_void; libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs; int rc = 1; /* convenience aliases */ libxl__colo_restore_state *const crs = &dcs->crs; EGC_GC; if (ret == 0 && retval == 0) rc = 0; LOGD(INFO, crs->domid, "%s", rc ? "colo fails" : "failover"); libxl__stream_write_abort(egc, &crcs->sws, 1); if (crs->saved_cb) { /* crcs->status is LIBXL_COLO_SETUPED */ dcs->srs.completion_callback = NULL; } libxl__xc_domain_restore_done(egc, dcs, ret, retval, errnoval); if (crs->qdisk_setuped) { libxl__qmp_stop_replication(gc, crs->domid, false); crs->qdisk_setuped = false; } crcs->saved_rc = rc; if (!crcs->teardown_devices) { colo_restore_teardown_devices_done(egc, &dcs->cds, 0); return; } dcs->cds.callback = colo_restore_teardown_devices_done; libxl__checkpoint_devices_teardown(egc, &dcs->cds); } static void colo_restore_teardown_devices_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_restore_state *crs = cds->concrete_data; libxl__colo_restore_checkpoint_state *crcs = crs->crcs; libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); EGC_GC; if (rc) LOGD(ERROR, cds->domid, "COLO: failed to teardown device for guest," " rc %d", rc); if (crcs->teardown_devices) cleanup_device_subkind(cds); colo_proxy_teardown(&crs->cps); rc = crcs->saved_rc; if (!rc) { crcs->callback = do_failover_done; do_failover(egc, crs); return; } libxl__colo_restore_teardown_done(egc, crs, rc); } static void do_failover(libxl__egc *egc, libxl__colo_restore_state *crs) { libxl__colo_restore_checkpoint_state *crcs = crs->crcs; /* Convenience aliases */ const int status = crcs->status; libxl__logdirty_switch *const lds = &crcs->lds; EGC_GC; switch(status) { case LIBXL_COLO_SETUPED: /* * We will come here only when reading emulator xenstore data or * emulator context fails, and libxl__xc_domain_restore_done() * is not called. In this case, the migration is not finished, * so we cannot do failover. */ LOGD(ERROR, crs->domid, "migration fails"); crcs->callback(egc, crcs, ERROR_FAIL); return; case LIBXL_COLO_SUSPENDED: case LIBXL_COLO_RESUMED: /* disable logdirty first */ lds->callback = colo_disable_logdirty_done; colo_disable_logdirty(crs, egc); return; default: LOGD(ERROR, crs->domid, "invalid status: %d", status); crcs->callback(egc, crcs, ERROR_FAIL); } } static void do_failover_done(libxl__egc *egc, libxl__colo_restore_checkpoint_state* crcs, int rc) { libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs); /* Convenience aliases */ libxl__colo_restore_state *const crs = crcs->crs; EGC_GC; if (rc) LOGD(ERROR, crs->domid, "cannot do failover"); libxl__colo_restore_teardown_done(egc, crs, rc); } static void colo_disable_logdirty_done(libxl__egc *egc, libxl__logdirty_switch *lds, int rc) { libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds); EGC_GC; if (rc) LOGD(WARN, crcs->crs->domid, "cannot disable logdirty"); if (crcs->status == LIBXL_COLO_SUSPENDED) { /* * failover when reading state from master, so no need to * call libxl__qmp_restore(). */ colo_resume_vm(egc, crcs, 0); return; } /* If we cannot disable logdirty, we still can do failover */ crcs->callback(egc, crcs, 0); } static void libxl__colo_restore_teardown_done(libxl__egc *egc, libxl__colo_restore_state *crs, int rc) { libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); EGC_GC; /* convenience aliases */ const int domid = crs->domid; const libxl_ctx *const ctx = libxl__gc_owner(gc); xc_interface *const xch = ctx->xch; if (!rc) /* failover, no need to destroy the secondary vm */ goto out; xc_domain_destroy(xch, domid); out: if (crs->saved_cb) { dcs->callback = crs->saved_cb; crs->saved_cb = NULL; } dcs->callback(egc, dcs, rc, crs->domid); } static void colo_common_write_stream_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc); static void colo_common_read_stream_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc); /* ======================== colo: checkpoint ======================= */ /* * Do the following things when resuming secondary vm: * 1. read emulator xenstore data * 2. read emulator context * 3. REC_TYPE_CHECKPOINT_END */ static void libxl__colo_restore_domain_checkpoint_callback(void *data) { libxl__save_helper_state *shs = data; libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs); libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs); libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs; crcs->callback = NULL; dcs->srs.checkpoint_callback = colo_common_read_stream_done; libxl__stream_read_start_checkpoint(shs->egc, &dcs->srs); } /* ===================== colo: resume secondary vm ===================== */ /* * Do the following things when resuming secondary vm the first time: * 1. resume secondary vm * 2. enable log dirty * 3. setup checkpoint devices * 4. write CHECKPOINT_SVM_READY * 5. unpause secondary vm * 6. write CHECKPOINT_SVM_RESUMED * * Do the following things when resuming secondary vm: * 1. write CHECKPOINT_SVM_READY * 2. resume secondary vm * 3. write CHECKPOINT_SVM_RESUMED */ static void colo_send_svm_ready(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs); static void colo_send_svm_ready_done(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs, int rc); static void colo_restore_preresume_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void colo_restore_resume_vm(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs); static void colo_resume_vm_done(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs, int rc); static void colo_write_svm_resumed(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs); static void colo_enable_logdirty_done(libxl__egc *egc, libxl__logdirty_switch *lds, int retval); static void colo_reenable_logdirty(libxl__egc *egc, libxl__logdirty_switch *lds, int rc); static void colo_reenable_logdirty_done(libxl__egc *egc, libxl__logdirty_switch *lds, int rc); static void colo_setup_checkpoint_devices(libxl__egc *egc, libxl__colo_restore_state *crs); static void colo_restore_setup_cds_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void colo_unpause_svm(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs); static void libxl__colo_restore_domain_resume_callback(void *data) { libxl__save_helper_state *shs = data; libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs); libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs); libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs; if (crcs->teardown_devices) colo_send_svm_ready(shs->egc, crcs); else colo_restore_resume_vm(shs->egc, crcs); } static void colo_send_svm_ready(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs) { libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_READY }; crcs->callback = colo_send_svm_ready_done; crcs->sws.checkpoint_callback = colo_common_write_stream_done; libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs); } static void colo_send_svm_ready_done(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs, int rc) { libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs); /* Convenience aliases */ libxl__checkpoint_devices_state *cds = &dcs->cds; if (!crcs->preresume) { crcs->preresume = true; colo_unpause_svm(egc, crcs); return; } cds->callback = colo_restore_preresume_cb; libxl__checkpoint_devices_preresume(egc, cds); } static void colo_restore_preresume_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_restore_state *crs = cds->concrete_data; libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); libxl__colo_restore_checkpoint_state *crcs = crs->crcs; /* Convenience aliases */ libxl__save_helper_state *const shs = &dcs->srs.shs; EGC_GC; if (rc) { LOGD(ERROR, crs->domid, "preresume fails"); goto out; } if (crs->qdisk_setuped) { if (libxl__qmp_colo_do_checkpoint(gc, crs->domid)) { LOGD(ERROR, crs->domid, "doing checkpoint fails"); goto out; } } if (!crs->cps.is_userspace_proxy) colo_proxy_preresume(&crs->cps); colo_restore_resume_vm(egc, crcs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0); } static void colo_restore_resume_vm(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs) { crcs->callback = colo_resume_vm_done; colo_resume_vm(egc, crcs, 1); } static void colo_resume_vm_done(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs, int rc) { libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs); /* Convenience aliases */ libxl__colo_restore_state *const crs = crcs->crs; libxl__logdirty_switch *const lds = &crcs->lds; libxl__save_helper_state *const shs = &dcs->srs.shs; EGC_GC; if (rc) { LOGD(ERROR, crs->domid, "cannot resume secondary vm"); goto out; } crcs->status = LIBXL_COLO_RESUMED; colo_proxy_postresume(&crs->cps); /* avoid calling stream->completion_callback() more than once */ if (crs->saved_cb) { dcs->callback = crs->saved_cb; crs->saved_cb = NULL; dcs->srs.completion_callback = NULL; lds->callback = colo_enable_logdirty_done; colo_enable_logdirty(crs, egc); return; } colo_write_svm_resumed(egc, crcs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0); } static void colo_write_svm_resumed(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs) { libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_RESUMED }; crcs->callback = NULL; crcs->sws.checkpoint_callback = colo_common_write_stream_done; libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs); } static void colo_enable_logdirty_done(libxl__egc *egc, libxl__logdirty_switch *lds, int rc) { libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds); /* Convenience aliases */ libxl__colo_restore_state *const crs = crcs->crs; EGC_GC; if (rc) { /* * log-dirty already enabled? There's no test op, * so attempt to disable then reenable it */ lds->callback = colo_reenable_logdirty; colo_disable_logdirty(crs, egc); return; } colo_setup_checkpoint_devices(egc, crs); } static void colo_reenable_logdirty(libxl__egc *egc, libxl__logdirty_switch *lds, int rc) { libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds); libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs); /* Convenience aliases */ libxl__colo_restore_state *const crs = crcs->crs; libxl__save_helper_state *const shs = &dcs->srs.shs; EGC_GC; if (rc) { LOGD(ERROR, crs->domid, "cannot enable logdirty"); goto out; } lds->callback = colo_reenable_logdirty_done; colo_enable_logdirty(crs, egc); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0); } static void colo_reenable_logdirty_done(libxl__egc *egc, libxl__logdirty_switch *lds, int rc) { libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(lds, *crcs, lds); libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs); /* Convenience aliases */ libxl__save_helper_state *const shs = &dcs->srs.shs; EGC_GC; if (rc) { LOGD(ERROR, crcs->crs->domid, "cannot enable logdirty"); goto out; } colo_setup_checkpoint_devices(egc, crcs->crs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0); } /* * We cannot setup checkpoint devices in libxl__colo_restore_setup(), * because the guest is not ready. */ static void colo_setup_checkpoint_devices(libxl__egc *egc, libxl__colo_restore_state *crs) { libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); libxl__colo_restore_checkpoint_state *crcs = crs->crcs; /* Convenience aliases */ libxl__checkpoint_devices_state *cds = &dcs->cds; libxl__save_helper_state *const shs = &dcs->srs.shs; STATE_AO_GC(crs->ao); if (crs->cps.is_userspace_proxy) cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VBD); else cds->device_kind_flags = (1 << LIBXL__DEVICE_KIND_VIF) | (1 << LIBXL__DEVICE_KIND_VBD); cds->callback = colo_restore_setup_cds_done; cds->ao = ao; cds->domid = crs->domid; cds->ops = colo_restore_ops; crs->cps.ao = ao; if (!crs->cps.is_userspace_proxy) { if (colo_proxy_setup(&crs->cps)) { LOGD(ERROR, cds->domid, "COLO: failed to setup colo proxy for guest"); goto out; } } if (init_device_subkind(cds)) goto out; crcs->teardown_devices = 1; libxl__checkpoint_devices_setup(egc, cds); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0); } static void colo_restore_setup_cds_done(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_restore_state *crs = cds->concrete_data; libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); libxl__colo_restore_checkpoint_state *crcs = crs->crcs; /* Convenience aliases */ libxl__save_helper_state *const shs = &dcs->srs.shs; EGC_GC; if (rc) { LOGD(ERROR, cds->domid, "COLO: failed to setup device for guest"); goto out; } if (crs->qdisk_used && !crs->qdisk_setuped) { if (libxl__qmp_start_replication(gc, crs->domid, false)) { LOGD(ERROR, cds->domid, "starting replication fails"); goto out; } crs->qdisk_setuped = true; } colo_send_svm_ready(egc, crcs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0); } static void colo_unpause_svm(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs) { libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs); int rc; /* Convenience aliases */ const uint32_t domid = crcs->crs->domid; libxl__save_helper_state *const shs = &dcs->srs.shs; EGC_GC; /* We have enabled secondary vm's logdirty, so we can unpause it now */ rc = libxl_domain_unpause(CTX, domid); if (rc) { LOGD(ERROR, domid, "cannot unpause secondary vm"); goto out; } colo_write_svm_resumed(egc, crcs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, shs, 0); } /* ===================== colo: wait new checkpoint ===================== */ static void colo_restore_commit_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void colo_stream_read_done(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs, int real_size); static void libxl__colo_restore_domain_wait_checkpoint_callback(void *data) { libxl__save_helper_state *shs = data; libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs); libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs); /* Convenience aliases */ libxl__checkpoint_devices_state *cds = &dcs->cds; cds->callback = colo_restore_commit_cb; libxl__checkpoint_devices_commit(shs->egc, cds); } static void colo_restore_commit_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_restore_state *crs = cds->concrete_data; libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); libxl__colo_restore_checkpoint_state *crcs = crs->crcs; EGC_GC; if (rc) { LOGD(ERROR, crs->domid, "commit fails"); goto out; } crcs->callback = colo_stream_read_done; dcs->srs.checkpoint_callback = colo_common_read_stream_done; libxl__stream_read_checkpoint_state(egc, &dcs->srs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0); } static void colo_stream_read_done(libxl__egc *egc, libxl__colo_restore_checkpoint_state *crcs, int id) { libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs); int ok = 0; EGC_GC; if (id != CHECKPOINT_NEW) { LOGD(ERROR, crcs->crs->domid, "invalid section: %d", id); goto out; } ok = 1; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok); } /* ===================== colo: suspend secondary vm ===================== */ /* * Do the following things when resuming secondary vm: * 1. suspend secondary vm * 2. send CHECKPOINT_SVM_SUSPENDED */ static void colo_suspend_vm_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int ok); static void colo_restore_postsuspend_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc); static void libxl__colo_restore_domain_suspend_callback(void *data) { libxl__save_helper_state *shs = data; libxl__stream_read_state *srs = CONTAINER_OF(shs, *srs, shs); libxl__domain_create_state *dcs = CONTAINER_OF(srs, *dcs, srs); libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs; STATE_AO_GC(dcs->ao); /* Convenience aliases */ libxl__domain_suspend_state *const dsps = &crcs->dsps; /* suspend secondary vm */ dsps->callback_common_done = colo_suspend_vm_done; libxl__domain_suspend(shs->egc, dsps); } static void colo_suspend_vm_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int rc) { libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(dsps, *crcs, dsps); libxl__colo_restore_state *crs = crcs->crs; libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); /* Convenience aliases */ libxl__checkpoint_devices_state *cds = &dcs->cds; EGC_GC; if (rc) { LOGD(ERROR, crs->domid, "cannot suspend secondary vm"); goto out; } crcs->status = LIBXL_COLO_SUSPENDED; if (libxl__qmp_query_xen_replication_status(gc, crs->domid)) { LOGD(ERROR, crs->domid, "replication error occurs when secondary vm is running"); goto out; } cds->callback = colo_restore_postsuspend_cb; libxl__checkpoint_devices_postsuspend(egc, cds); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, 0); } static void colo_restore_postsuspend_cb(libxl__egc *egc, libxl__checkpoint_devices_state *cds, int rc) { libxl__colo_restore_state *crs = cds->concrete_data; libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); libxl__colo_restore_checkpoint_state *crcs = crs->crcs; libxl_sr_checkpoint_state srcs = { .id = CHECKPOINT_SVM_SUSPENDED }; EGC_GC; if (rc) { LOGD(ERROR, crs->domid, "postsuspend fails"); goto out; } crcs->callback = NULL; crcs->sws.checkpoint_callback = colo_common_write_stream_done; libxl__stream_write_checkpoint_state(egc, &crcs->sws, &srcs); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, !rc); } /* ===================== colo: common callback ===================== */ static void colo_common_write_stream_done(libxl__egc *egc, libxl__stream_write_state *stream, int rc) { libxl__colo_restore_checkpoint_state *crcs = CONTAINER_OF(stream, *crcs, sws); libxl__domain_create_state *dcs = CONTAINER_OF(crcs->crs, *dcs, crs); int ok; EGC_GC; if (rc < 0) { /* TODO: it may be a internal error, but we don't know */ LOGD(ERROR, crcs->crs->domid, "sending data fails"); ok = 2; goto out; } if (!crcs->callback) { /* Everythins is OK */ ok = 1; goto out; } crcs->callback(egc, crcs, 0); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok); } static void colo_common_read_stream_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc) { libxl__domain_create_state *dcs = CONTAINER_OF(stream, *dcs, srs); libxl__colo_restore_checkpoint_state *crcs = dcs->crs.crcs; int ok; EGC_GC; if (rc < 0) { /* TODO: it may be a internal error, but we don't know */ LOGD(ERROR, crcs->crs->domid, "reading data fails"); ok = 2; goto out; } if (!crcs->callback) { /* Everythins is OK */ ok = 1; goto out; } /* rc contains the id */ crcs->callback(egc, crcs, rc); return; out: libxl__xc_domain_saverestore_async_callback_done(egc, &dcs->srs.shs, ok); } xen-4.9.2/tools/libxl/libxl_dom_suspend.c0000664000175000017500000003777613256712137016631 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" /*====================== Domain suspend =======================*/ int libxl__domain_suspend_init(libxl__egc *egc, libxl__domain_suspend_state *dsps, libxl_domain_type type) { STATE_AO_GC(dsps->ao); int rc = ERROR_FAIL; int port; /* Convenience aliases */ const uint32_t domid = dsps->domid; libxl__xswait_init(&dsps->pvcontrol); libxl__ev_evtchn_init(&dsps->guest_evtchn); libxl__ev_xswatch_init(&dsps->guest_watch); libxl__ev_time_init(&dsps->guest_timeout); if (type == LIBXL_DOMAIN_TYPE_INVALID) goto out; dsps->type = type; dsps->guest_evtchn.port = -1; dsps->guest_evtchn_lockfd = -1; dsps->guest_responded = 0; dsps->dm_savefile = libxl__device_model_savefile(gc, domid); port = xs_suspend_evtchn_port(domid); if (port >= 0) { rc = libxl__ctx_evtchn_init(gc); if (rc) goto out; dsps->guest_evtchn.port = xc_suspend_evtchn_init_exclusive(CTX->xch, CTX->xce, domid, port, &dsps->guest_evtchn_lockfd); if (dsps->guest_evtchn.port < 0) { LOGD(WARN, domid, "Suspend event channel initialization failed"); rc = ERROR_FAIL; goto out; } } rc = 0; out: return rc; } /*----- callbacks, called by xc_domain_save -----*/ int libxl__domain_suspend_device_model(libxl__gc *gc, libxl__domain_suspend_state *dsps) { int ret = 0; uint32_t const domid = dsps->domid; const char *const filename = dsps->dm_savefile; switch (libxl__device_model_version_running(gc, domid)) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: { LOGD(DEBUG, domid, "Saving device model state to %s", filename); libxl__qemu_traditional_cmd(gc, domid, "save"); libxl__wait_for_device_model_deprecated(gc, domid, "paused", NULL, NULL, NULL); break; } case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: if (libxl__qmp_stop(gc, domid)) return ERROR_FAIL; /* Save DM state into filename */ ret = libxl__qmp_save(gc, domid, filename); if (ret) unlink(filename); break; case LIBXL_DEVICE_MODEL_VERSION_NONE: break; default: return ERROR_INVAL; } return ret; } static void domain_suspend_common_wait_guest(libxl__egc *egc, libxl__domain_suspend_state *dsps); static void domain_suspend_common_guest_suspended(libxl__egc *egc, libxl__domain_suspend_state *dsps); static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc, libxl__xswait_state *xswa, int rc, const char *state); static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc, libxl__ev_evtchn *evev); static void suspend_common_wait_guest_watch(libxl__egc *egc, libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path); static void suspend_common_wait_guest_check(libxl__egc *egc, libxl__domain_suspend_state *dsps); static void suspend_common_wait_guest_timeout(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc); static void domain_suspend_common_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int rc); static void domain_suspend_callback_common(libxl__egc *egc, libxl__domain_suspend_state *dsps); static void domain_suspend_callback_common_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int rc); /* calls dsps->callback_common_done when done */ void libxl__domain_suspend(libxl__egc *egc, libxl__domain_suspend_state *dsps) { domain_suspend_callback_common(egc, dsps); } static bool domain_suspend_pvcontrol_acked(const char *state) { /* any value other than "suspend", including ENOENT (i.e. !state), is OK */ if (!state) return 1; return strcmp(state,"suspend"); } /* calls dsps->callback_common_done when done */ static void domain_suspend_callback_common(libxl__egc *egc, libxl__domain_suspend_state *dsps) { STATE_AO_GC(dsps->ao); uint64_t hvm_s_state = 0, hvm_pvdrv = 0; int ret, rc; /* Convenience aliases */ const uint32_t domid = dsps->domid; if (dsps->type == LIBXL_DOMAIN_TYPE_HVM) { xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_CALLBACK_IRQ, &hvm_pvdrv); xc_hvm_param_get(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, &hvm_s_state); } if ((hvm_s_state == 0) && (dsps->guest_evtchn.port >= 0)) { LOGD(DEBUG, domid, "issuing %s suspend request via event channel", dsps->type == LIBXL_DOMAIN_TYPE_HVM ? "PVHVM" : "PV"); ret = xenevtchn_notify(CTX->xce, dsps->guest_evtchn.port); if (ret < 0) { LOGD(ERROR, domid, "xenevtchn_notify failed ret=%d", ret); rc = ERROR_FAIL; goto err; } dsps->guest_evtchn.callback = domain_suspend_common_wait_guest_evtchn; rc = libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn); if (rc) goto err; rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout, suspend_common_wait_guest_timeout, 60*1000); if (rc) goto err; return; } if (dsps->type == LIBXL_DOMAIN_TYPE_HVM && (!hvm_pvdrv || hvm_s_state)) { LOGD(DEBUG, domid, "Calling xc_domain_shutdown on HVM domain"); ret = xc_domain_shutdown(CTX->xch, domid, SHUTDOWN_suspend); if (ret < 0) { LOGED(ERROR, domid, "xc_domain_shutdown failed"); rc = ERROR_FAIL; goto err; } /* The guest does not (need to) respond to this sort of request. */ dsps->guest_responded = 1; domain_suspend_common_wait_guest(egc, dsps); return; } LOGD(DEBUG, domid, "issuing %s suspend request via XenBus control node", dsps->type == LIBXL_DOMAIN_TYPE_HVM ? "PVHVM" : "PV"); libxl__domain_pvcontrol_write(gc, XBT_NULL, domid, "suspend"); dsps->pvcontrol.path = libxl__domain_pvcontrol_xspath(gc, domid); if (!dsps->pvcontrol.path) { rc = ERROR_FAIL; goto err; } dsps->pvcontrol.ao = ao; dsps->pvcontrol.what = "guest acknowledgement of suspend request"; dsps->pvcontrol.timeout_ms = 60 * 1000; dsps->pvcontrol.callback = domain_suspend_common_pvcontrol_suspending; libxl__xswait_start(gc, &dsps->pvcontrol); return; err: domain_suspend_common_done(egc, dsps, rc); } static void domain_suspend_common_wait_guest_evtchn(libxl__egc *egc, libxl__ev_evtchn *evev) { libxl__domain_suspend_state *dsps = CONTAINER_OF(evev, *dsps, guest_evtchn); STATE_AO_GC(dsps->ao); /* If we should be done waiting, suspend_common_wait_guest_check * will end up calling domain_suspend_common_guest_suspended or * domain_suspend_common_done, both of which cancel the evtchn * wait as needed. So re-enable it now. */ libxl__ev_evtchn_wait(gc, &dsps->guest_evtchn); suspend_common_wait_guest_check(egc, dsps); } static void domain_suspend_common_pvcontrol_suspending(libxl__egc *egc, libxl__xswait_state *xswa, int rc, const char *state) { libxl__domain_suspend_state *dsps = CONTAINER_OF(xswa, *dsps, pvcontrol); STATE_AO_GC(dsps->ao); xs_transaction_t t = 0; if (!rc && !domain_suspend_pvcontrol_acked(state)) /* keep waiting */ return; libxl__xswait_stop(gc, &dsps->pvcontrol); if (rc == ERROR_TIMEDOUT) { /* * Guest appears to not be responding. Cancel the suspend * request. * * We re-read the suspend node and clear it within a * transaction in order to handle the case where we race * against the guest catching up and acknowledging the request * at the last minute. */ for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto err; rc = libxl__xs_read_checked(gc, t, xswa->path, &state); if (rc) goto err; if (domain_suspend_pvcontrol_acked(state)) /* last minute ack */ break; rc = libxl__xs_write_checked(gc, t, xswa->path, ""); if (rc) goto err; rc = libxl__xs_transaction_commit(gc, &t); if (!rc) { LOGD(ERROR, dsps->domid, "guest didn't acknowledge suspend, cancelling request"); goto err; } if (rc<0) goto err; } } else if (rc) { /* some error in xswait's read of xenstore, already logged */ goto err; } assert(domain_suspend_pvcontrol_acked(state)); LOGD(DEBUG, dsps->domid, "guest acknowledged suspend request"); libxl__xs_transaction_abort(gc, &t); dsps->guest_responded = 1; domain_suspend_common_wait_guest(egc,dsps); return; err: libxl__xs_transaction_abort(gc, &t); domain_suspend_common_done(egc, dsps, rc); return; } static void domain_suspend_common_wait_guest(libxl__egc *egc, libxl__domain_suspend_state *dsps) { STATE_AO_GC(dsps->ao); int rc; LOGD(DEBUG, dsps->domid, "wait for the guest to suspend"); rc = libxl__ev_xswatch_register(gc, &dsps->guest_watch, suspend_common_wait_guest_watch, "@releaseDomain"); if (rc) goto err; rc = libxl__ev_time_register_rel(ao, &dsps->guest_timeout, suspend_common_wait_guest_timeout, 60*1000); if (rc) goto err; return; err: domain_suspend_common_done(egc, dsps, rc); } static void suspend_common_wait_guest_watch(libxl__egc *egc, libxl__ev_xswatch *xsw, const char *watch_path, const char *event_path) { libxl__domain_suspend_state *dsps = CONTAINER_OF(xsw, *dsps, guest_watch); suspend_common_wait_guest_check(egc, dsps); } static void suspend_common_wait_guest_check(libxl__egc *egc, libxl__domain_suspend_state *dsps) { STATE_AO_GC(dsps->ao); xc_domaininfo_t info; int ret; int shutdown_reason; /* Convenience aliases */ const uint32_t domid = dsps->domid; ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info); if (ret < 0) { LOGED(ERROR, domid, "unable to check for status of guest"); goto err; } if (!(ret == 1 && info.domain == domid)) { LOGED(ERROR, domid, "guest we were suspending has been destroyed"); goto err; } if (!(info.flags & XEN_DOMINF_shutdown)) /* keep waiting */ return; shutdown_reason = (info.flags >> XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask; if (shutdown_reason != SHUTDOWN_suspend) { LOGD(DEBUG, domid, "guest we were suspending has shut down" " with unexpected reason code %d", shutdown_reason); goto err; } LOGD(DEBUG, domid, "guest has suspended"); domain_suspend_common_guest_suspended(egc, dsps); return; err: domain_suspend_common_done(egc, dsps, ERROR_FAIL); } static void suspend_common_wait_guest_timeout(libxl__egc *egc, libxl__ev_time *ev, const struct timeval *requested_abs, int rc) { libxl__domain_suspend_state *dsps = CONTAINER_OF(ev, *dsps, guest_timeout); STATE_AO_GC(dsps->ao); if (rc == ERROR_TIMEDOUT) { LOGD(ERROR, dsps->domid, "guest did not suspend, timed out"); rc = ERROR_GUEST_TIMEDOUT; } domain_suspend_common_done(egc, dsps, rc); } static void domain_suspend_common_guest_suspended(libxl__egc *egc, libxl__domain_suspend_state *dsps) { STATE_AO_GC(dsps->ao); int rc; libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); libxl__ev_time_deregister(gc, &dsps->guest_timeout); if (dsps->type == LIBXL_DOMAIN_TYPE_HVM) { rc = libxl__domain_suspend_device_model(gc, dsps); if (rc) { LOGD(ERROR, dsps->domid, "libxl__domain_suspend_device_model failed ret=%d", rc); domain_suspend_common_done(egc, dsps, rc); return; } } domain_suspend_common_done(egc, dsps, 0); } static void domain_suspend_common_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int rc) { EGC_GC; assert(!libxl__xswait_inuse(&dsps->pvcontrol)); libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); libxl__ev_time_deregister(gc, &dsps->guest_timeout); dsps->callback_common_done(egc, dsps, rc); } void libxl__domain_suspend_callback(void *data) { libxl__save_helper_state *shs = data; libxl__egc *egc = shs->egc; libxl__domain_save_state *dss = shs->caller_state; libxl__domain_suspend_state *dsps = &dss->dsps; dsps->callback_common_done = domain_suspend_callback_common_done; domain_suspend_callback_common(egc, dsps); } static void domain_suspend_callback_common_done(libxl__egc *egc, libxl__domain_suspend_state *dsps, int rc) { libxl__domain_save_state *dss = CONTAINER_OF(dsps, *dss, dsps); dss->rc = rc; libxl__xc_domain_saverestore_async_callback_done(egc, &dss->sws.shs, !rc); } /*======================= Domain resume ========================*/ int libxl__domain_resume_device_model(libxl__gc *gc, uint32_t domid) { const char *path, *state; switch (libxl__device_model_version_running(gc, domid)) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: { uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid); path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/state"); state = libxl__xs_read(gc, XBT_NULL, path); if (state != NULL && !strcmp(state, "paused")) { libxl__qemu_traditional_cmd(gc, domid, "continue"); libxl__wait_for_device_model_deprecated(gc, domid, "running", NULL, NULL, NULL); } break; } case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: if (libxl__qmp_resume(gc, domid)) return ERROR_FAIL; break; default: return ERROR_INVAL; } return 0; } int libxl__domain_resume(libxl__gc *gc, uint32_t domid, int suspend_cancel) { int rc = 0; libxl_domain_type type = libxl__domain_type(gc, domid); if (type == LIBXL_DOMAIN_TYPE_INVALID) { rc = ERROR_FAIL; goto out; } if (type == LIBXL_DOMAIN_TYPE_HVM) { rc = libxl__domain_resume_device_model(gc, domid); if (rc) { LOGD(ERROR, domid, "failed to resume device model:%d", rc); goto out; } } if (xc_domain_resume(CTX->xch, domid, suspend_cancel)) { LOGED(ERROR, domid, "xc_domain_resume failed"); rc = ERROR_FAIL; goto out; } if (!xs_resume_domain(CTX->xsh, domid)) { LOGED(ERROR, domid, "xs_resume_domain failed"); rc = ERROR_FAIL; } out: return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_arm_no_acpi.c0000664000175000017500000000210613256712137016533 0ustar smbsmb/* * ARM DomU ACPI generation * * Copyright (C) 2016 Linaro Ltd. * * Author: Shannon Zhao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_arm.h" int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info, struct xc_dom_image *dom) { return ERROR_FAIL; } int libxl__get_acpi_size(libxl__gc *gc, const libxl_domain_build_info *info, uint64_t *out) { return ERROR_FAIL; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/Makefile0000664000175000017500000003014113256712137014366 0ustar smbsmb# # tools/libxl/Makefile # XEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk MAJOR = 4.9 MINOR = 0 XLUMAJOR = 4.9 XLUMINOR = 0 CFLAGS += -Werror -Wno-format-zero-length -Wmissing-declarations \ -Wno-declaration-after-statement -Wformat-nonliteral CFLAGS += -I. -fPIC ifeq ($(CONFIG_Linux),y) LIBUUID_LIBS += -luuid endif LIBXL_LIBS = LIBXL_LIBS = $(LDLIBS_libxentoollog) $(LDLIBS_libxenevtchn) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenstore) $(LDLIBS_libblktapctl) $(PTYFUNCS_LIBS) $(LIBUUID_LIBS) ifeq ($(CONFIG_LIBNL),y) LIBXL_LIBS += $(LIBNL3_LIBS) endif CFLAGS_LIBXL += $(CFLAGS_libxentoollog) CFLAGS_LIBXL += $(CFLAGS_libxenevtchn) CFLAGS_LIBXL += $(CFLAGS_libxenctrl) CFLAGS_LIBXL += $(CFLAGS_libxenguest) CFLAGS_LIBXL += $(CFLAGS_libxenstore) CFLAGS_LIBXL += $(CFLAGS_libblktapctl) ifeq ($(CONFIG_LIBNL),y) CFLAGS_LIBXL += $(LIBNL3_CFLAGS) endif CFLAGS_LIBXL += -Wshadow LIBXL_LIBS-$(CONFIG_ARM) += -lfdt CFLAGS += $(PTHREAD_CFLAGS) LDFLAGS += $(PTHREAD_LDFLAGS) LIBXL_LIBS += $(PTHREAD_LIBS) LIBXL_LIBS += $(LIBXL_LIBS-y) LIBXLU_LIBS = $(LDLIBS_libxenlight) LIBXL_OBJS-y = osdeps.o libxl_paths.o libxl_bootloader.o flexarray.o ifeq ($(LIBXL_BLKTAP),y) LIBXL_OBJS-y += libxl_blktap2.o else LIBXL_OBJS-y += libxl_noblktap2.o endif ifeq ($(CONFIG_LIBNL),y) LIBXL_OBJS-y += libxl_netbuffer.o else LIBXL_OBJS-y += libxl_nonetbuffer.o endif ifeq ($(CONFIG_X86),y) LIBXL_OBJS-y += libxl_convert_callout.o else LIBXL_OBJS-y += libxl_no_convert_callout.o endif LIBXL_OBJS-y += libxl_remus.o libxl_checkpoint_device.o libxl_remus_disk_drbd.o ifeq ($(CONFIG_LIBNL),y) LIBXL_OBJS-y += libxl_colo_restore.o libxl_colo_save.o LIBXL_OBJS-y += libxl_colo_qdisk.o LIBXL_OBJS-y += libxl_colo_proxy.o LIBXL_OBJS-y += libxl_colo_nic.o else LIBXL_OBJS-y += libxl_no_colo.o endif ACPI_PATH = $(XEN_ROOT)/tools/libacpi DSDT_FILES-$(CONFIG_X86) = dsdt_pvh.c ACPI_OBJS = $(patsubst %.c,%.o,$(DSDT_FILES-y)) build.o static_tables.o $(DSDT_FILES-y): acpi $(ACPI_OBJS): CFLAGS += -I. -DLIBACPI_STDUTILS=\"$(CURDIR)/libxl_x86_acpi.h\" vpath build.c $(ACPI_PATH)/ vpath static_tables.c $(ACPI_PATH)/ LIBXL_OBJS-$(CONFIG_X86) += $(ACPI_OBJS) .PHONY: acpi acpi: $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)" LIBXL_OBJS-$(CONFIG_X86) += libxl_cpuid.o libxl_x86.o libxl_psr.o libxl_x86_acpi.o LIBXL_OBJS-$(CONFIG_ARM) += libxl_nocpuid.o libxl_arm.o libxl_libfdt_compat.o ifeq ($(CONFIG_ARM_64),y) DSDT_FILES-y = dsdt_anycpu_arm.c LIBXL_OBJS-y += libxl_arm_acpi.o $(patsubst %.c,%.o,$(DSDT_FILES-y)) dsdt_anycpu_arm.c: $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) DSDT_FILES="$(DSDT_FILES-y)" else LIBXL_OBJS-$(CONFIG_ARM) += libxl_arm_no_acpi.o endif ifeq ($(CONFIG_NetBSD),y) LIBXL_OBJS-y += libxl_netbsd.o else ifeq ($(CONFIG_Linux),y) LIBXL_OBJS-y += libxl_linux.o else ifeq ($(CONFIG_FreeBSD),y) LIBXL_OBJS-y += libxl_freebsd.o else $(error Your Operating System is not supported by libxenlight, \ please check libxl_linux.c and libxl_netbsd.c to see how to get it ported) endif endif endif ifeq ($(FLEX),) %.c %.h:: %.l $(warning Flex is needed to rebuild some libxl parsers and \ scanners, please install it and rerun configure) endif ifeq ($(BISON),) %.c %.h:: %.y $(warning Bison is needed to rebuild some libxl parsers and \ scanners, please install it an rerun configure) endif LIBXL_LIBS += -lyajl LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ libxl_dom.o libxl_exec.o libxl_xshelp.o libxl_device.o \ libxl_internal.o libxl_utils.o libxl_uuid.o \ libxl_json.o libxl_aoutils.o libxl_numa.o libxl_vnuma.o \ libxl_stream_read.o libxl_stream_write.o \ libxl_save_callout.o _libxl_save_msgs_callout.o \ libxl_qmp.o libxl_event.o libxl_fork.o \ libxl_dom_suspend.o libxl_dom_save.o libxl_usb.o \ libxl_vtpm.o libxl_nic.o libxl_disk.o libxl_console.o \ libxl_cpupool.o libxl_mem.o libxl_sched.o libxl_tmem.o \ libxl_9pfs.o libxl_domain.o \ $(LIBXL_OBJS-y) LIBXL_OBJS += libxl_genid.o LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o LIBXL_TESTS += timedereg LIBXL_TESTS_PROGS = $(LIBXL_TESTS) fdderegrace LIBXL_TESTS_INSIDE = $(LIBXL_TESTS) fdevent # Each entry FOO in LIBXL_TESTS has two main .c files: # libxl_test_FOO.c "inside libxl" code to support the test case # test_FOO.c "outside libxl" code to exercise the test case # Conventionally there will also be: # libxl_test_FOO.h interface between the "inside" and "outside" parts # The "inside libxl" file is compiled exactly like a piece of libxl, and the # "outside libxl" file is compiled exactly like a piece of application # code. They must share information via explicit libxl entrypoints. # Unlike proper parts of libxl, it is permissible for libxl_test_FOO.c # to use private global variables for its state. Note that all the # "inside" parts are compiled into a single test library, so their # symbol names must be unique. # # To run these tests, either use LD_PRELOAD to get libxenlight_test.so # loaded, or rename it to libxenlight.so so it is the target of the # appropriate symlinks. LIBXL_TEST_OBJS += $(foreach t, $(LIBXL_TESTS_INSIDE),libxl_test_$t.o) TEST_PROG_OBJS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t.o) test_common.o TEST_PROGS += $(foreach t, $(LIBXL_TESTS_PROGS),test_$t) $(LIBXL_OBJS) $(LIBXL_TEST_OBJS): CFLAGS += $(CFLAGS_LIBXL) -include $(XEN_ROOT)/tools/config.h AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h _libxl_list.h _paths.h \ libxlu_disk_l.h _libxl_save_msgs_callout.h _libxl_save_msgs_helper.h AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c AUTOSRCS += _libxl_save_msgs_callout.c _libxl_save_msgs_helper.c LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o \ libxlu_disk_l.o libxlu_disk.o libxlu_vif.o libxlu_pci.o $(LIBXLU_OBJS): CFLAGS += $(CFLAGS_libxenctrl) # For xentoollog.h $(TEST_PROG_OBJS) _libxl.api-for-check: CFLAGS += $(CFLAGS_libxentoollog) CLIENTS = testidl libxl-save-helper libxl_dom.o: CFLAGS += -I$(XEN_ROOT)/tools # include libacpi/x86.h libxl_x86_acpi.o: CFLAGS += -I$(XEN_ROOT)/tools SAVE_HELPER_OBJS = libxl_save_helper.o _libxl_save_msgs_helper.o $(SAVE_HELPER_OBJS): CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenevtchn) PKG_CONFIG = xenlight.pc xlutil.pc PKG_CONFIG_VERSION := $(MAJOR).$(MINOR) ifneq ($(CONFIG_LIBXC_MINIOS),y) PKG_CONFIG_INST := $(PKG_CONFIG) xenlight.pc: PKG_CONFIG_VERSION = $(MAJOR).$(MINOR) xlutil.pc: PKG_CONFIG_VERSION = $(XLUMAJOR).$(XLUMINOR) $(PKG_CONFIG_INST): PKG_CONFIG_PREFIX = $(prefix) $(PKG_CONFIG_INST): PKG_CONFIG_INCDIR = $(includedir) $(PKG_CONFIG_INST): PKG_CONFIG_LIBDIR = $(libdir) endif PKG_CONFIG_LOCAL := $(foreach pc,$(PKG_CONFIG),$(PKG_CONFIG_DIR)/$(pc)) $(PKG_CONFIG_DIR)/xenlight.pc: PKG_CONFIG_VERSION = $(MAJOR).$(MINOR) $(PKG_CONFIG_DIR)/xlutil.pc: PKG_CONFIG_VERSION = $(XLUMAJOR).$(XLUMINOR) $(PKG_CONFIG_LOCAL): PKG_CONFIG_PREFIX = $(XEN_ROOT) $(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(CURDIR) $(PKG_CONFIG_LOCAL): PKG_CONFIG_LIBDIR = $(CURDIR) $(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude) testidl.o: CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_libxenlight) testidl.c: libxl_types.idl gentest.py libxl.h $(AUTOINCS) $(PYTHON) gentest.py libxl_types.idl testidl.c.new mv testidl.c.new testidl.c .PHONY: all all: $(CLIENTS) $(TEST_PROGS) $(PKG_CONFIG) $(PKG_CONFIG_LOCAL) \ libxenlight.so libxenlight.a libxlutil.so libxlutil.a \ $(AUTOSRCS) $(AUTOINCS) $(LIBXL_OBJS) $(LIBXLU_OBJS) $(SAVE_HELPER_OBJS) \ $(LIBXL_TEST_OBJS) $(TEST_PROG_OBJS): \ $(AUTOINCS) libxl.api-ok %.c %.h:: %.y @rm -f $*.[ch] $(BISON) --output=$*.c $< %.c %.h:: %.l @rm -f $*.[ch] $(FLEX) --header-file=$*.h --outfile=$*.c $< genpath-target = $(call buildmakevars2header,_paths.h) $(eval $(genpath-target)) libxl.api-ok: check-libxl-api-rules _libxl.api-for-check $(PERL) $^ touch $@ _%.api-for-check: %.h $(AUTOINCS) $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_$*.o) -c -E $< $(APPEND_CFLAGS) \ -DLIBXL_EXTERNAL_CALLERS_ONLY=LIBXL_EXTERNAL_CALLERS_ONLY \ >$@.new mv -f $@.new $@ _libxl_list.h: $(XEN_INCLUDE)/xen-external/bsd-sys-queue-h-seddery $(XEN_INCLUDE)/xen-external/bsd-sys-queue.h $(PERL) $^ --prefix=libxl >$@.new $(call move-if-changed,$@.new,$@) _libxl_save_msgs_helper.c _libxl_save_msgs_callout.c \ _libxl_save_msgs_helper.h _libxl_save_msgs_callout.h: \ libxl_save_msgs_gen.pl $(PERL) -w $< $@ >$@.new $(call move-if-changed,$@.new,$@) libxl.h: _libxl_types.h _libxl_list.h libxl_json.h: _libxl_types_json.h libxl_internal.h: _libxl_types_internal.h _libxl_types_private.h _libxl_types_internal_private.h _paths.h libxl_internal_json.h: _libxl_types_internal_json.h xl.h: _paths.h $(LIBXL_OBJS) $(LIBXL_TEST_OBJS) $(LIBXLU_OBJS) \ $(TEST_PROG_OBJS) $(SAVE_HELPER_OBJS): libxl.h $(LIBXL_OBJS) $(LIBXL_TEST_OBJS): libxl_internal.h _libxl_type%.h _libxl_type%_json.h _libxl_type%_private.h _libxl_type%.c: libxl_type%.idl gentypes.py idl.py $(eval stem = $(notdir $*)) $(PYTHON) gentypes.py libxl_type$(stem).idl __libxl_type$(stem).h __libxl_type$(stem)_private.h \ __libxl_type$(stem)_json.h __libxl_type$(stem).c $(call move-if-changed,__libxl_type$(stem).h,_libxl_type$(stem).h) $(call move-if-changed,__libxl_type$(stem)_private.h,_libxl_type$(stem)_private.h) $(call move-if-changed,__libxl_type$(stem)_json.h,_libxl_type$(stem)_json.h) $(call move-if-changed,__libxl_type$(stem).c,_libxl_type$(stem).c) libxenlight.so: libxenlight.so.$(MAJOR) $(SYMLINK_SHLIB) $< $@ libxenlight.so.$(MAJOR): libxenlight.so.$(MAJOR).$(MINOR) $(SYMLINK_SHLIB) $< $@ libxenlight.so.$(MAJOR).$(MINOR): $(LIBXL_OBJS) $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenlight.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LIBXL_LIBS) $(APPEND_LDFLAGS) libxenlight_test.so: $(LIBXL_OBJS) $(LIBXL_TEST_OBJS) $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenlight.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LIBXL_LIBS) $(APPEND_LDFLAGS) libxenlight.a: $(LIBXL_OBJS) $(AR) rcs libxenlight.a $^ libxlutil.so: libxlutil.so.$(XLUMAJOR) $(SYMLINK_SHLIB) $< $@ libxlutil.so.$(XLUMAJOR): libxlutil.so.$(XLUMAJOR).$(XLUMINOR) $(SYMLINK_SHLIB) $< $@ libxlutil.so.$(XLUMAJOR).$(XLUMINOR): $(LIBXLU_OBJS) libxenlight.so $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxlutil.so.$(XLUMAJOR) $(SHLIB_LDFLAGS) -o $@ $(LIBXLU_OBJS) $(LIBXLU_LIBS) $(APPEND_LDFLAGS) libxlutil.a: $(LIBXLU_OBJS) $(AR) rcs libxlutil.a $^ test_%: test_%.o test_common.o libxlutil.so libxenlight_test.so $(CC) $(LDFLAGS) -o $@ $^ $(filter-out %libxenlight.so, $(LDLIBS_libxenlight)) $(LDLIBS_libxentoollog) -lyajl $(APPEND_LDFLAGS) libxl-save-helper: $(SAVE_HELPER_OBJS) libxenlight.so $(CC) $(LDFLAGS) -o $@ $(SAVE_HELPER_OBJS) $(LDLIBS_libxentoollog) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(APPEND_LDFLAGS) testidl: testidl.o libxlutil.so libxenlight.so $(CC) $(LDFLAGS) -o $@ testidl.o libxlutil.so $(LDLIBS_libxenlight) $(LDLIBS_libxentoollog) $(APPEND_LDFLAGS) .PHONY: install install: all $(INSTALL_DIR) $(DESTDIR)$(libdir) $(INSTALL_DIR) $(DESTDIR)$(includedir) $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN) $(INSTALL_PROG) libxl-save-helper $(DESTDIR)$(LIBEXEC_BIN) $(INSTALL_SHLIB) libxenlight.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir) $(SYMLINK_SHLIB) libxenlight.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libxenlight.so.$(MAJOR) $(SYMLINK_SHLIB) libxenlight.so.$(MAJOR) $(DESTDIR)$(libdir)/libxenlight.so $(INSTALL_DATA) libxenlight.a $(DESTDIR)$(libdir) $(INSTALL_SHLIB) libxlutil.so.$(XLUMAJOR).$(XLUMINOR) $(DESTDIR)$(libdir) $(SYMLINK_SHLIB) libxlutil.so.$(XLUMAJOR).$(XLUMINOR) $(DESTDIR)$(libdir)/libxlutil.so.$(XLUMAJOR) $(SYMLINK_SHLIB) libxlutil.so.$(XLUMAJOR) $(DESTDIR)$(libdir)/libxlutil.so $(INSTALL_DATA) libxlutil.a $(DESTDIR)$(libdir) $(INSTALL_DATA) libxl.h libxl_event.h libxl_json.h _libxl_types.h _libxl_types_json.h _libxl_list.h libxl_utils.h libxl_uuid.h libxlutil.h $(DESTDIR)$(includedir) $(INSTALL_DATA) xenlight.pc $(DESTDIR)$(PKG_INSTALLDIR) $(INSTALL_DATA) xlutil.pc $(DESTDIR)$(PKG_INSTALLDIR) .PHONY: clean clean: $(RM) -f _*.h *.o *.so* *.a $(CLIENTS) $(DEPS) $(RM) -f _*.c *.pyc _paths.*.tmp _*.api-for-check $(RM) -f testidl.c.new testidl.c *.api-ok $(RM) -f xenlight.pc $(RM) -f xlutil.pc $(MAKE) -C $(ACPI_PATH) ACPI_BUILD_DIR=$(CURDIR) clean distclean: clean realclean: distclean $(RM) -f $(AUTOSRCS) $(AUTOINCS) -include $(DEPS) xen-4.9.2/tools/libxl/libxl_dm.c0000664000175000017500000027210113256712137014670 0ustar smbsmb/* * Copyright (C) 2010 Citrix Ltd. * Author Vincent Hanquez * Author Stefano Stabellini * Author Gianni Tedesco * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include #include #include #include static const char *libxl_tapif_script(libxl__gc *gc) { #if defined(__linux__) || defined(__FreeBSD__) return libxl__strdup(gc, "no"); #else return GCSPRINTF("%s/qemu-ifup", libxl__xen_script_dir_path()); #endif } const char *libxl__device_model_savefile(libxl__gc *gc, uint32_t domid) { return GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", domid); } static const char *qemu_xen_path(libxl__gc *gc) { return QEMU_XEN_PATH; } static int libxl__create_qemu_logfile(libxl__gc *gc, char *name) { char *logfile; int rc, logfile_w; rc = libxl_create_logfile(CTX, name, &logfile); if (rc) return rc; logfile_w = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644); if (logfile_w < 0) { LOGE(ERROR, "unable to open Qemu logfile: %s", logfile); free(logfile); return ERROR_FAIL; } free(logfile); return logfile_w; } const char *libxl__domain_device_model(libxl__gc *gc, const libxl_domain_build_info *info) { const char *dm; if (libxl_defbool_val(info->device_model_stubdomain)) return NULL; if (info->device_model) { dm = libxl__strdup(gc, info->device_model); } else { switch (info->device_model_version) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: dm = libxl__abs_path(gc, "qemu-dm", libxl__private_bindir_path()); break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: dm = qemu_xen_path(gc); break; default: LOG(ERROR, "invalid device model version %d", info->device_model_version); dm = NULL; break; } } return dm; } static int libxl__xc_device_get_rdm(libxl__gc *gc, uint32_t flags, uint16_t seg, uint8_t bus, uint8_t devfn, unsigned int *nr_entries, struct xen_reserved_device_memory **xrdm) { int rc = 0, r; /* * We really can't presume how many entries we can get in advance. */ *nr_entries = 0; r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn, NULL, nr_entries); assert(r <= 0); /* "0" means we have no any rdm entry. */ if (!r) goto out; if (errno != ENOBUFS) { rc = ERROR_FAIL; goto out; } GCNEW_ARRAY(*xrdm, *nr_entries); r = xc_reserved_device_memory_map(CTX->xch, flags, seg, bus, devfn, *xrdm, nr_entries); if (r) rc = ERROR_FAIL; out: if (rc) { *nr_entries = 0; *xrdm = NULL; LOG(ERROR, "Could not get reserved device memory maps."); } return rc; } /* * Check whether there exists rdm hole in the specified memory range. * Returns true if exists, else returns false. */ static bool overlaps_rdm(uint64_t start, uint64_t memsize, uint64_t rdm_start, uint64_t rdm_size) { return (start + memsize > rdm_start) && (start < rdm_start + rdm_size); } static void add_rdm_entry(libxl__gc *gc, libxl_domain_config *d_config, uint64_t rdm_start, uint64_t rdm_size, int rdm_policy) { d_config->rdms = libxl__realloc(NOGC, d_config->rdms, (d_config->num_rdms+1) * sizeof(libxl_device_rdm)); d_config->rdms[d_config->num_rdms].start = rdm_start; d_config->rdms[d_config->num_rdms].size = rdm_size; d_config->rdms[d_config->num_rdms].policy = rdm_policy; d_config->num_rdms++; } /* * Check reported RDM regions and handle potential gfn conflicts according * to user preferred policy. * * RDM can reside in address space beyond 4G theoretically, but we never * see this in real world. So in order to avoid breaking highmem layout * we don't solve highmem conflict. Note this means highmem rmrr could * still be supported if no conflict. * * But in the case of lowmem, RDM probably scatter the whole RAM space. * Especially multiple RDM entries would worsen this to lead a complicated * memory layout. And then its hard to extend hvm_info_table{} to work * hvmloader out. So here we're trying to figure out a simple solution to * avoid breaking existing layout. So when a conflict occurs, * * #1. Above a predefined boundary (default 2G) * - Move lowmem_end below reserved region to solve conflict; * * #2. Below a predefined boundary (default 2G) * - Check strict/relaxed policy. * "strict" policy leads to fail libxl. * "relaxed" policy issue a warning message and also mask this entry * INVALID to indicate we shouldn't expose this entry to hvmloader. * Note when both policies are specified on a given region, the per-device * policy should override the global policy. */ int libxl__domain_device_construct_rdm(libxl__gc *gc, libxl_domain_config *d_config, uint64_t rdm_mem_boundary, struct xc_dom_image *dom) { int i, j, conflict, rc; struct xen_reserved_device_memory *xrdm = NULL; uint32_t strategy = d_config->b_info.u.hvm.rdm.strategy; uint16_t seg; uint8_t bus, devfn; uint64_t rdm_start, rdm_size; uint64_t highmem_end = dom->highmem_end ? dom->highmem_end : (1ull<<32); /* * We just want to construct RDM once since RDM is specific to the * given platform, so this shouldn't change again. */ if (d_config->num_rdms) return 0; /* Might not expose rdm. */ if (strategy == LIBXL_RDM_RESERVE_STRATEGY_IGNORE && !d_config->num_pcidevs) return 0; /* Query all RDM entries in this platform */ if (strategy == LIBXL_RDM_RESERVE_STRATEGY_HOST) { unsigned int nr_entries; /* Collect all rdm info if exist. */ rc = libxl__xc_device_get_rdm(gc, XENMEM_RDM_ALL, 0, 0, 0, &nr_entries, &xrdm); if (rc) goto out; if (!nr_entries) return 0; assert(xrdm); for (i = 0; i < nr_entries; i++) { add_rdm_entry(gc, d_config, pfn_to_paddr(xrdm[i].start_pfn), pfn_to_paddr(xrdm[i].nr_pages), d_config->b_info.u.hvm.rdm.policy); } } /* Query RDM entries per-device */ for (i = 0; i < d_config->num_pcidevs; i++) { unsigned int nr_entries; bool new = true; seg = d_config->pcidevs[i].domain; bus = d_config->pcidevs[i].bus; devfn = PCI_DEVFN(d_config->pcidevs[i].dev, d_config->pcidevs[i].func); nr_entries = 0; rc = libxl__xc_device_get_rdm(gc, 0, seg, bus, devfn, &nr_entries, &xrdm); if (rc) goto out; /* No RDM to associated with this device. */ if (!nr_entries) continue; assert(xrdm); /* * Need to check whether this entry is already saved in the array. * This could come from two cases: * * - user may configure to get all RDMs in this platform, which * is already queried before this point * - or two assigned devices may share one RDM entry * * Different policies may be configured on the same RDM due to * above two cases. But we don't allow to assign such a group * devies right now so it doesn't come true in our case. */ for (j = 0; j < d_config->num_rdms; j++) { if (d_config->rdms[j].start == pfn_to_paddr(xrdm[0].start_pfn)) { /* * So the per-device policy always override the global * policy in this case. */ d_config->rdms[j].policy = d_config->pcidevs[i].rdm_policy; new = false; break; } } if (new) { add_rdm_entry(gc, d_config, pfn_to_paddr(xrdm[0].start_pfn), pfn_to_paddr(xrdm[0].nr_pages), d_config->pcidevs[i].rdm_policy); } } /* * Next step is to check and avoid potential conflict between RDM * entries and guest RAM. To avoid intrusive impact to existing * memory layout {lowmem, mmio, highmem} which is passed around * various function blocks, below conflicts are not handled which * are rare and handling them would lead to a more scattered * layout: * - RDM in highmem area (>4G) * - RDM lower than a defined memory boundary (e.g. 2G) * Otherwise for conflicts between boundary and 4G, we'll simply * move lowmem end below reserved region to solve conflict. * * If a conflict is detected on a given RDM entry, an error will * be returned if 'strict' policy is specified. Instead, if * 'relaxed' policy specified, this conflict is treated just as a * warning, but we mark this RDM entry as INVALID to indicate that * this entry shouldn't be exposed to hvmloader. * * Firstly we should check the case of rdm < 4G because we may * need to expand highmem_end. */ for (i = 0; i < d_config->num_rdms; i++) { rdm_start = d_config->rdms[i].start; rdm_size = d_config->rdms[i].size; conflict = overlaps_rdm(0, dom->lowmem_end, rdm_start, rdm_size); if (!conflict) continue; /* Just check if RDM > our memory boundary. */ if (rdm_start > rdm_mem_boundary) { /* * We will move downwards lowmem_end so we have to expand * highmem_end. */ highmem_end += (dom->lowmem_end - rdm_start); /* Now move downwards lowmem_end. */ dom->lowmem_end = rdm_start; } } /* Sync highmem_end. */ dom->highmem_end = highmem_end; /* * Finally we can take same policy to check lowmem(< 2G) and * highmem adjusted above. */ for (i = 0; i < d_config->num_rdms; i++) { rdm_start = d_config->rdms[i].start; rdm_size = d_config->rdms[i].size; /* Does this entry conflict with lowmem? */ conflict = overlaps_rdm(0, dom->lowmem_end, rdm_start, rdm_size); /* Does this entry conflict with highmem? */ conflict |= overlaps_rdm((1ULL<<32), dom->highmem_end - (1ULL<<32), rdm_start, rdm_size); if (!conflict) continue; if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_STRICT) { LOG(ERROR, "RDM conflict at 0x%"PRIx64".\n", d_config->rdms[i].start); goto out; } else { LOG(WARN, "Ignoring RDM conflict at 0x%"PRIx64".\n", d_config->rdms[i].start); /* * Then mask this INVALID to indicate we shouldn't expose this * to hvmloader. */ d_config->rdms[i].policy = LIBXL_RDM_RESERVE_POLICY_INVALID; } } return 0; out: return ERROR_FAIL; } /* XSA-180 / CVE-2014-3672 * * The QEMU shipped with Xen has a bodge. It checks for * XEN_QEMU_CONSOLE_LIMIT to see how much data QEMU is allowed * to write to stderr. We set that to 1MB if it is not set by * system administrator. */ static void libxl__set_qemu_env_for_xsa_180(libxl__gc *gc, flexarray_t *dm_envs) { if (getenv("XEN_QEMU_CONSOLE_LIMIT")) return; flexarray_append_pair(dm_envs, "XEN_QEMU_CONSOLE_LIMIT", "1048576"); } const libxl_vnc_info *libxl__dm_vnc(const libxl_domain_config *guest_config) { const libxl_vnc_info *vnc = NULL; if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) { vnc = &guest_config->b_info.u.hvm.vnc; } else if (guest_config->num_vfbs > 0) { vnc = &guest_config->vfbs[0].vnc; } return vnc && libxl_defbool_val(vnc->enable) ? vnc : NULL; } static const libxl_sdl_info *dm_sdl(const libxl_domain_config *guest_config) { const libxl_sdl_info *sdl = NULL; if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) { sdl = &guest_config->b_info.u.hvm.sdl; } else if (guest_config->num_vfbs > 0) { sdl = &guest_config->vfbs[0].sdl; } return sdl && libxl_defbool_val(sdl->enable) ? sdl : NULL; } static const char *dm_keymap(const libxl_domain_config *guest_config) { if (guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) { return guest_config->b_info.u.hvm.keymap; } else if (guest_config->num_vfbs > 0) { return guest_config->vfbs[0].keymap; } else return NULL; } static int libxl__build_device_model_args_old(libxl__gc *gc, const char *dm, int domid, const libxl_domain_config *guest_config, char ***args, char ***envs, const libxl__domain_build_state *state) { const libxl_domain_create_info *c_info = &guest_config->c_info; const libxl_domain_build_info *b_info = &guest_config->b_info; const libxl_device_nic *nics = guest_config->nics; const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config); const libxl_sdl_info *sdl = dm_sdl(guest_config); const int num_nics = guest_config->num_nics; const char *keymap = dm_keymap(guest_config); int i; flexarray_t *dm_args, *dm_envs; dm_args = flexarray_make(gc, 16, 1); dm_envs = flexarray_make(gc, 16, 1); libxl__set_qemu_env_for_xsa_180(gc, dm_envs); flexarray_vappend(dm_args, dm, "-d", GCSPRINTF("%d", domid), NULL); if (c_info->name) flexarray_vappend(dm_args, "-domain-name", c_info->name, NULL); if (vnc) { char *vncarg = NULL; flexarray_append(dm_args, "-vnc"); /* * If vnc->listen is present and contains a :, and * - vnc->display is 0, use vnc->listen * - vnc->display is non-zero, be confused * If vnc->listen is present but doesn't, use vnc->listen:vnc->display. * If vnc->listen is not present, use 127.0.0.1:vnc->display * (Remembering that vnc->display already defaults to 0.) */ if (vnc->listen) { if (strchr(vnc->listen, ':') != NULL) { if (vnc->display) { LOGD(ERROR, domid, "vncdisplay set, vnclisten contains display"); return ERROR_INVAL; } vncarg = vnc->listen; } else { vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display); } } else vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display); if (vnc->passwd && vnc->passwd[0]) { vncarg = GCSPRINTF("%s,password", vncarg); } flexarray_append(dm_args, vncarg); if (libxl_defbool_val(vnc->findunused)) { flexarray_append(dm_args, "-vncunused"); } } else /* * VNC is not enabled by default by qemu-xen-traditional, * however passing -vnc none causes SDL to not be * (unexpectedly) enabled by default. This is overridden by * explicitly passing -sdl below as required. */ flexarray_append_pair(dm_args, "-vnc", "none"); if (sdl) { flexarray_append(dm_args, "-sdl"); if (!libxl_defbool_val(sdl->opengl)) { flexarray_append(dm_args, "-disable-opengl"); } if (sdl->display) flexarray_append_pair(dm_envs, "DISPLAY", sdl->display); if (sdl->xauthority) flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority); } if (keymap) { flexarray_vappend(dm_args, "-k", keymap, NULL); } if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) { int ioemu_nics = 0; int nr_set_cpus = 0; char *s; if (b_info->kernel) { LOGD(ERROR, domid, "HVM direct kernel boot is not supported by " "qemu-xen-traditional"); return ERROR_INVAL; } if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) { if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list ) { LOGD(ERROR, domid, "Both serial and serial_list set"); return ERROR_INVAL; } if (b_info->u.hvm.serial) { flexarray_vappend(dm_args, "-serial", b_info->u.hvm.serial, NULL); } else if (b_info->u.hvm.serial_list) { char **p; for (p = b_info->u.hvm.serial_list; *p; p++) { flexarray_vappend(dm_args, "-serial", *p, NULL); } } } if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) { flexarray_append(dm_args, "-nographic"); } if (b_info->video_memkb) { flexarray_vappend(dm_args, "-videoram", GCSPRINTF("%d", libxl__sizekb_to_mb(b_info->video_memkb)), NULL); } switch (b_info->u.hvm.vga.kind) { case LIBXL_VGA_INTERFACE_TYPE_STD: flexarray_append(dm_args, "-std-vga"); break; case LIBXL_VGA_INTERFACE_TYPE_CIRRUS: break; case LIBXL_VGA_INTERFACE_TYPE_NONE: flexarray_append_pair(dm_args, "-vga", "none"); break; case LIBXL_VGA_INTERFACE_TYPE_QXL: break; default: LOGD(ERROR, domid, "Invalid emulated video card specified"); return ERROR_INVAL; } if (b_info->u.hvm.boot) { flexarray_vappend(dm_args, "-boot", b_info->u.hvm.boot, NULL); } if (libxl_defbool_val(b_info->u.hvm.usb) || b_info->u.hvm.usbdevice || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) { if (b_info->u.hvm.usbdevice && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) { LOGD(ERROR, domid, "Both usbdevice and usbdevice_list set"); return ERROR_INVAL; } flexarray_append(dm_args, "-usb"); if (b_info->u.hvm.usbdevice) { flexarray_vappend(dm_args, "-usbdevice", b_info->u.hvm.usbdevice, NULL); } else if (b_info->u.hvm.usbdevice_list) { char **p; for (p = b_info->u.hvm.usbdevice_list; *p; p++) { flexarray_vappend(dm_args, "-usbdevice", *p, NULL); } } } if (b_info->u.hvm.soundhw) { flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL); } if (libxl__acpi_defbool_val(b_info)) { flexarray_append(dm_args, "-acpi"); } if (b_info->max_vcpus > 1) { flexarray_vappend(dm_args, "-vcpus", GCSPRINTF("%d", b_info->max_vcpus), NULL); } nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus); s = libxl_bitmap_to_hex_string(CTX, &b_info->avail_vcpus); flexarray_vappend(dm_args, "-vcpu_avail", GCSPRINTF("%s", s), NULL); free(s); for (i = 0; i < num_nics; i++) { if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) { char *smac = GCSPRINTF( LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nics[i].mac)); const char *ifname = libxl__device_nic_devname(gc, domid, nics[i].devid, LIBXL_NIC_TYPE_VIF_IOEMU); flexarray_vappend(dm_args, "-net", GCSPRINTF( "nic,vlan=%d,macaddr=%s,model=%s", nics[i].devid, smac, nics[i].model), "-net", GCSPRINTF( "tap,vlan=%d,ifname=%s,bridge=%s," "script=%s,downscript=%s", nics[i].devid, ifname, nics[i].bridge, libxl_tapif_script(gc), libxl_tapif_script(gc)), NULL); ioemu_nics++; } } /* If we have no emulated nics, tell qemu not to create any */ if ( ioemu_nics == 0 ) { flexarray_vappend(dm_args, "-net", "none", NULL); } if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) { switch (b_info->u.hvm.gfx_passthru_kind) { case LIBXL_GFX_PASSTHRU_KIND_DEFAULT: case LIBXL_GFX_PASSTHRU_KIND_IGD: flexarray_append(dm_args, "-gfx_passthru"); break; default: LOGD(ERROR, domid, "unsupported gfx_passthru_kind."); return ERROR_INVAL; } } } else { if (!sdl && !vnc) flexarray_append(dm_args, "-nographic"); } if (state->saved_state) { flexarray_vappend(dm_args, "-loadvm", state->saved_state, NULL); } for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++) flexarray_append(dm_args, b_info->extra[i]); flexarray_append(dm_args, "-M"); switch (b_info->type) { case LIBXL_DOMAIN_TYPE_PV: flexarray_append(dm_args, "xenpv"); for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++) flexarray_append(dm_args, b_info->extra_pv[i]); break; case LIBXL_DOMAIN_TYPE_HVM: flexarray_append(dm_args, "xenfv"); for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++) flexarray_append(dm_args, b_info->extra_hvm[i]); break; default: abort(); } flexarray_append(dm_args, NULL); *args = (char **) flexarray_contents(dm_args); flexarray_append(dm_envs, NULL); if (envs) *envs = (char **) flexarray_contents(dm_envs); return 0; } static const char *qemu_disk_format_string(libxl_disk_format format) { switch (format) { case LIBXL_DISK_FORMAT_QCOW: return "qcow"; case LIBXL_DISK_FORMAT_QCOW2: return "qcow2"; case LIBXL_DISK_FORMAT_VHD: return "vpc"; case LIBXL_DISK_FORMAT_RAW: return "raw"; case LIBXL_DISK_FORMAT_EMPTY: return NULL; case LIBXL_DISK_FORMAT_QED: return "qed"; default: return NULL; } } static char *dm_spice_options(libxl__gc *gc, const libxl_spice_info *spice) { char *opt; if (!spice->port && !spice->tls_port) { LOG(ERROR, "at least one of the spiceport or tls_port must be provided"); return NULL; } if (!libxl_defbool_val(spice->disable_ticketing)) { if (!spice->passwd) { LOG(ERROR, "spice ticketing is enabled but missing password"); return NULL; } else if (!spice->passwd[0]) { LOG(ERROR, "spice password can't be empty"); return NULL; } } opt = GCSPRINTF("port=%d,tls-port=%d", spice->port, spice->tls_port); if (spice->host) opt = GCSPRINTF("%s,addr=%s", opt, spice->host); if (libxl_defbool_val(spice->disable_ticketing)) opt = GCSPRINTF("%s,disable-ticketing", opt); else opt = GCSPRINTF("%s,password=%s", opt, spice->passwd); opt = GCSPRINTF("%s,agent-mouse=%s", opt, libxl_defbool_val(spice->agent_mouse) ? "on" : "off"); if (!libxl_defbool_val(spice->clipboard_sharing)) opt = GCSPRINTF("%s,disable-copy-paste", opt); if (spice->image_compression) opt = GCSPRINTF("%s,image-compression=%s", opt, spice->image_compression); if (spice->streaming_video) opt = GCSPRINTF("%s,streaming-video=%s", opt, spice->streaming_video); return opt; } static enum libxl_gfx_passthru_kind libxl__detect_gfx_passthru_kind(libxl__gc *gc, const libxl_domain_config *guest_config) { const libxl_domain_build_info *b_info = &guest_config->b_info; if (b_info->u.hvm.gfx_passthru_kind != LIBXL_GFX_PASSTHRU_KIND_DEFAULT) return b_info->u.hvm.gfx_passthru_kind; if (libxl__is_igd_vga_passthru(gc, guest_config)) { return LIBXL_GFX_PASSTHRU_KIND_IGD; } return LIBXL_GFX_PASSTHRU_KIND_DEFAULT; } /* return 1 if the user was found, 0 if it was not, -1 on error */ static int libxl__dm_runas_helper(libxl__gc *gc, const char *username) { struct passwd pwd, *user = NULL; char *buf = NULL; long buf_size; int ret; buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); if (buf_size < 0) { buf_size = 2048; LOG(DEBUG, "sysconf(_SC_GETPW_R_SIZE_MAX) failed, setting the initial buffer size to %ld", buf_size); } while (1) { buf = libxl__realloc(gc, buf, buf_size); ret = getpwnam_r(username, &pwd, buf, buf_size, &user); if (ret == ERANGE) { buf_size += 128; continue; } if (ret != 0) return ERROR_FAIL; if (user != NULL) return 1; return 0; } } /* colo mode */ enum { LIBXL__COLO_NONE = 0, LIBXL__COLO_PRIMARY, LIBXL__COLO_SECONDARY, }; static char *qemu_disk_scsi_drive_string(libxl__gc *gc, const char *target_path, int unit, const char *format, const libxl_device_disk *disk, int colo_mode) { char *drive = NULL; const char *exportname = disk->colo_export; const char *active_disk = disk->active_disk; const char *hidden_disk = disk->hidden_disk; switch (colo_mode) { case LIBXL__COLO_NONE: drive = libxl__sprintf (gc, "file=%s,if=scsi,bus=0,unit=%d,format=%s,cache=writeback", target_path, unit, format); break; case LIBXL__COLO_PRIMARY: /* * primary: * -dirve if=scsi,bus=0,unit=x,cache=writeback,driver=quorum,\ * id=exportname,\ * children.0.file.filename=target_path,\ * children.0.driver=format,\ * read-pattern=fifo,\ * vote-threshold=1 */ drive = GCSPRINTF( "if=scsi,bus=0,unit=%d,cache=writeback,driver=quorum," "id=%s," "children.0.file.filename=%s," "children.0.driver=%s," "read-pattern=fifo," "vote-threshold=1", unit, exportname, target_path, format); break; case LIBXL__COLO_SECONDARY: /* * secondary: * -drive if=scsi,bus=0,unit=x,cache=writeback,driver=replication,\ * mode=secondary,\ * file.driver=qcow2,\ * file.file.filename=active_disk,\ * file.backing.driver=qcow2,\ * file.backing.file.filename=hidden_disk,\ * file.backing.backing=exportname, */ drive = GCSPRINTF( "if=scsi,id=top-colo,bus=0,unit=%d,cache=writeback," "driver=replication," "mode=secondary," "top-id=top-colo," "file.driver=qcow2," "file.file.filename=%s," "file.backing.driver=qcow2," "file.backing.file.filename=%s," "file.backing.backing=%s", unit, active_disk, hidden_disk, exportname); break; default: abort(); } return drive; } static char *qemu_disk_ide_drive_string(libxl__gc *gc, const char *target_path, int unit, const char *format, const libxl_device_disk *disk, int colo_mode) { char *drive = NULL; const char *exportname = disk->colo_export; const char *active_disk = disk->active_disk; const char *hidden_disk = disk->hidden_disk; switch (colo_mode) { case LIBXL__COLO_NONE: drive = GCSPRINTF ("file=%s,if=ide,index=%d,media=disk,format=%s,cache=writeback", target_path, unit, format); break; case LIBXL__COLO_PRIMARY: /* * primary: * -dirve if=ide,index=x,media=disk,cache=writeback,driver=quorum,\ * id=exportname,\ * children.0.file.filename=target_path,\ * children.0.driver=format,\ * read-pattern=fifo,\ * vote-threshold=1 */ drive = GCSPRINTF( "if=ide,index=%d,media=disk,cache=writeback,driver=quorum," "id=%s," "children.0.file.filename=%s," "children.0.driver=%s," "read-pattern=fifo," "vote-threshold=1", unit, exportname, target_path, format); break; case LIBXL__COLO_SECONDARY: /* * secondary: * -drive if=ide,index=x,media=disk,cache=writeback,driver=replication,\ * mode=secondary,\ * file.driver=qcow2,\ * file.file.filename=active_disk,\ * file.backing.driver=qcow2,\ * file.backing.file.filename=hidden_disk,\ * file.backing.backing=exportname, */ drive = GCSPRINTF( "if=ide,index=%d,id=top-colo,media=disk,cache=writeback," "driver=replication," "mode=secondary," "top-id=top-colo," "file.driver=qcow2," "file.file.filename=%s," "file.backing.driver=qcow2," "file.backing.file.filename=%s," "file.backing.backing=%s", unit, active_disk, hidden_disk, exportname); break; default: abort(); } return drive; } static int libxl__build_device_model_args_new(libxl__gc *gc, const char *dm, int guest_domid, const libxl_domain_config *guest_config, char ***args, char ***envs, const libxl__domain_build_state *state, int *dm_state_fd) { const libxl_domain_create_info *c_info = &guest_config->c_info; const libxl_domain_build_info *b_info = &guest_config->b_info; const libxl_device_disk *disks = guest_config->disks; const libxl_device_nic *nics = guest_config->nics; const int num_disks = guest_config->num_disks; const int num_nics = guest_config->num_nics; const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config); const libxl_sdl_info *sdl = dm_sdl(guest_config); const char *keymap = dm_keymap(guest_config); char *machinearg; flexarray_t *dm_args, *dm_envs; int i, connection, devid, ret; uint64_t ram_size; const char *path, *chardev; char *user = NULL; dm_args = flexarray_make(gc, 16, 1); dm_envs = flexarray_make(gc, 16, 1); libxl__set_qemu_env_for_xsa_180(gc, dm_envs); flexarray_vappend(dm_args, dm, "-xen-domid", GCSPRINTF("%d", guest_domid), NULL); flexarray_append(dm_args, "-chardev"); flexarray_append(dm_args, GCSPRINTF("socket,id=libxl-cmd," "path=%s/qmp-libxl-%d,server,nowait", libxl__run_dir_path(), guest_domid)); flexarray_append(dm_args, "-no-shutdown"); flexarray_append(dm_args, "-mon"); flexarray_append(dm_args, "chardev=libxl-cmd,mode=control"); flexarray_append(dm_args, "-chardev"); flexarray_append(dm_args, GCSPRINTF("socket,id=libxenstat-cmd," "path=%s/qmp-libxenstat-%d,server,nowait", libxl__run_dir_path(), guest_domid)); flexarray_append(dm_args, "-mon"); flexarray_append(dm_args, "chardev=libxenstat-cmd,mode=control"); for (i = 0; i < guest_config->num_channels; i++) { connection = guest_config->channels[i].connection; devid = guest_config->channels[i].devid; switch (connection) { case LIBXL_CHANNEL_CONNECTION_PTY: chardev = GCSPRINTF("pty,id=libxl-channel%d", devid); break; case LIBXL_CHANNEL_CONNECTION_SOCKET: path = guest_config->channels[i].u.socket.path; chardev = GCSPRINTF("socket,id=libxl-channel%d,path=%s," "server,nowait", devid, path); break; default: /* We've forgotten to add the clause */ LOGD(ERROR, guest_domid, "%s: unknown channel connection %d", __func__, connection); return ERROR_INVAL; } flexarray_append(dm_args, "-chardev"); flexarray_append(dm_args, (void*)chardev); } /* * Remove default devices created by qemu. Qemu will create only devices * defined by xen, since the devices not defined by xen are not usable. */ flexarray_append(dm_args, "-nodefaults"); /* * Do not use any of the user-provided config files in sysconfdir, * avoiding unkown and uncontrolled configuration. */ flexarray_append(dm_args, "-no-user-config"); if (b_info->type == LIBXL_DOMAIN_TYPE_PV) { flexarray_append(dm_args, "-xen-attach"); } if (c_info->name) { flexarray_vappend(dm_args, "-name", c_info->name, NULL); } if (vnc) { char *vncarg = NULL; flexarray_append(dm_args, "-vnc"); /* * If vnc->listen is present and contains a :, and * - vnc->display is 0, use vnc->listen * - vnc->display is non-zero, be confused * If vnc->listen is present but doesn't, use vnc->listen:vnc->display. * If vnc->listen is not present, use 127.0.0.1:vnc->display * (Remembering that vnc->display already defaults to 0.) */ if (vnc->listen) { if (strchr(vnc->listen, ':') != NULL) { if (vnc->display) { LOGD(ERROR, guest_domid, "vncdisplay set, vnclisten contains display"); return ERROR_INVAL; } vncarg = vnc->listen; } else { vncarg = GCSPRINTF("%s:%d", vnc->listen, vnc->display); } } else vncarg = GCSPRINTF("127.0.0.1:%d", vnc->display); if (vnc->passwd && vnc->passwd[0]) { vncarg = GCSPRINTF("%s,password", vncarg); } if (libxl_defbool_val(vnc->findunused)) { /* This option asks to QEMU to try this number of port before to * give up. So QEMU will try ports between $display and $display + * 99. This option needs to be the last one of the vnc options. */ vncarg = GCSPRINTF("%s,to=99", vncarg); } flexarray_append(dm_args, vncarg); } else /* * Ensure that by default no vnc server is created. */ flexarray_append_pair(dm_args, "-vnc", "none"); /* * Ensure that by default no display backend is created. Further * options given below might then enable more. */ flexarray_append_pair(dm_args, "-display", "none"); if (sdl) { flexarray_append(dm_args, "-sdl"); if (sdl->display) flexarray_append_pair(dm_envs, "DISPLAY", sdl->display); if (sdl->xauthority) flexarray_append_pair(dm_envs, "XAUTHORITY", sdl->xauthority); } if (keymap) { flexarray_vappend(dm_args, "-k", keymap, NULL); } if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) { int ioemu_nics = 0; if (b_info->kernel) flexarray_vappend(dm_args, "-kernel", b_info->kernel, NULL); if (b_info->ramdisk) flexarray_vappend(dm_args, "-initrd", b_info->ramdisk, NULL); if (b_info->cmdline) flexarray_vappend(dm_args, "-append", b_info->cmdline, NULL); if (b_info->u.hvm.serial || b_info->u.hvm.serial_list) { if ( b_info->u.hvm.serial && b_info->u.hvm.serial_list ) { LOGD(ERROR, guest_domid, "Both serial and serial_list set"); return ERROR_INVAL; } if (b_info->u.hvm.serial) { flexarray_vappend(dm_args, "-serial", b_info->u.hvm.serial, NULL); } else if (b_info->u.hvm.serial_list) { char **p; for (p = b_info->u.hvm.serial_list; *p; p++) { flexarray_vappend(dm_args, "-serial", *p, NULL); } } } if (libxl_defbool_val(b_info->u.hvm.nographic) && (!sdl && !vnc)) { flexarray_append(dm_args, "-nographic"); } if (libxl_defbool_val(b_info->u.hvm.spice.enable)) { const libxl_spice_info *spice = &b_info->u.hvm.spice; char *spiceoptions = dm_spice_options(gc, spice); if (!spiceoptions) return ERROR_INVAL; flexarray_append(dm_args, "-spice"); flexarray_append(dm_args, spiceoptions); if (libxl_defbool_val(b_info->u.hvm.spice.vdagent)) { flexarray_vappend(dm_args, "-device", "virtio-serial", "-chardev", "spicevmc,id=vdagent,name=vdagent", "-device", "virtserialport,chardev=vdagent,name=com.redhat.spice.0", NULL); } } switch (b_info->u.hvm.vga.kind) { case LIBXL_VGA_INTERFACE_TYPE_STD: flexarray_append_pair(dm_args, "-device", GCSPRINTF("VGA,vgamem_mb=%d", libxl__sizekb_to_mb(b_info->video_memkb))); break; case LIBXL_VGA_INTERFACE_TYPE_CIRRUS: flexarray_append_pair(dm_args, "-device", GCSPRINTF("cirrus-vga,vgamem_mb=%d", libxl__sizekb_to_mb(b_info->video_memkb))); break; case LIBXL_VGA_INTERFACE_TYPE_NONE: break; case LIBXL_VGA_INTERFACE_TYPE_QXL: /* QXL have 2 ram regions, ram and vram */ flexarray_append_pair(dm_args, "-device", GCSPRINTF("qxl-vga,vram_size_mb=%"PRIu64",ram_size_mb=%"PRIu64, (b_info->video_memkb/2/1024), (b_info->video_memkb/2/1024) ) ); break; default: LOGD(ERROR, guest_domid, "Invalid emulated video card specified"); return ERROR_INVAL; } if (b_info->u.hvm.boot) { flexarray_vappend(dm_args, "-boot", GCSPRINTF("order=%s", b_info->u.hvm.boot), NULL); } if (libxl_defbool_val(b_info->u.hvm.usb) || b_info->u.hvm.usbdevice || libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) { if (b_info->u.hvm.usbdevice && libxl_string_list_length(&b_info->u.hvm.usbdevice_list)) { LOGD(ERROR, guest_domid, "Both usbdevice and usbdevice_list set"); return ERROR_INVAL; } flexarray_append(dm_args, "-usb"); if (b_info->u.hvm.usbdevice) { flexarray_vappend(dm_args, "-usbdevice", b_info->u.hvm.usbdevice, NULL); } else if (b_info->u.hvm.usbdevice_list) { char **p; for (p = b_info->u.hvm.usbdevice_list; *p; p++) { flexarray_vappend(dm_args, "-usbdevice", *p, NULL); } } } else if (b_info->u.hvm.usbversion) { switch (b_info->u.hvm.usbversion) { case 1: flexarray_vappend(dm_args, "-device", "piix3-usb-uhci,id=usb", NULL); break; case 2: flexarray_append_pair(dm_args, "-device", "ich9-usb-ehci1,id=usb,addr=0x1d.0x7,multifunction=on"); for (i = 1; i < 4; i++) flexarray_append_pair(dm_args, "-device", GCSPRINTF("ich9-usb-uhci%d,masterbus=usb.0," "firstport=%d,addr=0x1d.%#x,multifunction=on", i, 2*(i-1), i-1)); break; case 3: flexarray_vappend(dm_args, "-device", "nec-usb-xhci,id=usb", NULL); break; default: LOGD(ERROR, guest_domid, "usbversion parameter is invalid, " "must be between 1 and 3"); return ERROR_INVAL; } if (b_info->u.hvm.spice.usbredirection >= 0 && b_info->u.hvm.spice.usbredirection < 5) { for (i = 1; i <= b_info->u.hvm.spice.usbredirection; i++) flexarray_vappend(dm_args, "-chardev", GCSPRINTF("spicevmc,name=usbredir,id=usbrc%d", i), "-device", GCSPRINTF("usb-redir,chardev=usbrc%d," "id=usbrc%d", i, i), NULL); } else { LOGD(ERROR, guest_domid, "usbredirection parameter is invalid, " "it must be between 1 and 4"); return ERROR_INVAL; } } if (b_info->u.hvm.soundhw) { flexarray_vappend(dm_args, "-soundhw", b_info->u.hvm.soundhw, NULL); } if (!libxl__acpi_defbool_val(b_info)) { flexarray_append(dm_args, "-no-acpi"); } if (b_info->max_vcpus > 1) { flexarray_append(dm_args, "-smp"); if (b_info->avail_vcpus.size) { int nr_set_cpus = 0; nr_set_cpus = libxl_bitmap_count_set(&b_info->avail_vcpus); flexarray_append(dm_args, GCSPRINTF("%d,maxcpus=%d", nr_set_cpus, b_info->max_vcpus)); } else flexarray_append(dm_args, GCSPRINTF("%d", b_info->max_vcpus)); } for (i = 0; i < num_nics; i++) { if (nics[i].nictype == LIBXL_NIC_TYPE_VIF_IOEMU) { char *smac = GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nics[i].mac)); const char *ifname = libxl__device_nic_devname(gc, guest_domid, nics[i].devid, LIBXL_NIC_TYPE_VIF_IOEMU); flexarray_append(dm_args, "-device"); flexarray_append(dm_args, GCSPRINTF("%s,id=nic%d,netdev=net%d,mac=%s", nics[i].model, nics[i].devid, nics[i].devid, smac)); flexarray_append(dm_args, "-netdev"); flexarray_append(dm_args, GCSPRINTF("type=tap,id=net%d,ifname=%s," "script=%s,downscript=%s", nics[i].devid, ifname, libxl_tapif_script(gc), libxl_tapif_script(gc))); /* Userspace COLO Proxy need this */ #define APPEND_COLO_SOCK_SERVER(sock_id, sock_ip, sock_port) ({ \ if (nics[i].colo_##sock_id && \ nics[i].colo_##sock_ip && \ nics[i].colo_##sock_port) { \ flexarray_append(dm_args, "-chardev"); \ flexarray_append(dm_args, \ GCSPRINTF("socket,id=%s,host=%s,port=%s,server,nowait", \ nics[i].colo_##sock_id, \ nics[i].colo_##sock_ip, \ nics[i].colo_##sock_port)); \ } \ }) #define APPEND_COLO_SOCK_CLIENT(sock_id, sock_ip, sock_port) ({ \ if (nics[i].colo_##sock_id && \ nics[i].colo_##sock_ip && \ nics[i].colo_##sock_port) { \ flexarray_append(dm_args, "-chardev"); \ flexarray_append(dm_args, \ GCSPRINTF("socket,id=%s,host=%s,port=%s", \ nics[i].colo_##sock_id, \ nics[i].colo_##sock_ip, \ nics[i].colo_##sock_port)); \ } \ }) if (state->saved_state) { /* secondary colo run */ APPEND_COLO_SOCK_CLIENT(sock_sec_redirector0_id, sock_sec_redirector0_ip, sock_sec_redirector0_port); APPEND_COLO_SOCK_CLIENT(sock_sec_redirector1_id, sock_sec_redirector1_ip, sock_sec_redirector1_port); if (nics[i].colo_filter_sec_redirector0_queue && nics[i].colo_filter_sec_redirector0_indev) { flexarray_append(dm_args, "-object"); flexarray_append(dm_args, GCSPRINTF("filter-redirector,id=rs1,netdev=net%d,queue=%s,indev=%s", nics[i].devid, nics[i].colo_filter_sec_redirector0_queue, nics[i].colo_filter_sec_redirector0_indev)); } if (nics[i].colo_filter_sec_redirector1_queue && nics[i].colo_filter_sec_redirector1_outdev) { flexarray_append(dm_args, "-object"); flexarray_append(dm_args, GCSPRINTF("filter-redirector,id=rs2,netdev=net%d,queue=%s,outdev=%s", nics[i].devid, nics[i].colo_filter_sec_redirector1_queue, nics[i].colo_filter_sec_redirector1_outdev)); } if (nics[i].colo_filter_sec_rewriter0_queue) { flexarray_append(dm_args, "-object"); flexarray_append(dm_args, GCSPRINTF("filter-rewriter,id=rs3,netdev=net%d,queue=%s", nics[i].devid, nics[i].colo_filter_sec_rewriter0_queue)); } } else { /* primary colo run */ APPEND_COLO_SOCK_SERVER(sock_mirror_id, sock_mirror_ip, sock_mirror_port); APPEND_COLO_SOCK_SERVER(sock_compare_pri_in_id, sock_compare_pri_in_ip, sock_compare_pri_in_port); APPEND_COLO_SOCK_SERVER(sock_compare_sec_in_id, sock_compare_sec_in_ip, sock_compare_sec_in_port); APPEND_COLO_SOCK_SERVER(sock_compare_notify_id, sock_compare_notify_ip, sock_compare_notify_port); APPEND_COLO_SOCK_SERVER(sock_redirector0_id, sock_redirector0_ip, sock_redirector0_port); APPEND_COLO_SOCK_CLIENT(sock_redirector1_id, sock_redirector1_ip, sock_redirector1_port); APPEND_COLO_SOCK_CLIENT(sock_redirector2_id, sock_redirector2_ip, sock_redirector2_port); if (nics[i].colo_filter_mirror_queue && nics[i].colo_filter_mirror_outdev) { flexarray_append(dm_args, "-object"); flexarray_append(dm_args, GCSPRINTF("filter-mirror,id=m1,netdev=net%d,queue=%s,outdev=%s", nics[i].devid, nics[i].colo_filter_mirror_queue, nics[i].colo_filter_mirror_outdev)); } if (nics[i].colo_filter_redirector0_queue && nics[i].colo_filter_redirector0_indev) { flexarray_append(dm_args, "-object"); flexarray_append(dm_args, GCSPRINTF("filter-redirector,id=r1,netdev=net%d,queue=%s,indev=%s", nics[i].devid, nics[i].colo_filter_redirector0_queue, nics[i].colo_filter_redirector0_indev)); } if (nics[i].colo_filter_redirector1_queue && nics[i].colo_filter_redirector1_outdev) { flexarray_append(dm_args, "-object"); flexarray_append(dm_args, GCSPRINTF("filter-redirector,id=r2,netdev=net%d,queue=%s,outdev=%s", nics[i].devid, nics[i].colo_filter_redirector1_queue, nics[i].colo_filter_redirector1_outdev)); } if (nics[i].colo_compare_pri_in && nics[i].colo_compare_sec_in && nics[i].colo_compare_out && nics[i].colo_compare_notify_dev) { flexarray_append(dm_args, "-object"); flexarray_append(dm_args, GCSPRINTF("colo-compare,id=c1,primary_in=%s,secondary_in=%s,outdev=%s,notify_dev=%s", nics[i].colo_compare_pri_in, nics[i].colo_compare_sec_in, nics[i].colo_compare_out, nics[i].colo_compare_notify_dev)); } } ioemu_nics++; #undef APPEND_COLO_SOCK_SERVER #undef APPEND_COLO_SOCK_CLIENT } } /* If we have no emulated nics, tell qemu not to create any */ if ( ioemu_nics == 0 ) { flexarray_append(dm_args, "-net"); flexarray_append(dm_args, "none"); } } else { if (!sdl && !vnc) { flexarray_append(dm_args, "-nographic"); } } if (state->saved_state) { /* This file descriptor is meant to be used by QEMU */ *dm_state_fd = open(state->saved_state, O_RDONLY); flexarray_append(dm_args, "-incoming"); flexarray_append(dm_args, GCSPRINTF("fd:%d",*dm_state_fd)); } for (i = 0; b_info->extra && b_info->extra[i] != NULL; i++) flexarray_append(dm_args, b_info->extra[i]); flexarray_append(dm_args, "-machine"); switch (b_info->type) { case LIBXL_DOMAIN_TYPE_PV: flexarray_append(dm_args, "xenpv"); for (i = 0; b_info->extra_pv && b_info->extra_pv[i] != NULL; i++) flexarray_append(dm_args, b_info->extra_pv[i]); break; case LIBXL_DOMAIN_TYPE_HVM: if (!libxl_defbool_val(b_info->u.hvm.xen_platform_pci)) { /* Switching here to the machine "pc" which does not add * the xen-platform device instead of the default "xenfv" machine. */ machinearg = libxl__strdup(gc, "pc,accel=xen"); } else { machinearg = libxl__strdup(gc, "xenfv"); } if (b_info->u.hvm.mmio_hole_memkb) { uint64_t max_ram_below_4g = (1ULL << 32) - (b_info->u.hvm.mmio_hole_memkb << 10); if (max_ram_below_4g > HVM_BELOW_4G_MMIO_START) { LOGD(WARN, guest_domid, "mmio_hole_memkb=%"PRIu64 " invalid ignored.\n", b_info->u.hvm.mmio_hole_memkb); } else { machinearg = GCSPRINTF("%s,max-ram-below-4g=%"PRIu64, machinearg, max_ram_below_4g); } } if (libxl_defbool_val(b_info->u.hvm.gfx_passthru)) { enum libxl_gfx_passthru_kind gfx_passthru_kind = libxl__detect_gfx_passthru_kind(gc, guest_config); switch (gfx_passthru_kind) { case LIBXL_GFX_PASSTHRU_KIND_IGD: machinearg = GCSPRINTF("%s,igd-passthru=on", machinearg); break; case LIBXL_GFX_PASSTHRU_KIND_DEFAULT: LOGD(ERROR, guest_domid, "unable to detect required gfx_passthru_kind"); return ERROR_FAIL; default: LOGD(ERROR, guest_domid, "invalid value for gfx_passthru_kind"); return ERROR_INVAL; } } flexarray_append(dm_args, machinearg); for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++) flexarray_append(dm_args, b_info->extra_hvm[i]); break; default: abort(); } ram_size = libxl__sizekb_to_mb(b_info->max_memkb - b_info->video_memkb); flexarray_append(dm_args, "-m"); flexarray_append(dm_args, GCSPRINTF("%"PRId64, ram_size)); if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) { if (b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI) flexarray_append_pair(dm_args, "-device", "ahci,id=ahci0"); for (i = 0; i < num_disks; i++) { int disk, part; int dev_number = libxl__device_disk_dev_number(disks[i].vdev, &disk, &part); const char *format; char *drive; const char *target_path = NULL; int colo_mode; if (dev_number == -1) { LOGD(WARN, guest_domid, "unable to determine"" disk number for %s", disks[i].vdev); continue; } /* * If qemu isn't doing the interpreting, the parameter is * always raw */ if (disks[i].backend == LIBXL_DISK_BACKEND_QDISK) format = qemu_disk_format_string(disks[i].format); else format = qemu_disk_format_string(LIBXL_DISK_FORMAT_RAW); if (disks[i].format == LIBXL_DISK_FORMAT_EMPTY) { if (!disks[i].is_cdrom) { LOGD(WARN, guest_domid, "Cannot support empty disk format for %s", disks[i].vdev); continue; } } else { if (format == NULL) { LOGD(WARN, guest_domid, "Unable to determine disk image format: %s\n" "Disk will be available via PV drivers but not as an" "emulated disk.", disks[i].vdev); continue; } /* * We can't call libxl__blktap_devpath from * libxl__device_disk_find_local_path for now because * the bootloader is called before the disks are set * up, so this function would set up a blktap node, * but there's no TAP tear-down on error conditions in * the bootloader path. */ if (disks[i].backend == LIBXL_DISK_BACKEND_TAP) target_path = libxl__blktap_devpath(gc, disks[i].pdev_path, disks[i].format); else target_path = libxl__device_disk_find_local_path(gc, guest_domid, &disks[i], true); if (!target_path) { LOGD(WARN, guest_domid, "No way to get local access disk to image: %s\n" "Disk will be available via PV drivers but not as an" "emulated disk.", disks[i].vdev); continue; } } if (disks[i].is_cdrom) { drive = libxl__sprintf(gc, "if=ide,index=%d,readonly=on,media=cdrom,id=ide-%i", disk, dev_number); if (target_path) drive = libxl__sprintf(gc, "%s,file=%s,format=%s", drive, target_path, format); } else { /* * Explicit sd disks are passed through as is. * * For other disks we translate devices 0..3 into * hd[a-d] and ignore the rest. */ if (libxl_defbool_val(disks[i].colo_enable)) { if (libxl_defbool_val(disks[i].colo_restore_enable)) colo_mode = LIBXL__COLO_SECONDARY; else colo_mode = LIBXL__COLO_PRIMARY; } else { colo_mode = LIBXL__COLO_NONE; } if (strncmp(disks[i].vdev, "sd", 2) == 0) { if (colo_mode == LIBXL__COLO_SECONDARY) { drive = libxl__sprintf (gc, "if=none,driver=%s,file=%s,id=%s", format, target_path, disks[i].colo_export); flexarray_append(dm_args, "-drive"); flexarray_append(dm_args, drive); } drive = qemu_disk_scsi_drive_string(gc, target_path, disk, format, &disks[i], colo_mode); } else if (disk < 6 && b_info->u.hvm.hdtype == LIBXL_HDTYPE_AHCI) { if (!disks[i].readwrite) { LOGD(ERROR, guest_domid, "qemu-xen doesn't support read-only AHCI disk drivers"); return ERROR_INVAL; } flexarray_vappend(dm_args, "-drive", GCSPRINTF("file=%s,if=none,id=ahcidisk-%d,format=%s,cache=writeback", target_path, disk, format), "-device", GCSPRINTF("ide-hd,bus=ahci0.%d,unit=0,drive=ahcidisk-%d", disk, disk), NULL); continue; } else if (disk < 4) { if (!disks[i].readwrite) { LOGD(ERROR, guest_domid, "qemu-xen doesn't support read-only IDE disk drivers"); return ERROR_INVAL; } if (colo_mode == LIBXL__COLO_SECONDARY) { drive = libxl__sprintf (gc, "if=none,driver=%s,file=%s,id=%s", format, target_path, disks[i].colo_export); flexarray_append(dm_args, "-drive"); flexarray_append(dm_args, drive); } drive = qemu_disk_ide_drive_string(gc, target_path, disk, format, &disks[i], colo_mode); } else { continue; /* Do not emulate this disk */ } if (!drive) continue; } flexarray_append(dm_args, "-drive"); flexarray_append(dm_args, drive); } switch (b_info->u.hvm.vendor_device) { case LIBXL_VENDOR_DEVICE_XENSERVER: flexarray_append(dm_args, "-device"); flexarray_append(dm_args, "xen-pvdevice,device-id=0xc000"); break; default: break; } if (b_info->device_model_user) { user = b_info->device_model_user; goto end_search; } user = GCSPRINTF("%s%d", LIBXL_QEMU_USER_BASE, guest_domid); ret = libxl__dm_runas_helper(gc, user); if (ret < 0) return ret; if (ret > 0) goto end_search; user = LIBXL_QEMU_USER_SHARED; ret = libxl__dm_runas_helper(gc, user); if (ret < 0) return ret; if (ret > 0) { LOGD(WARN, guest_domid, "Could not find user %s%d, falling back to %s", LIBXL_QEMU_USER_BASE, guest_domid, LIBXL_QEMU_USER_SHARED); goto end_search; } user = NULL; LOGD(DEBUG, guest_domid, "Could not find user %s, starting QEMU as root", LIBXL_QEMU_USER_SHARED); end_search: if (user != NULL && strcmp(user, "root")) { flexarray_append(dm_args, "-runas"); flexarray_append(dm_args, user); } } flexarray_append(dm_args, NULL); *args = (char **) flexarray_contents(dm_args); flexarray_append(dm_envs, NULL); if (envs) *envs = (char **) flexarray_contents(dm_envs); return 0; } static int libxl__build_device_model_args(libxl__gc *gc, const char *dm, int guest_domid, const libxl_domain_config *guest_config, char ***args, char ***envs, const libxl__domain_build_state *state, int *dm_state_fd) /* dm_state_fd may be NULL iff caller knows we are using old stubdom * and therefore will be passing a filename rather than a fd. */ { switch (guest_config->b_info.device_model_version) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: return libxl__build_device_model_args_old(gc, dm, guest_domid, guest_config, args, envs, state); case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: assert(dm_state_fd != NULL); assert(*dm_state_fd < 0); return libxl__build_device_model_args_new(gc, dm, guest_domid, guest_config, args, envs, state, dm_state_fd); default: LOGED(ERROR, guest_domid, "unknown device model version %d", guest_config->b_info.device_model_version); return ERROR_INVAL; } } static void libxl__dm_vifs_from_hvm_guest_config(libxl__gc *gc, libxl_domain_config * const guest_config, libxl_domain_config *dm_config) { int i, nr = guest_config->num_nics; GCNEW_ARRAY(dm_config->nics, nr); for (i=0; inics[i] = guest_config->nics[i]; dm_config->nics[i].nictype = LIBXL_NIC_TYPE_VIF; if (dm_config->nics[i].ifname) dm_config->nics[i].ifname = GCSPRINTF("%s" TAP_DEVICE_SUFFIX, dm_config->nics[i].ifname); } dm_config->num_nics = nr; } static int libxl__vfb_and_vkb_from_hvm_guest_config(libxl__gc *gc, const libxl_domain_config *guest_config, libxl_device_vfb *vfb, libxl_device_vkb *vkb) { const libxl_domain_build_info *b_info = &guest_config->b_info; if (b_info->type != LIBXL_DOMAIN_TYPE_HVM) return ERROR_INVAL; libxl_device_vfb_init(vfb); libxl_device_vkb_init(vkb); vfb->backend_domid = 0; vfb->devid = 0; vfb->vnc = b_info->u.hvm.vnc; vfb->keymap = b_info->u.hvm.keymap; vfb->sdl = b_info->u.hvm.sdl; vkb->backend_domid = 0; vkb->devid = 0; return 0; } static int libxl__write_stub_dmargs(libxl__gc *gc, int dm_domid, int guest_domid, char **args) { libxl_ctx *ctx = libxl__gc_owner(gc); int i; char *vm_path; char *dmargs, *path; int dmargs_size; struct xs_permissions roperm[2]; xs_transaction_t t; roperm[0].id = 0; roperm[0].perms = XS_PERM_NONE; roperm[1].id = dm_domid; roperm[1].perms = XS_PERM_READ; vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("/local/domain/%d/vm", guest_domid)); i = 0; dmargs_size = 0; while (args[i] != NULL) { dmargs_size = dmargs_size + strlen(args[i]) + 1; i++; } dmargs_size++; dmargs = (char *) libxl__malloc(gc, dmargs_size); i = 1; dmargs[0] = '\0'; while (args[i] != NULL) { if (strcmp(args[i], "-sdl") && strcmp(args[i], "-M") && strcmp(args[i], "xenfv")) { strcat(dmargs, " "); strcat(dmargs, args[i]); } i++; } path = GCSPRINTF("%s/image/dmargs", vm_path); retry_transaction: t = xs_transaction_start(ctx->xsh); xs_write(ctx->xsh, t, path, dmargs, strlen(dmargs)); xs_set_permissions(ctx->xsh, t, path, roperm, ARRAY_SIZE(roperm)); xs_set_permissions(ctx->xsh, t, GCSPRINTF("%s/rtc/timeoffset", vm_path), roperm, ARRAY_SIZE(roperm)); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; return 0; } static void spawn_stubdom_pvqemu_cb(libxl__egc *egc, libxl__dm_spawn_state *stubdom_dmss, int rc); static void spawn_stub_launch_dm(libxl__egc *egc, libxl__multidev *aodevs, int ret); static void stubdom_pvqemu_cb(libxl__egc *egc, libxl__multidev *aodevs, int rc); static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait, int rc, const char *p); char *libxl__stub_dm_name(libxl__gc *gc, const char *guest_name) { return GCSPRINTF("%s-dm", guest_name); } void libxl__spawn_stub_dm(libxl__egc *egc, libxl__stub_dm_spawn_state *sdss) { STATE_AO_GC(sdss->dm.spawn.ao); libxl_ctx *ctx = libxl__gc_owner(gc); int ret; libxl_device_vfb *vfb; libxl_device_vkb *vkb; char **args; struct xs_permissions perm[2]; xs_transaction_t t; /* convenience aliases */ libxl_domain_config *const dm_config = &sdss->dm_config; libxl_domain_config *const guest_config = sdss->dm.guest_config; const int guest_domid = sdss->dm.guest_domid; libxl__domain_build_state *const d_state = sdss->dm.build_state; libxl__domain_build_state *const stubdom_state = &sdss->dm_state; if (guest_config->b_info.device_model_version != LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL) { ret = ERROR_INVAL; goto out; } sdss->pvqemu.guest_domid = 0; libxl_domain_create_info_init(&dm_config->c_info); dm_config->c_info.type = LIBXL_DOMAIN_TYPE_PV; dm_config->c_info.name = libxl__stub_dm_name(gc, libxl__domid_to_name(gc, guest_domid)); /* When we are here to launch stubdom, ssidref is a valid value * already, no need to parse it again. */ dm_config->c_info.ssidref = guest_config->b_info.device_model_ssidref; dm_config->c_info.ssid_label = NULL; libxl_uuid_generate(&dm_config->c_info.uuid); libxl_domain_build_info_init(&dm_config->b_info); libxl_domain_build_info_init_type(&dm_config->b_info, LIBXL_DOMAIN_TYPE_PV); dm_config->b_info.max_vcpus = 1; dm_config->b_info.max_memkb = 28 * 1024 + guest_config->b_info.video_memkb; dm_config->b_info.target_memkb = dm_config->b_info.max_memkb; dm_config->b_info.u.pv.features = ""; dm_config->b_info.device_model_version = guest_config->b_info.device_model_version; dm_config->b_info.device_model = guest_config->b_info.device_model; dm_config->b_info.extra = guest_config->b_info.extra; dm_config->b_info.extra_pv = guest_config->b_info.extra_pv; dm_config->b_info.extra_hvm = guest_config->b_info.extra_hvm; dm_config->disks = guest_config->disks; dm_config->num_disks = guest_config->num_disks; libxl__dm_vifs_from_hvm_guest_config(gc, guest_config, dm_config); dm_config->c_info.run_hotplug_scripts = guest_config->c_info.run_hotplug_scripts; ret = libxl__domain_create_info_setdefault(gc, &dm_config->c_info); if (ret) goto out; ret = libxl__domain_build_info_setdefault(gc, &dm_config->b_info); if (ret) goto out; GCNEW(vfb); GCNEW(vkb); libxl__vfb_and_vkb_from_hvm_guest_config(gc, guest_config, vfb, vkb); dm_config->vfbs = vfb; dm_config->num_vfbs = 1; dm_config->vkbs = vkb; dm_config->num_vkbs = 1; stubdom_state->pv_kernel.path = libxl__abs_path(gc, "ioemu-stubdom.gz", libxl__xenfirmwaredir_path()); stubdom_state->pv_cmdline = GCSPRINTF(" -d %d", guest_domid); stubdom_state->pv_ramdisk.path = ""; /* fixme: this function can leak the stubdom if it fails */ ret = libxl__domain_make(gc, dm_config, &sdss->pvqemu.guest_domid, &stubdom_state->config); if (ret) goto out; uint32_t dm_domid = sdss->pvqemu.guest_domid; ret = libxl__domain_build(gc, dm_config, dm_domid, stubdom_state); if (ret) goto out; ret = libxl__build_device_model_args(gc, "stubdom-dm", guest_domid, guest_config, &args, NULL, d_state, NULL); if (ret) { ret = ERROR_FAIL; goto out; } libxl__write_stub_dmargs(gc, dm_domid, guest_domid, args); libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/image/device-model-domid", libxl__xs_get_dompath(gc, guest_domid)), "%d", dm_domid); libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/target", libxl__xs_get_dompath(gc, dm_domid)), "%d", guest_domid); ret = xc_domain_set_target(ctx->xch, dm_domid, guest_domid); if (ret<0) { LOGED(ERROR, guest_domid, "setting target domain %d -> %d", dm_domid, guest_domid); ret = ERROR_FAIL; goto out; } xs_set_target(ctx->xsh, dm_domid, guest_domid); perm[0].id = dm_domid; perm[0].perms = XS_PERM_NONE; perm[1].id = guest_domid; perm[1].perms = XS_PERM_READ; retry_transaction: t = xs_transaction_start(ctx->xsh); xs_mkdir(ctx->xsh, t, DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, "")); xs_set_permissions(ctx->xsh, t, DEVICE_MODEL_XS_PATH(gc, dm_domid, guest_domid, ""), perm, ARRAY_SIZE(perm)); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; libxl__multidev_begin(ao, &sdss->multidev); sdss->multidev.callback = spawn_stub_launch_dm; libxl__add_disks(egc, ao, dm_domid, dm_config, &sdss->multidev); libxl__multidev_prepared(egc, &sdss->multidev, 0); return; out: assert(ret); spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, ret); } static void spawn_stub_launch_dm(libxl__egc *egc, libxl__multidev *multidev, int ret) { libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev); STATE_AO_GC(sdss->dm.spawn.ao); libxl_ctx *ctx = libxl__gc_owner(gc); int i, num_console = STUBDOM_SPECIAL_CONSOLES; libxl__device_console *console; /* convenience aliases */ libxl_domain_config *const dm_config = &sdss->dm_config; libxl_domain_config *const guest_config = sdss->dm.guest_config; const int guest_domid = sdss->dm.guest_domid; libxl__domain_build_state *const d_state = sdss->dm.build_state; libxl__domain_build_state *const stubdom_state = &sdss->dm_state; uint32_t dm_domid = sdss->pvqemu.guest_domid; if (ret) { LOGD(ERROR, guest_domid, "error connecting disk devices"); goto out; } for (i = 0; i < dm_config->num_nics; i++) { /* We have to init the nic here, because we still haven't * called libxl_device_nic_add at this point, but qemu needs * the nic information to be complete. */ ret = libxl__device_nic_setdefault(gc, &dm_config->nics[i], dm_domid, false); if (ret) goto out; } ret = libxl__device_vfb_add(gc, dm_domid, &dm_config->vfbs[0]); if (ret) goto out; ret = libxl__device_vkb_add(gc, dm_domid, &dm_config->vkbs[0]); if (ret) goto out; if (guest_config->b_info.u.hvm.serial) num_console++; console = libxl__calloc(gc, num_console, sizeof(libxl__device_console)); for (i = 0; i < num_console; i++) { libxl__device device; console[i].devid = i; console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU; /* STUBDOM_CONSOLE_LOGGING (console 0) is for minios logging * STUBDOM_CONSOLE_SAVE (console 1) is for writing the save file * STUBDOM_CONSOLE_RESTORE (console 2) is for reading the save file */ switch (i) { char *filename; char *name; case STUBDOM_CONSOLE_LOGGING: name = GCSPRINTF("qemu-dm-%s", libxl_domid_to_name(ctx, guest_domid)); ret = libxl_create_logfile(ctx, name, &filename); if (ret) goto out; console[i].output = GCSPRINTF("file:%s", filename); free(filename); break; case STUBDOM_CONSOLE_SAVE: console[i].output = GCSPRINTF("file:%s", libxl__device_model_savefile(gc, guest_domid)); break; case STUBDOM_CONSOLE_RESTORE: if (d_state->saved_state) console[i].output = GCSPRINTF("pipe:%s", d_state->saved_state); break; default: console[i].output = "pty"; break; } ret = libxl__device_console_add(gc, dm_domid, &console[i], i == STUBDOM_CONSOLE_LOGGING ? stubdom_state : NULL, &device); if (ret) goto out; } sdss->pvqemu.spawn.ao = ao; sdss->pvqemu.guest_domid = dm_domid; sdss->pvqemu.guest_config = &sdss->dm_config; sdss->pvqemu.build_state = &sdss->dm_state; sdss->pvqemu.callback = spawn_stubdom_pvqemu_cb; libxl__spawn_local_dm(egc, &sdss->pvqemu); return; out: assert(ret); spawn_stubdom_pvqemu_cb(egc, &sdss->pvqemu, ret); } static void spawn_stubdom_pvqemu_cb(libxl__egc *egc, libxl__dm_spawn_state *stubdom_dmss, int rc) { libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(stubdom_dmss, *sdss, pvqemu); STATE_AO_GC(sdss->dm.spawn.ao); uint32_t dm_domid = sdss->pvqemu.guest_domid; libxl_domain_config *d_config = stubdom_dmss->guest_config; if (rc) goto out; if (d_config->num_nics > 0) { libxl__multidev_begin(ao, &sdss->multidev); sdss->multidev.callback = stubdom_pvqemu_cb; libxl__add_nics(egc, ao, dm_domid, d_config, &sdss->multidev); libxl__multidev_prepared(egc, &sdss->multidev, 0); return; } out: stubdom_pvqemu_cb(egc, &sdss->multidev, rc); } static void stubdom_pvqemu_cb(libxl__egc *egc, libxl__multidev *multidev, int rc) { libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(multidev, *sdss, multidev); STATE_AO_GC(sdss->dm.spawn.ao); uint32_t dm_domid = sdss->pvqemu.guest_domid; libxl__xswait_init(&sdss->xswait); if (rc) { LOGED(ERROR, sdss->dm.guest_domid, "error connecting nics devices"); goto out; } rc = libxl_domain_unpause(CTX, dm_domid); if (rc) goto out; sdss->xswait.ao = ao; sdss->xswait.what = GCSPRINTF("Stubdom %u for %u startup", dm_domid, sdss->dm.guest_domid); sdss->xswait.path = DEVICE_MODEL_XS_PATH(gc, dm_domid, sdss->dm.guest_domid, "/state"); sdss->xswait.timeout_ms = LIBXL_STUBDOM_START_TIMEOUT * 1000; sdss->xswait.callback = stubdom_xswait_cb; rc = libxl__xswait_start(gc, &sdss->xswait); if (rc) goto out; return; out: stubdom_xswait_cb(egc, &sdss->xswait, rc, NULL); } static void stubdom_xswait_cb(libxl__egc *egc, libxl__xswait_state *xswait, int rc, const char *p) { EGC_GC; libxl__stub_dm_spawn_state *sdss = CONTAINER_OF(xswait, *sdss, xswait); if (rc) { if (rc == ERROR_TIMEDOUT) LOGD(ERROR, sdss->dm.guest_domid, "%s: startup timed out", xswait->what); goto out; } if (!p) return; if (strcmp(p, "running")) return; out: libxl__xswait_stop(gc, xswait); sdss->callback(egc, &sdss->dm, rc); } /* callbacks passed to libxl__spawn_spawn */ static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn, const char *xsdata); static void device_model_startup_failed(libxl__egc *egc, libxl__spawn_state *spawn, int rc); static void device_model_detached(libxl__egc *egc, libxl__spawn_state *spawn); /* our "next step" function, called from those callbacks and elsewhere */ static void device_model_spawn_outcome(libxl__egc *egc, libxl__dm_spawn_state *dmss, int rc); void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss) { /* convenience aliases */ const int domid = dmss->guest_domid; libxl__domain_build_state *const state = dmss->build_state; libxl__spawn_state *const spawn = &dmss->spawn; STATE_AO_GC(dmss->spawn.ao); libxl_ctx *ctx = CTX; libxl_domain_config *guest_config = dmss->guest_config; const libxl_domain_create_info *c_info = &guest_config->c_info; const libxl_domain_build_info *b_info = &guest_config->b_info; const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config); char *path; int logfile_w, null; int rc; char **args, **arg, **envs; xs_transaction_t t; char *vm_path; char **pass_stuff; const char *dm; int dm_state_fd = -1; if (libxl_defbool_val(b_info->device_model_stubdomain)) { abort(); } dm = libxl__domain_device_model(gc, b_info); if (!dm) { rc = ERROR_FAIL; goto out; } if (access(dm, X_OK) < 0) { LOGED(ERROR, domid, "device model %s is not executable", dm); rc = ERROR_FAIL; goto out; } rc = libxl__build_device_model_args(gc, dm, domid, guest_config, &args, &envs, state, &dm_state_fd); if (rc) goto out; if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) { path = xs_get_domain_path(ctx->xsh, domid); libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/hvmloader/bios", path), "%s", libxl_bios_type_to_string(b_info->u.hvm.bios)); /* Disable relocating memory to make the MMIO hole larger * unless we're running qemu-traditional and vNUMA is not * configured. */ libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/hvmloader/allow-memory-relocate", path), "%d", b_info->device_model_version==LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL && !libxl__vnuma_configured(b_info)); free(path); } path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, ""); xs_mkdir(ctx->xsh, XBT_NULL, path); if (b_info->type == LIBXL_DOMAIN_TYPE_HVM && b_info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL) libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/disable_pf", path), "%d", !libxl_defbool_val(b_info->u.hvm.xen_platform_pci)); logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qemu-dm-%s", c_info->name)); if (logfile_w < 0) { rc = logfile_w; goto out; } null = open("/dev/null", O_RDONLY); if (null < 0) { LOGED(ERROR, domid, "unable to open /dev/null"); rc = ERROR_FAIL; goto out_close; } const char *dom_path = libxl__xs_get_dompath(gc, domid); spawn->pidpath = GCSPRINTF("%s/%s", dom_path, "image/device-model-pid"); if (vnc && vnc->passwd) { /* This xenstore key will only be used by qemu-xen-traditionnal. * The code to supply vncpasswd to qemu-xen is later. */ retry_transaction: /* Find uuid and the write the vnc password to xenstore for qemu. */ t = xs_transaction_start(ctx->xsh); vm_path = libxl__xs_read(gc,t,GCSPRINTF("%s/vm", dom_path)); if (vm_path) { /* Now write the vncpassword into it. */ pass_stuff = libxl__calloc(gc, 3, sizeof(char *)); pass_stuff[0] = "vncpasswd"; pass_stuff[1] = vnc->passwd; libxl__xs_writev(gc,t,vm_path,pass_stuff); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; } } LOGD(DEBUG, domid, "Spawning device-model %s with arguments:", dm); for (arg = args; *arg; arg++) LOGD(DEBUG, domid, " %s", *arg); if (*envs) { LOGD(DEBUG, domid, "Spawning device-model %s with additional environment:", dm); for (arg = envs; *arg; arg += 2) LOGD(DEBUG, domid, " %s=%s", arg[0], arg[1]); } spawn->what = GCSPRINTF("domain %d device model", domid); spawn->xspath = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, "/state"); spawn->timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000; spawn->pidpath = GCSPRINTF("%s/image/device-model-pid", dom_path); spawn->midproc_cb = libxl__spawn_record_pid; spawn->confirm_cb = device_model_confirm; spawn->failure_cb = device_model_startup_failed; spawn->detached_cb = device_model_detached; rc = libxl__spawn_spawn(egc, spawn); if (rc < 0) goto out_close; if (!rc) { /* inner child */ setsid(); libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs); } rc = 0; out_close: if (null >= 0) close(null); if (logfile_w >= 0) close(logfile_w); out: if (dm_state_fd >= 0) close(dm_state_fd); if (rc) device_model_spawn_outcome(egc, dmss, rc); } bool libxl__query_qemu_backend(libxl__gc *gc, uint32_t domid, uint32_t backend_id, const char *type, bool def) { char *path; char **dir; unsigned int n; path = GCSPRINTF("%s/device-model/%u/backends", libxl__xs_get_dompath(gc, backend_id), domid); dir = libxl__xs_directory(gc, XBT_NULL, path, &n); if (!dir) return def; path = GCSPRINTF("%s/device-model/%u/backends/%s", libxl__xs_get_dompath(gc, backend_id), domid, type); dir = libxl__xs_directory(gc, XBT_NULL, path, &n); return !!dir; } static void device_model_confirm(libxl__egc *egc, libxl__spawn_state *spawn, const char *xsdata) { STATE_AO_GC(spawn->ao); if (!xsdata) return; if (strcmp(xsdata, "running")) return; libxl__spawn_initiate_detach(gc, spawn); } static void device_model_startup_failed(libxl__egc *egc, libxl__spawn_state *spawn, int rc) { libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn); device_model_spawn_outcome(egc, dmss, rc); } static void device_model_detached(libxl__egc *egc, libxl__spawn_state *spawn) { libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn); device_model_spawn_outcome(egc, dmss, 0); } static void device_model_spawn_outcome(libxl__egc *egc, libxl__dm_spawn_state *dmss, int rc) { STATE_AO_GC(dmss->spawn.ao); int ret2; if (rc) LOGD(ERROR, dmss->guest_domid, "%s: spawn failed (rc=%d)", dmss->spawn.what, rc); libxl__domain_build_state *state = dmss->build_state; if (state->saved_state) { ret2 = unlink(state->saved_state); if (ret2) { LOGED(ERROR, dmss->guest_domid, "%s: failed to remove device-model state %s", dmss->spawn.what, state->saved_state); rc = ERROR_FAIL; goto out; } } out: dmss->callback(egc, dmss, rc); } void libxl__spawn_qdisk_backend(libxl__egc *egc, libxl__dm_spawn_state *dmss) { STATE_AO_GC(dmss->spawn.ao); flexarray_t *dm_args, *dm_envs; char **args, **envs; const char *dm; int logfile_w, null = -1, rc; uint32_t domid = dmss->guest_domid; /* Always use qemu-xen as device model */ dm = qemu_xen_path(gc); dm_args = flexarray_make(gc, 15, 1); dm_envs = flexarray_make(gc, 1, 1); flexarray_vappend(dm_args, dm, "-xen-domid", GCSPRINTF("%d", domid), NULL); flexarray_append(dm_args, "-xen-attach"); flexarray_vappend(dm_args, "-name", GCSPRINTF("domain-%u", domid), NULL); flexarray_append(dm_args, "-nographic"); flexarray_vappend(dm_args, "-M", "xenpv", NULL); flexarray_vappend(dm_args, "-monitor", "/dev/null", NULL); flexarray_vappend(dm_args, "-serial", "/dev/null", NULL); flexarray_vappend(dm_args, "-parallel", "/dev/null", NULL); flexarray_append(dm_args, NULL); args = (char **) flexarray_contents(dm_args); libxl__set_qemu_env_for_xsa_180(gc, dm_envs); envs = (char **) flexarray_contents(dm_envs); logfile_w = libxl__create_qemu_logfile(gc, GCSPRINTF("qdisk-%u", domid)); if (logfile_w < 0) { rc = logfile_w; goto out; } null = open("/dev/null", O_RDONLY); if (null < 0) { rc = ERROR_FAIL; goto out; } dmss->guest_config = NULL; /* * Clearly specify Qemu not using a saved state, so * device_model_spawn_outcome doesn't try to unlink it. */ dmss->build_state = libxl__zalloc(gc, sizeof(*dmss->build_state)); dmss->build_state->saved_state = 0; dmss->spawn.what = GCSPRINTF("domain %u Qdisk backend", domid); dmss->spawn.xspath = GCSPRINTF("device-model/%u/state", domid); dmss->spawn.timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000; /* * We cannot save Qemu pid anywhere in the xenstore guest dir, * because we will call this from unprivileged driver domains, * so save it in the current domain libxl private dir. */ dmss->spawn.pidpath = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid); dmss->spawn.midproc_cb = libxl__spawn_record_pid; dmss->spawn.confirm_cb = device_model_confirm; dmss->spawn.failure_cb = device_model_startup_failed; dmss->spawn.detached_cb = device_model_detached; rc = libxl__spawn_spawn(egc, &dmss->spawn); if (rc < 0) goto out; if (!rc) { /* inner child */ setsid(); libxl__exec(gc, null, logfile_w, logfile_w, dm, args, envs); } rc = 0; out: if (logfile_w >= 0) close(logfile_w); if (null >= 0) close(null); /* callback on error only, success goes via dmss->spawn.*_cb */ if (rc) dmss->callback(egc, dmss, rc); return; } /* Generic function to signal a Qemu instance to exit */ static int kill_device_model(libxl__gc *gc, const char *xs_path_pid) { const char *xs_pid; int ret, pid; ret = libxl__xs_read_checked(gc, XBT_NULL, xs_path_pid, &xs_pid); if (ret || !xs_pid) { LOG(ERROR, "unable to find device model pid in %s", xs_path_pid); ret = ret ? : ERROR_FAIL; goto out; } pid = atoi(xs_pid); ret = kill(pid, SIGHUP); if (ret < 0 && errno == ESRCH) { LOG(ERROR, "Device Model already exited"); ret = 0; } else if (ret == 0) { LOG(DEBUG, "Device Model signaled"); ret = 0; } else { LOGE(ERROR, "failed to kill Device Model [%d]", pid); ret = ERROR_FAIL; goto out; } out: return ret; } /* Helper to destroy a Qdisk backend */ int libxl__destroy_qdisk_backend(libxl__gc *gc, uint32_t domid) { char *pid_path; int rc; pid_path = GCSPRINTF("libxl/%u/qdisk-backend-pid", domid); rc = kill_device_model(gc, pid_path); if (rc) goto out; libxl__xs_rm_checked(gc, XBT_NULL, pid_path); libxl__xs_rm_checked(gc, XBT_NULL, GCSPRINTF("device-model/%u", domid)); out: return rc; } int libxl__destroy_device_model(libxl__gc *gc, uint32_t domid) { char *path = DEVICE_MODEL_XS_PATH(gc, LIBXL_TOOLSTACK_DOMID, domid, ""); if (!xs_rm(CTX->xsh, XBT_NULL, path)) LOGD(ERROR, domid, "xs_rm failed for %s", path); /* We should try to destroy the device model anyway. */ return kill_device_model(gc, GCSPRINTF("/local/domain/%d/image/device-model-pid", domid)); } /* Return 0 if no dm needed, 1 if needed and <0 if error. */ int libxl__need_xenpv_qemu(libxl__gc *gc, libxl_domain_config *d_config) { int idx, i, ret, num; uint32_t domid; const struct libxl_device_type *dt; ret = libxl__get_domid(gc, &domid); if (ret) { LOG(ERROR, "unable to get domain id"); goto out; } if (d_config->num_vfbs > 0) { ret = 1; goto out; } for (idx = 0;; idx++) { dt = device_type_tbl[idx]; if (!dt) break; num = *libxl__device_type_get_num(dt, d_config); if (!dt->dm_needed || !num) continue; for (i = 0; i < num; i++) { if (dt->dm_needed(libxl__device_type_get_elem(dt, d_config, i), domid)) { ret = 1; goto out; } } } for (i = 0; i < d_config->num_channels; i++) { if (d_config->channels[i].backend_domid == domid) { /* xenconsoled is limited to the first console only. Until this restriction is removed we must use qemu for secondary consoles which includes all channels. */ ret = 1; goto out; } } out: return ret; } int libxl__dm_active(libxl__gc *gc, uint32_t domid) { char *pid, *path; path = GCSPRINTF("/local/domain/%d/image/device-model-pid", domid); pid = libxl__xs_read(gc, XBT_NULL, path); return pid != NULL; } int libxl__dm_check_start(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid) { int rc; if (libxl__dm_active(gc, domid)) return 0; rc = libxl__need_xenpv_qemu(gc, d_config); if (rc < 0) goto out; if (!rc) return 0; LOGD(ERROR, domid, "device model required but not running"); rc = ERROR_FAIL; out: return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_disk_l.c0000664000175000017500000026207713256712137015735 0ustar smbsmb#line 2 "libxlu_disk_l.c" #line 31 "libxlu_disk_l.l" #include "libxl_osdeps.h" /* must come before any other headers */ #line 8 "libxlu_disk_l.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 39 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE xlu__disk_yyrestart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) #define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via xlu__disk_yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void xlu__disk_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void xlu__disk_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__disk_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void xlu__disk_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void xlu__disk_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void xlu__disk_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void xlu__disk_yypop_buffer_state (yyscan_t yyscanner ); static void xlu__disk_yyensure_buffer_stack (yyscan_t yyscanner ); static void xlu__disk_yy_load_buffer_state (yyscan_t yyscanner ); static void xlu__disk_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER xlu__disk_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE xlu__disk_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__disk_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__disk_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); void *xlu__disk_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *xlu__disk_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void xlu__disk_yyfree (void * ,yyscan_t yyscanner ); #define yy_new_buffer xlu__disk_yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ xlu__disk_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ xlu__disk_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ xlu__disk_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ xlu__disk_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) #define xlu__disk_yywrap(yyscanner) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyg->yytext_ptr -= yyg->yy_more_len; \ yyleng = (size_t) (yy_cp - yyg->yytext_ptr); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 36 #define YY_END_OF_BUFFER 37 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_acclist[575] = { 0, 35, 35, 37, 33, 34, 36, 8193, 33, 34, 36, 16385, 8193, 33, 36,16385, 33, 34, 36, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 33, 34, 36, 35, 36, 36, 33, 33, 8193, 33, 8193, 33,16385, 8193, 33, 8193, 33, 33, 8224, 33,16416, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 35, 8193, 33, 8193, 33, 8193, 8224, 33, 8224, 33, 8224, 23, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 8224, 33, 8224, 33, 8224, 23, 33, 33, 28, 8224, 33,16416, 33, 33, 15, 33, 33, 33, 33, 33, 33, 33, 33, 33, 8217, 8224, 33,16409,16416, 33, 33, 31, 8224, 33,16416, 33, 8216, 8224, 33,16408,16416, 33, 33, 8219, 8224, 33,16411,16416, 33, 33, 33, 33, 33, 28, 8224, 33, 28, 8224, 33, 28, 33, 28, 8224, 33, 3, 33, 15, 33, 33, 33, 33, 33, 30, 8224, 33, 16416, 33, 33, 33, 8217, 8224, 33, 8217, 8224, 33, 8217, 33, 8217, 8224, 33, 33, 31, 8224, 33, 31, 8224, 33, 31, 33, 31, 8224, 8216, 8224, 33, 8216, 8224, 33, 8216, 33, 8216, 8224, 33, 8219, 8224, 33, 8219, 8224, 33, 8219, 33, 8219, 8224, 33, 33, 10, 33, 33, 28, 8224, 33, 28, 8224, 33, 28, 8224, 28, 33, 28, 33, 3, 33, 33, 33, 33, 33, 33, 33, 30, 8224, 33, 30, 8224, 33, 30, 33, 30, 8224, 33, 33, 29, 8224, 33,16416, 8217, 8224, 33, 8217, 8224, 33, 8217, 8224, 8217, 33, 8217, 33, 33, 31, 8224, 33, 31, 8224, 33, 31, 8224, 31, 33, 31, 8216, 8224, 33, 8216, 8224, 33, 8216, 8224, 8216, 33, 8216, 33, 8219, 8224, 33, 8219, 8224, 33, 8219, 8224, 8219, 33, 8219, 33, 33, 10, 23, 10, 7, 33, 33, 33, 33, 33, 33, 33, 13, 33, 30, 8224, 33, 30, 8224, 33, 30, 8224, 30, 33, 30, 2, 33, 29, 8224, 33, 29, 8224, 33, 29, 33, 29, 8224, 16, 33, 33, 11, 33, 22, 10, 10, 23, 7, 23, 7, 33, 8, 33, 33, 33, 33, 6, 33, 13, 33, 2, 23, 2, 33, 29, 8224, 33, 29, 8224, 33, 29, 8224, 29, 33, 29, 16, 33, 33, 11, 23, 11, 26, 8224, 33,16416, 22, 23, 22, 7, 7, 23, 33, 8, 23, 8, 33, 33, 33, 33, 6, 23, 6, 6, 23, 6, 23, 33, 2, 2, 23, 33, 33, 11, 11, 23, 26, 8224, 33, 26, 8224, 33, 26, 33, 26, 8224, 22, 23, 33, 8, 8, 23, 33, 33, 17, 18, 6, 6, 23, 6, 6, 33, 33, 14, 33, 26, 8224, 33, 26, 8224, 33, 26, 8224, 26, 33, 26, 33, 33, 33, 17, 23, 17, 18, 23, 18, 6, 6, 33, 33, 14, 33, 20, 9, 19, 17, 17, 23, 18, 18, 23, 6, 5, 6, 33, 21, 20, 23, 20, 9, 23, 9, 19, 23, 19, 4, 6, 5, 6, 33, 21, 23, 21, 20, 20, 23, 9, 9, 23, 19, 19, 23, 4, 6, 12, 33, 21, 21, 23, 12, 33 } ; static yyconst flex_int16_t yy_accept[356] = { 0, 1, 1, 1, 2, 3, 4, 7, 12, 16, 19, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 71, 72, 73, 74, 76, 79, 81, 82, 83, 84, 87, 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, 113, 115, 116, 118, 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, 148, 150, 151, 152, 153, 154, 158, 159, 160, 162, 163, 164, 165, 166, 167, 168, 169, 170, 175, 176, 177, 181, 182, 187, 188, 189, 194, 195, 196, 197, 198, 199, 202, 205, 207, 209, 210, 212, 214, 215, 216, 217, 218, 222, 223, 224, 225, 228, 231, 233, 235, 236, 237, 240, 243, 245, 247, 250, 253, 255, 257, 258, 261, 264, 266, 268, 269, 270, 271, 272, 273, 276, 279, 281, 283, 284, 285, 287, 288, 289, 290, 291, 292, 293, 296, 299, 301, 303, 304, 305, 309, 312, 315, 317, 319, 320, 321, 322, 325, 328, 330, 332, 333, 336, 339, 341, 343, 344, 345, 348, 351, 353, 355, 356, 357, 358, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 371, 374, 377, 379, 381, 382, 383, 384, 387, 390, 392, 394, 396, 397, 398, 399, 400, 401, 403, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 416, 418, 419, 420, 423, 426, 428, 430, 431, 433, 434, 436, 437, 441, 443, 444, 445, 447, 448, 450, 451, 452, 453, 454, 455, 457, 458, 460, 462, 463, 464, 466, 467, 468, 469, 471, 474, 477, 479, 481, 483, 484, 485, 487, 488, 489, 490, 491, 492, 494, 495, 496, 497, 498, 500, 503, 506, 508, 510, 511, 512, 513, 514, 516, 517, 519, 520, 521, 522, 523, 524, 526, 527, 528, 529, 530, 532, 533, 535, 536, 538, 539, 540, 542, 543, 545, 546, 548, 549, 551, 553, 554, 556, 557, 558, 560, 561, 563, 564, 566, 568, 570, 571, 573, 575, 575 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 1, 1, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 8, 1, 1, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[35] = { 0, 1, 1, 2, 3, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int16_t yy_base[424] = { 0, 0, 0, 901, 900, 902, 897, 33, 36, 905, 905, 45, 63, 31, 42, 51, 52, 890, 33, 65, 67, 69, 70, 889, 71, 888, 75, 0, 905, 893, 905, 91, 94, 0, 0, 103, 886, 112, 0, 89, 98, 113, 92, 114, 99, 100, 48, 121, 116, 119, 74, 124, 129, 123, 135, 132, 133, 137, 134, 138, 139, 141, 0, 155, 0, 0, 164, 0, 0, 849, 142, 152, 164, 140, 161, 165, 166, 167, 168, 169, 173, 174, 178, 176, 180, 184, 208, 189, 183, 192, 195, 215, 191, 193, 223, 0, 0, 905, 208, 204, 236, 219, 209, 238, 196, 237, 831, 242, 815, 241, 224, 243, 261, 244, 259, 277, 266, 286, 250, 288, 298, 249, 283, 274, 282, 294, 308, 0, 310, 0, 295, 305, 905, 308, 306, 313, 314, 342, 319, 316, 320, 331, 0, 349, 0, 342, 344, 356, 0, 358, 0, 365, 0, 367, 0, 354, 375, 0, 377, 0, 363, 356, 809, 327, 322, 384, 0, 0, 0, 0, 379, 905, 382, 384, 386, 390, 372, 392, 403, 0, 410, 0, 407, 413, 423, 426, 0, 0, 0, 0, 409, 424, 435, 0, 0, 0, 0, 437, 0, 0, 0, 0, 433, 444, 0, 0, 0, 0, 391, 440, 781, 905, 769, 439, 445, 444, 447, 449, 454, 453, 399, 464, 0, 0, 0, 0, 757, 465, 476, 0, 478, 0, 479, 476, 753, 462, 490, 749, 905, 745, 905, 483, 737, 424, 485, 487, 490, 500, 493, 905, 729, 905, 502, 518, 0, 0, 0, 0, 905, 498, 721, 905, 527, 713, 0, 705, 905, 495, 697, 905, 365, 521, 528, 530, 685, 905, 534, 540, 540, 657, 905, 537, 542, 650, 905, 553, 0, 557, 0, 0, 551, 641, 905, 558, 557, 633, 614, 613, 905, 547, 555, 563, 565, 569, 584, 0, 0, 0, 0, 583, 570, 585, 612, 905, 601, 905, 522, 580, 589, 594, 905, 600, 585, 563, 520, 905, 514, 905, 586, 486, 597, 480, 441, 905, 416, 905, 345, 905, 334, 905, 601, 254, 905, 242, 905, 200, 905, 151, 905, 905, 607, 86, 905, 905, 905, 620, 624, 627, 631, 635, 639, 643, 647, 651, 655, 659, 663, 667, 671, 675, 679, 683, 687, 691, 695, 699, 703, 707, 711, 715, 719, 723, 727, 731, 735, 739, 743, 747, 751, 755, 759, 763, 767, 771, 775, 779, 783, 787, 791, 795, 799, 803, 807, 811, 815, 819, 823, 827, 831, 835, 839, 843, 847, 851, 855, 859, 863, 867, 871, 875, 879, 883, 887, 891 } ; static yyconst flex_int16_t yy_def[424] = { 0, 354, 1, 355, 355, 354, 356, 357, 357, 354, 354, 358, 358, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 359, 354, 356, 354, 360, 357, 361, 361, 362, 12, 356, 363, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 359, 360, 361, 361, 364, 365, 365, 354, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 362, 12, 12, 12, 12, 12, 12, 12, 364, 365, 365, 354, 12, 12, 366, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 367, 86, 86, 368, 12, 369, 12, 12, 370, 12, 12, 12, 12, 12, 371, 372, 366, 372, 12, 12, 354, 86, 12, 12, 12, 373, 12, 12, 12, 374, 375, 367, 375, 86, 86, 376, 377, 368, 377, 378, 379, 369, 379, 12, 380, 381, 370, 381, 12, 12, 382, 12, 12, 371, 372, 372, 383, 383, 12, 354, 86, 86, 86, 12, 12, 12, 384, 385, 373, 385, 12, 12, 386, 374, 375, 375, 387, 387, 86, 86, 376, 377, 377, 388, 388, 378, 379, 379, 389, 389, 12, 380, 381, 381, 390, 390, 12, 12, 391, 354, 392, 86, 12, 86, 86, 86, 12, 86, 12, 384, 385, 385, 393, 393, 394, 86, 395, 396, 386, 396, 86, 86, 397, 12, 398, 391, 354, 399, 354, 86, 400, 12, 86, 86, 86, 401, 86, 354, 402, 354, 86, 395, 396, 396, 403, 403, 354, 86, 404, 354, 405, 406, 406, 399, 354, 86, 407, 354, 12, 86, 86, 86, 408, 354, 408, 408, 86, 402, 354, 86, 86, 404, 354, 409, 410, 405, 410, 406, 86, 407, 354, 12, 86, 411, 412, 408, 354, 408, 408, 86, 86, 86, 409, 410, 410, 413, 413, 86, 12, 86, 414, 354, 415, 354, 408, 408, 86, 86, 354, 416, 417, 418, 414, 354, 415, 354, 408, 408, 86, 419, 420, 354, 421, 354, 422, 354, 408, 354, 86, 423, 354, 420, 354, 421, 354, 422, 354, 354, 86, 423, 354, 354, 0, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354 } ; static yyconst flex_int16_t yy_nxt[940] = { 0, 6, 7, 8, 9, 6, 6, 6, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 17, 17, 17, 17, 20, 17, 21, 22, 23, 24, 25, 17, 26, 17, 17, 17, 32, 32, 33, 32, 32, 33, 36, 34, 36, 42, 34, 29, 29, 29, 30, 35, 50, 36, 37, 38, 43, 44, 39, 36, 79, 45, 36, 36, 40, 29, 29, 29, 30, 35, 46, 48, 37, 38, 41, 47, 36, 49, 36, 53, 36, 36, 36, 56, 58, 36, 36, 55, 82, 60, 51, 342, 54, 61, 52, 29, 64, 32, 32, 33, 36, 65, 70, 36, 34, 29, 29, 29, 30, 36, 36, 36, 29, 38, 66, 66, 66, 67, 66, 71, 74, 66, 68, 72, 36, 36, 73, 36, 77, 78, 36, 76, 36, 53, 36, 36, 75, 85, 80, 83, 36, 86, 84, 36, 36, 36, 36, 81, 36, 36, 36, 36, 36, 36, 93, 89, 337, 98, 88, 29, 64, 101, 90, 36, 91, 65, 92, 87, 29, 95, 89, 99, 36, 100, 96, 36, 36, 36, 36, 36, 36, 106, 105, 85, 36, 36, 102, 36, 107, 36, 103, 36, 109, 112, 36, 36, 104, 108, 115, 110, 36, 117, 36, 36, 36, 335, 36, 36, 122, 111, 29, 29, 29, 30, 118, 36, 116, 29, 38, 36, 36, 113, 114, 119, 120, 123, 36, 29, 95, 121, 36, 134, 131, 96, 130, 36, 125, 124, 126, 126, 66, 127, 126, 132, 133, 126, 129, 333, 36, 36, 135, 137, 36, 36, 36, 140, 139, 35, 35, 352, 36, 36, 85, 141, 141, 66, 142, 141, 160, 145, 141, 144, 35, 35, 89, 117, 155, 36, 146, 147, 147, 66, 148, 147, 162, 36, 147, 150, 151, 151, 66, 152, 151, 36, 36, 151, 154, 120, 161, 36, 156, 156, 66, 157, 156, 36, 36, 156, 159, 164, 171, 163, 29, 166, 29, 168, 36, 36, 167, 170, 169, 35, 35, 172, 36, 36, 173, 36, 213, 184, 36, 36, 175, 36, 174, 29, 186, 212, 36, 349, 183, 187, 177, 176, 178, 178, 66, 179, 178, 182, 348, 178, 181, 29, 188, 35, 35, 35, 35, 189, 29, 193, 29, 195, 190, 36, 194, 36, 196, 29, 198, 29, 200, 191, 36, 199, 36, 201, 219, 29, 204, 29, 206, 36, 202, 205, 209, 207, 29, 166, 36, 293, 208, 214, 167, 35, 35, 35, 35, 35, 35, 36, 36, 36, 249, 218, 220, 29, 222, 216, 36, 217, 235, 223, 29, 224, 215, 226, 36, 227, 225, 346, 35, 35, 36, 228, 228, 66, 229, 228, 29, 186, 228, 231, 232, 36, 187, 233, 35, 29, 193, 29, 198, 234, 36, 194, 344, 199, 29, 204, 236, 36, 35, 241, 205, 242, 36, 35, 35, 270, 35, 35, 35, 35, 247, 36, 35, 35, 29, 222, 244, 262, 248, 36, 223, 243, 245, 246, 35, 252, 29, 254, 29, 256, 258, 342, 255, 259, 257, 35, 35, 339, 35, 35, 69, 264, 35, 35, 35, 35, 35, 35, 267, 35, 35, 275, 35, 35, 35, 35, 271, 35, 35, 276, 277, 35, 35, 272, 278, 315, 273, 281, 29, 254, 290, 313, 282, 275, 255, 285, 285, 66, 286, 285, 35, 35, 285, 288, 295, 298, 296, 35, 35, 35, 35, 298, 301, 328, 299, 294, 35, 35, 275, 35, 35, 35, 303, 29, 305, 300, 275, 29, 307, 306, 35, 35, 302, 308, 337, 36, 35, 35, 309, 310, 320, 316, 35, 35, 35, 35, 322, 36, 35, 35, 317, 275, 319, 311, 29, 305, 335, 275, 318, 321, 306, 323, 35, 35, 35, 35, 330, 329, 35, 35, 331, 333, 327, 35, 35, 338, 35, 35, 353, 340, 35, 35, 350, 325, 275, 315, 35, 35, 27, 27, 27, 27, 29, 29, 29, 31, 31, 31, 31, 36, 36, 36, 36, 62, 313, 62, 62, 63, 63, 63, 63, 65, 269, 65, 65, 35, 35, 35, 35, 69, 69, 261, 69, 94, 94, 94, 94, 96, 251, 96, 96, 128, 128, 128, 128, 143, 143, 143, 143, 149, 149, 149, 149, 153, 153, 153, 153, 158, 158, 158, 158, 165, 165, 165, 165, 167, 298, 167, 167, 180, 180, 180, 180, 185, 185, 185, 185, 187, 292, 187, 187, 192, 192, 192, 192, 194, 240, 194, 194, 197, 197, 197, 197, 199, 289, 199, 199, 203, 203, 203, 203, 205, 284, 205, 205, 210, 210, 210, 210, 169, 280, 169, 169, 221, 221, 221, 221, 223, 269, 223, 223, 230, 230, 230, 230, 189, 266, 189, 189, 196, 211, 196, 196, 201, 261, 201, 201, 207, 251, 207, 207, 237, 237, 237, 237, 239, 239, 239, 239, 225, 240, 225, 225, 250, 250, 250, 250, 253, 253, 253, 253, 255, 238, 255, 255, 260, 260, 260, 260, 263, 263, 263, 263, 265, 265, 265, 265, 268, 268, 268, 268, 274, 274, 274, 274, 279, 279, 279, 279, 257, 211, 257, 257, 283, 283, 283, 283, 287, 287, 287, 287, 264, 138, 264, 264, 291, 291, 291, 291, 297, 297, 297, 297, 304, 304, 304, 304, 306, 136, 306, 306, 312, 312, 312, 312, 314, 314, 314, 314, 308, 97, 308, 308, 324, 324, 324, 324, 326, 326, 326, 326, 332, 332, 332, 332, 334, 334, 334, 334, 336, 336, 336, 336, 341, 341, 341, 341, 343, 343, 343, 343, 345, 345, 345, 345, 347, 347, 347, 347, 351, 351, 351, 351, 36, 30, 59, 57, 36, 30, 354, 28, 28, 5, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354 } ; static yyconst flex_int16_t yy_chk[940] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 7, 7, 8, 8, 8, 13, 7, 18, 13, 8, 11, 11, 11, 11, 11, 18, 14, 11, 11, 13, 14, 11, 46, 46, 14, 15, 16, 11, 12, 12, 12, 12, 12, 14, 16, 12, 12, 12, 15, 19, 16, 20, 20, 21, 22, 24, 22, 24, 50, 26, 21, 50, 26, 19, 351, 20, 26, 19, 31, 31, 32, 32, 32, 39, 31, 39, 42, 32, 35, 35, 35, 35, 40, 44, 45, 35, 35, 37, 37, 37, 37, 37, 39, 42, 37, 37, 40, 41, 43, 41, 48, 45, 45, 49, 44, 47, 47, 53, 51, 43, 53, 48, 51, 52, 54, 52, 55, 56, 58, 54, 49, 57, 59, 60, 73, 61, 70, 60, 61, 347, 70, 56, 63, 63, 73, 58, 71, 59, 63, 59, 55, 66, 66, 57, 71, 74, 72, 66, 72, 75, 76, 77, 78, 79, 78, 77, 79, 80, 81, 74, 83, 80, 82, 75, 84, 82, 85, 88, 85, 76, 81, 87, 83, 87, 89, 92, 89, 93, 345, 90, 104, 92, 84, 86, 86, 86, 86, 90, 99, 88, 86, 86, 98, 102, 86, 86, 91, 91, 93, 91, 94, 94, 91, 101, 104, 102, 94, 101, 110, 99, 98, 100, 100, 100, 100, 100, 103, 103, 100, 100, 343, 105, 103, 105, 107, 109, 107, 111, 110, 109, 113, 113, 341, 121, 118, 111, 112, 112, 112, 112, 112, 121, 113, 112, 112, 114, 114, 116, 116, 118, 116, 114, 115, 115, 115, 115, 115, 123, 123, 115, 115, 117, 117, 117, 117, 117, 124, 122, 117, 117, 119, 122, 119, 120, 120, 120, 120, 120, 125, 130, 120, 120, 125, 131, 124, 126, 126, 128, 128, 131, 134, 126, 130, 128, 133, 133, 133, 135, 136, 133, 139, 164, 140, 138, 140, 134, 164, 133, 141, 141, 163, 163, 338, 139, 141, 136, 135, 137, 137, 137, 137, 137, 138, 336, 137, 137, 143, 143, 145, 145, 146, 146, 143, 147, 147, 149, 149, 145, 155, 147, 161, 149, 151, 151, 153, 153, 146, 160, 151, 270, 153, 176, 156, 156, 158, 158, 176, 155, 156, 161, 158, 165, 165, 170, 270, 160, 170, 165, 172, 172, 173, 173, 174, 174, 175, 208, 177, 220, 175, 177, 178, 178, 173, 220, 174, 208, 178, 180, 180, 172, 182, 182, 183, 180, 334, 190, 190, 183, 184, 184, 184, 184, 184, 185, 185, 184, 184, 190, 243, 185, 191, 191, 192, 192, 197, 197, 202, 202, 192, 332, 197, 203, 203, 209, 209, 213, 213, 203, 214, 214, 215, 215, 243, 216, 216, 217, 217, 218, 218, 219, 219, 221, 221, 215, 235, 219, 235, 221, 214, 216, 217, 227, 227, 228, 228, 230, 230, 232, 331, 228, 233, 230, 233, 233, 329, 232, 232, 236, 236, 241, 241, 244, 244, 245, 245, 241, 246, 246, 247, 248, 248, 267, 267, 244, 259, 259, 247, 247, 252, 252, 245, 248, 326, 246, 252, 253, 253, 267, 324, 259, 316, 253, 262, 262, 262, 262, 262, 271, 271, 262, 262, 272, 276, 273, 272, 272, 273, 273, 277, 278, 316, 276, 271, 281, 281, 299, 278, 278, 282, 282, 285, 285, 277, 300, 287, 287, 285, 290, 290, 281, 287, 323, 293, 294, 294, 290, 293, 303, 299, 301, 301, 302, 302, 310, 310, 303, 303, 300, 317, 302, 294, 304, 304, 322, 328, 301, 309, 304, 311, 309, 309, 311, 311, 318, 317, 318, 318, 319, 321, 314, 319, 319, 328, 330, 330, 350, 330, 340, 340, 340, 312, 297, 296, 350, 350, 355, 355, 355, 355, 356, 356, 356, 357, 357, 357, 357, 358, 358, 358, 358, 359, 295, 359, 359, 360, 360, 360, 360, 361, 291, 361, 361, 362, 362, 362, 362, 363, 363, 283, 363, 364, 364, 364, 364, 365, 279, 365, 365, 366, 366, 366, 366, 367, 367, 367, 367, 368, 368, 368, 368, 369, 369, 369, 369, 370, 370, 370, 370, 371, 371, 371, 371, 372, 274, 372, 372, 373, 373, 373, 373, 374, 374, 374, 374, 375, 268, 375, 375, 376, 376, 376, 376, 377, 265, 377, 377, 378, 378, 378, 378, 379, 263, 379, 379, 380, 380, 380, 380, 381, 260, 381, 381, 382, 382, 382, 382, 383, 250, 383, 383, 384, 384, 384, 384, 385, 242, 385, 385, 386, 386, 386, 386, 387, 239, 387, 387, 388, 237, 388, 388, 389, 234, 389, 389, 390, 226, 390, 390, 391, 391, 391, 391, 392, 392, 392, 392, 393, 212, 393, 393, 394, 394, 394, 394, 395, 395, 395, 395, 396, 210, 396, 396, 397, 397, 397, 397, 398, 398, 398, 398, 399, 399, 399, 399, 400, 400, 400, 400, 401, 401, 401, 401, 402, 402, 402, 402, 403, 162, 403, 403, 404, 404, 404, 404, 405, 405, 405, 405, 406, 108, 406, 406, 407, 407, 407, 407, 408, 408, 408, 408, 409, 409, 409, 409, 410, 106, 410, 410, 411, 411, 411, 411, 412, 412, 412, 412, 413, 69, 413, 413, 414, 414, 414, 414, 415, 415, 415, 415, 416, 416, 416, 416, 417, 417, 417, 417, 418, 418, 418, 418, 419, 419, 419, 419, 420, 420, 420, 420, 421, 421, 421, 421, 422, 422, 422, 422, 423, 423, 423, 423, 36, 29, 25, 23, 17, 6, 5, 4, 3, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354 } ; #define YY_TRAILING_MASK 0x2000 #define YY_TRAILING_HEAD_MASK 0x4000 #define REJECT \ { \ *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ \ yy_cp = yyg->yy_full_match; /* restore poss. backed-over text */ \ yyg->yy_lp = yyg->yy_full_lp; /* restore orig. accepting pos. */ \ yyg->yy_state_ptr = yyg->yy_full_state; /* restore orig. state */ \ yy_current_state = *yyg->yy_state_ptr; /* restore curr. state */ \ ++yyg->yy_lp; \ goto find_rule; \ } #define yymore() (yyg->yy_more_flag = 1) #define YY_MORE_ADJ yyg->yy_more_len #define YY_RESTORE_YY_MORE_OFFSET #line 1 "libxlu_disk_l.l" /* -*- fundamental -*- */ /* * libxlu_disk_l.l - parser for disk specification strings * * Copyright (C) 2011 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * Parsing the old xm/xend/xl-4.1 disk specs is a tricky problem, * because the target string might in theory contain "," which is the * delimiter we use for stripping off things on the RHS, and ":", * which is the delimiter we use for stripping off things on the LHS. * * In this parser we do not support such target strings in the old * syntax; if the target string has to contain "," or ":" the new * syntax's "target=" should be used. */ #line 35 "libxlu_disk_l.l" #include "libxlu_disk_i.h" #define YY_NO_INPUT /* Some versions of flex have a bug (Fedora bugzilla 612465) which causes * it to fail to declare these functions, which it defines. So declare * them ourselves. Hopefully we won't have to simultaneously support * a flex version which declares these differently somehow. */ int xlu__disk_yyget_column(yyscan_t yyscanner); void xlu__disk_yyset_column(int column_no, yyscan_t yyscanner); /*----- useful macros and functions used in actions ----- * we use macros in the actual rules to keep the actions short * and particularly to avoid repeating boilerplate values such as * DPC->disk, yytext, etc. */ /* Sets an enum, checking it hasn't already been set to a different value */ #define DSET(dpc,member,enumname,str,valname) do{ \ if (dpc->disk->member != LIBXL_DISK_##enumname##_UNKNOWN && \ dpc->disk->member != LIBXL_DISK_##enumname##_##valname) { \ xlu__disk_err(dpc, str, TOSTRING(member) " respecified"); \ } else { \ dpc->disk->member = LIBXL_DISK_##enumname##_##valname; \ } \ }while(0) /* For actions whose patterns contain '=', finds the start of the value */ #define FROMEQUALS (strchr(yytext,'=')+1) /* Chops the delimiter off, modifying yytext and yyleng. */ #define STRIP(delim) do{ \ if (yyleng>0 && yytext[yyleng-1]==(delim)) \ yytext[--yyleng] = 0; \ }while(0) /* Sets a string value, checking it hasn't been set already. */ #define SAVESTRING(what,loc,val) do{ \ savestring(DPC, what " respecified", &DPC->disk->loc, (val)); \ }while(0) static void savestring(DiskParseContext *dpc, const char *what_respecified, char **update, const char *value) { if (*update) { if (**update) { xlu__disk_err(dpc,value,what_respecified); return; } free(*update); /* do not complain about overwriting empty strings */ } *update = strdup(value); } #define DPC dpc /* our convention in lexer helper functions */ /* Sets ->readwrite from the string. This ought to be an enum, perhaps. */ static void setaccess(DiskParseContext *dpc, const char *str) { if (!strcmp(str, "r") || !strcmp(str, "ro")) { dpc->disk->readwrite = 0; } else if (!strcmp(str, "rw") || !strcmp(str, "w") || !strcmp(str,"")) { dpc->disk->readwrite = 1; } else { xlu__disk_err(dpc,str,"unknown value for access"); } } /* Sets ->format from the string. IDL should provide something for this. */ static void setformat(DiskParseContext *dpc, const char *str) { if (!strcmp(str,"")) DSET(dpc,format,FORMAT,str,RAW); else if (!strcmp(str,"raw")) DSET(dpc,format,FORMAT,str,RAW); else if (!strcmp(str,"qcow")) DSET(dpc,format,FORMAT,str,QCOW); else if (!strcmp(str,"qcow2")) DSET(dpc,format,FORMAT,str,QCOW2); else if (!strcmp(str,"vhd")) DSET(dpc,format,FORMAT,str,VHD); else if (!strcmp(str,"empty")) DSET(dpc,format,FORMAT,str,EMPTY); else if (!strcmp(str,"qed")) DSET(dpc,format,FORMAT,str,QED); else xlu__disk_err(dpc,str,"unknown value for format"); } /* Sets ->backend from the string. IDL should provide something for this. */ static void setbackendtype(DiskParseContext *dpc, const char *str) { if ( !strcmp(str,"phy")) DSET(dpc,backend,BACKEND,str,PHY); else if (!strcmp(str,"tap")) DSET(dpc,backend,BACKEND,str,TAP); else if (!strcmp(str,"qdisk")) DSET(dpc,backend,BACKEND,str,QDISK); else xlu__disk_err(dpc,str,"unknown value for backendtype"); } /* Sets ->colo-port from the string. COLO need this. */ static void setcoloport(DiskParseContext *dpc, const char *str) { int port = atoi(str); if (port) { dpc->disk->colo_port = port; } else { xlu__disk_err(dpc,str,"unknown value for colo_port"); } } #define DEPRECATE(usewhatinstead) /* not currently reported */ /* Handles a vdev positional parameter which includes a devtype. */ static int vdev_and_devtype(DiskParseContext *dpc, char *str) { /* returns 1 if it was :, 0 (doing nothing) otherwise */ char *colon = strrchr(str, ':'); if (!colon) return 0; DEPRECATE("use `devtype=...'"); *colon++ = 0; SAVESTRING("vdev", vdev, str); if (!strcmp(colon,"cdrom")) { DPC->disk->is_cdrom = 1; } else if (!strcmp(colon,"disk")) { DPC->disk->is_cdrom = 0; } else { xlu__disk_err(DPC,colon,"unknown deprecated type"); } return 1; } #undef DPC /* needs to be defined differently the actual lexer */ #define DPC ((DiskParseContext*)yyextra) #line 1006 "libxlu_disk_l.c" #define INITIAL 0 #define LEXERR 1 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; yy_size_t yy_n_chars; yy_size_t yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; yy_state_type *yy_state_buf; yy_state_type *yy_state_ptr; char *yy_full_match; int yy_lp; /* These are only needed for trailing context rules, * but there's no conditional variable for that yet. */ int yy_looking_for_trail_begin; int yy_full_lp; int *yy_full_state; char *yytext_r; int yy_more_flag; int yy_more_len; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); int xlu__disk_yylex_init (yyscan_t* scanner); int xlu__disk_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int xlu__disk_yylex_destroy (yyscan_t yyscanner ); int xlu__disk_yyget_debug (yyscan_t yyscanner ); void xlu__disk_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE xlu__disk_yyget_extra (yyscan_t yyscanner ); void xlu__disk_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *xlu__disk_yyget_in (yyscan_t yyscanner ); void xlu__disk_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *xlu__disk_yyget_out (yyscan_t yyscanner ); void xlu__disk_yyset_out (FILE * out_str ,yyscan_t yyscanner ); yy_size_t xlu__disk_yyget_leng (yyscan_t yyscanner ); char *xlu__disk_yyget_text (yyscan_t yyscanner ); int xlu__disk_yyget_lineno (yyscan_t yyscanner ); void xlu__disk_yyset_lineno (int line_number ,yyscan_t yyscanner ); int xlu__disk_yyget_column (yyscan_t yyscanner ); void xlu__disk_yyset_column (int column_no ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int xlu__disk_yywrap (yyscan_t yyscanner ); #else extern int xlu__disk_yywrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int xlu__disk_yylex (yyscan_t yyscanner); #define YY_DECL int xlu__disk_yylex (yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif /* Create the reject buffer large enough to save one state per allowed character. */ if ( ! yyg->yy_state_buf ) yyg->yy_state_buf = (yy_state_type *)xlu__disk_yyalloc(YY_STATE_BUF_SIZE ,yyscanner); if ( ! yyg->yy_state_buf ) YY_FATAL_ERROR( "out of dynamic memory in xlu__disk_yylex()" ); if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { xlu__disk_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = xlu__disk_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } xlu__disk_yy_load_buffer_state(yyscanner ); } { #line 166 "libxlu_disk_l.l" /*----- the scanner rules which do the parsing -----*/ #line 1284 "libxlu_disk_l.c" while ( 1 ) /* loops until end-of-file is reached */ { yyg->yy_more_len = 0; if ( yyg->yy_more_flag ) { yyg->yy_more_len = yyg->yy_c_buf_p - yyg->yytext_ptr; yyg->yy_more_flag = 0; } yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yyg->yy_state_ptr = yyg->yy_state_buf; *yyg->yy_state_ptr++ = yy_current_state; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 355 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; *yyg->yy_state_ptr++ = yy_current_state; ++yy_cp; } while ( yy_current_state != 354 ); yy_find_action: yy_current_state = *--yyg->yy_state_ptr; yyg->yy_lp = yy_accept[yy_current_state]; find_rule: /* we branch to this label when backing up */ for ( ; ; ) /* until we find what rule we matched */ { if ( yyg->yy_lp && yyg->yy_lp < yy_accept[yy_current_state + 1] ) { yy_act = yy_acclist[yyg->yy_lp]; if ( yy_act & YY_TRAILING_HEAD_MASK || yyg->yy_looking_for_trail_begin ) { if ( yy_act == yyg->yy_looking_for_trail_begin ) { yyg->yy_looking_for_trail_begin = 0; yy_act &= ~YY_TRAILING_HEAD_MASK; break; } } else if ( yy_act & YY_TRAILING_MASK ) { yyg->yy_looking_for_trail_begin = yy_act & ~YY_TRAILING_MASK; yyg->yy_looking_for_trail_begin |= YY_TRAILING_HEAD_MASK; } else { yyg->yy_full_match = yy_cp; yyg->yy_full_state = yyg->yy_state_ptr; yyg->yy_full_lp = yyg->yy_lp; break; } ++yyg->yy_lp; goto find_rule; } --yy_cp; yy_current_state = *--yyg->yy_state_ptr; yyg->yy_lp = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 1: /* rule 1 can match eol */ YY_RULE_SETUP #line 170 "libxlu_disk_l.l" { /* ignore whitespace before parameters */ } YY_BREAK /* ordinary parameters setting enums or strings */ case 2: /* rule 2 can match eol */ YY_RULE_SETUP #line 174 "libxlu_disk_l.l" { STRIP(','); setformat(DPC, FROMEQUALS); } YY_BREAK case 3: YY_RULE_SETUP #line 176 "libxlu_disk_l.l" { DPC->disk->is_cdrom = 1; } YY_BREAK case 4: YY_RULE_SETUP #line 177 "libxlu_disk_l.l" { DPC->disk->is_cdrom = 1; } YY_BREAK case 5: YY_RULE_SETUP #line 178 "libxlu_disk_l.l" { DPC->disk->is_cdrom = 0; } YY_BREAK case 6: /* rule 6 can match eol */ YY_RULE_SETUP #line 179 "libxlu_disk_l.l" { xlu__disk_err(DPC,yytext,"unknown value for type"); } YY_BREAK case 7: /* rule 7 can match eol */ YY_RULE_SETUP #line 181 "libxlu_disk_l.l" { STRIP(','); setaccess(DPC, FROMEQUALS); } YY_BREAK case 8: /* rule 8 can match eol */ YY_RULE_SETUP #line 182 "libxlu_disk_l.l" { STRIP(','); SAVESTRING("backend", backend_domname, FROMEQUALS); } YY_BREAK case 9: /* rule 9 can match eol */ YY_RULE_SETUP #line 183 "libxlu_disk_l.l" { STRIP(','); setbackendtype(DPC,FROMEQUALS); } YY_BREAK case 10: /* rule 10 can match eol */ YY_RULE_SETUP #line 185 "libxlu_disk_l.l" { STRIP(','); SAVESTRING("vdev", vdev, FROMEQUALS); } YY_BREAK case 11: /* rule 11 can match eol */ YY_RULE_SETUP #line 186 "libxlu_disk_l.l" { STRIP(','); SAVESTRING("script", script, FROMEQUALS); } YY_BREAK case 12: YY_RULE_SETUP #line 187 "libxlu_disk_l.l" { DPC->disk->direct_io_safe = 1; } YY_BREAK case 13: YY_RULE_SETUP #line 188 "libxlu_disk_l.l" { libxl_defbool_set(&DPC->disk->discard_enable, true); } YY_BREAK case 14: YY_RULE_SETUP #line 189 "libxlu_disk_l.l" { libxl_defbool_set(&DPC->disk->discard_enable, false); } YY_BREAK /* Note that the COLO configuration settings should be considered unstable. * They may change incompatibly in future versions of Xen. */ case 15: YY_RULE_SETUP #line 192 "libxlu_disk_l.l" { libxl_defbool_set(&DPC->disk->colo_enable, true); } YY_BREAK case 16: YY_RULE_SETUP #line 193 "libxlu_disk_l.l" { libxl_defbool_set(&DPC->disk->colo_enable, false); } YY_BREAK case 17: /* rule 17 can match eol */ YY_RULE_SETUP #line 194 "libxlu_disk_l.l" { STRIP(','); SAVESTRING("colo-host", colo_host, FROMEQUALS); } YY_BREAK case 18: /* rule 18 can match eol */ YY_RULE_SETUP #line 195 "libxlu_disk_l.l" { STRIP(','); setcoloport(DPC, FROMEQUALS); } YY_BREAK case 19: /* rule 19 can match eol */ YY_RULE_SETUP #line 196 "libxlu_disk_l.l" { STRIP(','); SAVESTRING("colo-export", colo_export, FROMEQUALS); } YY_BREAK case 20: /* rule 20 can match eol */ YY_RULE_SETUP #line 197 "libxlu_disk_l.l" { STRIP(','); SAVESTRING("active-disk", active_disk, FROMEQUALS); } YY_BREAK case 21: /* rule 21 can match eol */ YY_RULE_SETUP #line 198 "libxlu_disk_l.l" { STRIP(','); SAVESTRING("hidden-disk", hidden_disk, FROMEQUALS); } YY_BREAK /* the target magic parameter, eats the rest of the string */ case 22: YY_RULE_SETUP #line 202 "libxlu_disk_l.l" { STRIP(','); SAVESTRING("target", pdev_path, FROMEQUALS); } YY_BREAK /* unknown parameters */ case 23: /* rule 23 can match eol */ YY_RULE_SETUP #line 206 "libxlu_disk_l.l" { xlu__disk_err(DPC,yytext,"unknown parameter"); } YY_BREAK /* deprecated prefixes */ /* the "/.*" in these patterns ensures that they count as if they * matched the whole string, so these patterns take precedence */ case 24: YY_RULE_SETUP #line 213 "libxlu_disk_l.l" { STRIP(':'); DPC->had_depr_prefix=1; DEPRECATE("use `[format=]...,'"); setformat(DPC, yytext); } YY_BREAK case 25: YY_RULE_SETUP #line 219 "libxlu_disk_l.l" { char *newscript; STRIP(':'); DPC->had_depr_prefix=1; DEPRECATE("use `script=...'"); if (asprintf(&newscript, "block-%s", yytext) < 0) { xlu__disk_err(DPC,yytext,"unable to format script"); return 0; } savestring(DPC, "script respecified", &DPC->disk->script, newscript); free(newscript); } YY_BREAK case 26: *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 8; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 232 "libxlu_disk_l.l" { DPC->had_depr_prefix=1; DEPRECATE(0); } YY_BREAK case 27: YY_RULE_SETUP #line 233 "libxlu_disk_l.l" { DPC->had_depr_prefix=1; DEPRECATE(0); } YY_BREAK case 28: *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 4; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 234 "libxlu_disk_l.l" { DPC->had_depr_prefix=1; DEPRECATE(0); } YY_BREAK case 29: *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 6; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 235 "libxlu_disk_l.l" { DPC->had_depr_prefix=1; DEPRECATE(0); } YY_BREAK case 30: *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 5; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 236 "libxlu_disk_l.l" { DPC->had_depr_prefix=1; DEPRECATE(0); } YY_BREAK case 31: *yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ yyg->yy_c_buf_p = yy_cp = yy_bp + 4; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP #line 237 "libxlu_disk_l.l" { DPC->had_depr_prefix=1; DEPRECATE(0); } YY_BREAK case 32: /* rule 32 can match eol */ YY_RULE_SETUP #line 239 "libxlu_disk_l.l" { xlu__disk_err(DPC,yytext,"unknown deprecated disk prefix"); return 0; } YY_BREAK /* positional parameters */ case 33: /* rule 33 can match eol */ YY_RULE_SETUP #line 246 "libxlu_disk_l.l" { STRIP(','); if (DPC->err) { /* previous errors may just lead to subsequent ones */ } else if (!DPC->disk->pdev_path) { SAVESTRING("target", pdev_path, yytext); } else if (!DPC->had_depr_prefix && DPC->disk->format == LIBXL_DISK_FORMAT_UNKNOWN) { if (!*DPC->disk->pdev_path && vdev_and_devtype(DPC,yytext)) { DPC->disk->format = LIBXL_DISK_FORMAT_EMPTY; } else { setformat(DPC,yytext); } } else if (!DPC->disk->vdev) { if (!vdev_and_devtype(DPC,yytext)) SAVESTRING("vdev", vdev, yytext); } else if (!DPC->access_set) { DPC->access_set = 1; setaccess(DPC,yytext); } else { xlu__disk_err(DPC,yytext,"too many positional parameters"); return 0; /* don't print any more errors */ } } YY_BREAK case 34: YY_RULE_SETUP #line 272 "libxlu_disk_l.l" { BEGIN(LEXERR); yymore(); } YY_BREAK case 35: YY_RULE_SETUP #line 276 "libxlu_disk_l.l" { xlu__disk_err(DPC,yytext,"bad disk syntax"); return 0; } YY_BREAK case 36: YY_RULE_SETUP #line 279 "libxlu_disk_l.l" YY_FATAL_ERROR( "flex scanner jammed" ); YY_BREAK #line 1637 "libxlu_disk_l.c" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(LEXERR): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * xlu__disk_yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( xlu__disk_yywrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of xlu__disk_yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = yyg->yytext_ptr; register int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ YY_FATAL_ERROR( "input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; xlu__disk_yyrestart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) xlu__disk_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { register yy_state_type yy_current_state; register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; yyg->yy_state_ptr = yyg->yy_state_buf; *yyg->yy_state_ptr++ = yy_current_state; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 355 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; *yyg->yy_state_ptr++ = yy_current_state; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { register int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ register YY_CHAR yy_c = 1; while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 355 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 354); if ( ! yy_is_jam ) *yyg->yy_state_ptr++ = yy_current_state; (void)yyg; return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ xlu__disk_yyrestart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( xlu__disk_yywrap(yyscanner ) ) return EOF; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void xlu__disk_yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ xlu__disk_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = xlu__disk_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } xlu__disk_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); xlu__disk_yy_load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void xlu__disk_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * xlu__disk_yypop_buffer_state(); * xlu__disk_yypush_buffer_state(new_buffer); */ xlu__disk_yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; xlu__disk_yy_load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (xlu__disk_yywrap()) processing, but the only time this flag * is looked at is after xlu__disk_yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void xlu__disk_yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE xlu__disk_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) xlu__disk_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in xlu__disk_yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) xlu__disk_yyalloc(b->yy_buf_size + 2 ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in xlu__disk_yy_create_buffer()" ); b->yy_is_our_buffer = 1; xlu__disk_yy_init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with xlu__disk_yy_create_buffer() * @param yyscanner The scanner object. */ void xlu__disk_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) xlu__disk_yyfree((void *) b->yy_ch_buf ,yyscanner ); xlu__disk_yyfree((void *) b ,yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a xlu__disk_yyrestart() or at EOF. */ static void xlu__disk_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; xlu__disk_yy_flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then xlu__disk_yy_init_buffer was _probably_ * called from xlu__disk_yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void xlu__disk_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) xlu__disk_yy_load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void xlu__disk_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; xlu__disk_yyensure_buffer_stack(yyscanner); /* This block is copied from xlu__disk_yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from xlu__disk_yy_switch_to_buffer. */ xlu__disk_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void xlu__disk_yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; xlu__disk_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { xlu__disk_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void xlu__disk_yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; yyg->yy_buffer_stack = (struct yy_buffer_state**)xlu__disk_yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in xlu__disk_yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)xlu__disk_yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in xlu__disk_yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE xlu__disk_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) xlu__disk_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in xlu__disk_yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; xlu__disk_yy_switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to xlu__disk_yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * xlu__disk_yy_scan_bytes() instead. */ YY_BUFFER_STATE xlu__disk_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) { return xlu__disk_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to xlu__disk_yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE xlu__disk_yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; yy_size_t i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) xlu__disk_yyalloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in xlu__disk_yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = xlu__disk_yy_scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in xlu__disk_yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE xlu__disk_yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int xlu__disk_yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int xlu__disk_yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *xlu__disk_yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *xlu__disk_yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ yy_size_t xlu__disk_yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *xlu__disk_yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void xlu__disk_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param line_number * @param yyscanner The scanner object. */ void xlu__disk_yyset_lineno (int line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "xlu__disk_yyset_lineno called with no buffer" ); yylineno = line_number; } /** Set the current column. * @param line_number * @param yyscanner The scanner object. */ void xlu__disk_yyset_column (int column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "xlu__disk_yyset_column called with no buffer" ); yycolumn = column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * @param yyscanner The scanner object. * @see xlu__disk_yy_switch_to_buffer */ void xlu__disk_yyset_in (FILE * in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = in_str ; } void xlu__disk_yyset_out (FILE * out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = out_str ; } int xlu__disk_yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void xlu__disk_yyset_debug (int bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = bdebug ; } /* Accessor methods for yylval and yylloc */ /* User-visible API */ /* xlu__disk_yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int xlu__disk_yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) xlu__disk_yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* xlu__disk_yylex_init_extra has the same functionality as xlu__disk_yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to xlu__disk_yyalloc in * the yyextra field. */ int xlu__disk_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; xlu__disk_yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) xlu__disk_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); xlu__disk_yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from xlu__disk_yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = 0; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = (char *) 0; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; yyg->yy_state_buf = 0; yyg->yy_state_ptr = 0; yyg->yy_full_match = 0; yyg->yy_lp = 0; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * xlu__disk_yylex_init() */ return 0; } /* xlu__disk_yylex_destroy is for both reentrant and non-reentrant scanners. */ int xlu__disk_yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ xlu__disk_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; xlu__disk_yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ xlu__disk_yyfree(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ xlu__disk_yyfree(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; xlu__disk_yyfree ( yyg->yy_state_buf , yyscanner); yyg->yy_state_buf = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * xlu__disk_yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ xlu__disk_yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *xlu__disk_yyalloc (yy_size_t size , yyscan_t yyscanner) { return (void *) malloc( size ); } void *xlu__disk_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void xlu__disk_yyfree (void * ptr , yyscan_t yyscanner) { free( (char *) ptr ); /* see xlu__disk_yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 278 "libxlu_disk_l.l" xen-4.9.2/tools/libxl/libxl_save_helper.c0000664000175000017500000002311113256712137016560 0ustar smbsmb/* * Copyright (C) 2012 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * The libxl-save-helper utility speaks a protocol to its caller for * the callbacks. The protocol is as follows. * * The helper talks on stdin and stdout, in binary in machine * endianness. The helper speaks first, and only when it has a * callback to make. It writes a 16-bit number being the message * length, and then the message body. * * Each message starts with a 16-bit number indicating which of the * messages it is, and then some arguments in a binary marshalled form. * If the callback does not need a reply (it returns void), the helper * just continues. Otherwise the helper waits for its caller to send a * single int which is to be the return value from the callback. * * Where feasible the stubs and callbacks have prototypes identical to * those required by xc_domain_save and xc_domain_restore, so that the * autogenerated functions can be used/provided directly. * * The actual messages are in the array @msgs in libxl_save_msgs_gen.pl */ #include "libxl_osdeps.h" #include #include #include #include #include #include #include "libxl.h" #include "libxl_utils.h" #include "xenctrl.h" #include "xenguest.h" #include "_libxl_save_msgs_helper.h" /*----- logger -----*/ __attribute__((format(printf, 5, 0))) static void tellparent_vmessage(xentoollog_logger *logger_in, xentoollog_level level, int errnoval, const char *context, const char *format, va_list al) { char *formatted; int r = vasprintf(&formatted, format, al); if (r < 0) { perror("memory allocation failed during logging"); exit(-1); } helper_stub_log(level, errnoval, context, formatted, 0); free(formatted); } static void tellparent_progress(struct xentoollog_logger *logger_in, const char *context, const char *doing_what, int percent, unsigned long done, unsigned long total) { helper_stub_progress(context, doing_what, done, total, 0); } static void tellparent_destroy(struct xentoollog_logger *logger_in) { abort(); } /*----- globals -----*/ static const char *program = "libxl-save-helper"; static xentoollog_logger logger = { tellparent_vmessage, tellparent_progress, tellparent_destroy, }; static xc_interface *xch; static int io_fd; /*----- error handling -----*/ static void fail(int errnoval, const char *fmt, ...) __attribute__((noreturn,format(printf,2,3))); static void fail(int errnoval, const char *fmt, ...) { va_list al; va_start(al,fmt); xtl_logv(&logger,XTL_ERROR,errnoval,program,fmt,al); exit(-1); } static int read_exactly(int fd, void *buf, size_t len) /* returns 0 if we get eof, even if we got it midway through; 1 if ok */ { while (len) { ssize_t r = read(fd, buf, len); if (r<=0) return r; assert(r <= len); len -= r; buf = (char*)buf + r; } return 1; } static void *xmalloc(size_t sz) { if (!sz) return 0; void *r = malloc(sz); if (!r) { perror("memory allocation failed"); exit(-1); } return r; } /*----- signal handling -----*/ static int unwriteable_fd; static void save_signal_handler(int num) { /* * We want to be able to interrupt save. But the code in libxc * which does the actual saving is straight-through, and we need * to execute its error path to put the guest back to sanity. * * So what we do is this: when we get the signal, we dup2 * the result of open("/dev/null",O_RDONLY) onto the output fd. * * This is guaranteed to 1. interrupt libxc's write (causing it to * return short, or maybe EINTR); 2. make the next write give * EBADF, so that: 3. at latest, libxc will notice when it next * tries to write data and will then go into its cleanup path. * * We make no effort here to sanitise the resulting errors. * That's libxl's job. */ int esave = errno; int r = dup2(unwriteable_fd, io_fd); if (r != io_fd) /* we can't write an xtl message because we might end up * interleaving on our control stream; we can't use stdio * because it's not async-signal-safe */ abort(); errno = esave; } static void setup_signals(void (*handler)(int)) { struct sigaction sa; sigset_t spmask; int r; unwriteable_fd = open("/dev/null",O_RDONLY); if (unwriteable_fd < 0) fail(errno,"open /dev/null for reading"); LIBXL_FILLZERO(sa); sa.sa_handler = handler; sigemptyset(&sa.sa_mask); r = sigaction(SIGTERM, &sa, 0); if (r) fail(errno,"sigaction SIGTERM failed"); sigemptyset(&spmask); sigaddset(&spmask,SIGTERM); r = sigprocmask(SIG_UNBLOCK,&spmask,0); if (r) fail(errno,"sigprocmask unblock SIGTERM failed"); } /*----- helper functions called by autogenerated stubs -----*/ unsigned char * helper_allocbuf(int len, void *user) { return xmalloc(len); } static void transmit(const unsigned char *msg, int len, void *user) { while (len) { int r = write(1, msg, len); if (r<0) { perror("write"); exit(-1); } assert(r >= 0); assert(r <= len); len -= r; msg += r; } } void helper_transmitmsg(unsigned char *msg_freed, int len_in, void *user) { assert(len_in < 64*1024); uint16_t len = len_in; transmit((const void*)&len, sizeof(len), user); transmit(msg_freed, len, user); free(msg_freed); } int helper_getreply(void *user) { int v; int r = read_exactly(0, &v, sizeof(v)); if (r<=0) exit(-2); return v; } /*----- other callbacks -----*/ static struct save_callbacks helper_save_callbacks; static void startup(const char *op) { xtl_log(&logger,XTL_DEBUG,0,program,"starting %s",op); xch = xc_interface_open(&logger,&logger,0); if (!xch) fail(errno,"xc_interface_open failed"); } static void complete(int retval) { int errnoval = retval ? errno : 0; /* suppress irrelevant errnos */ xtl_log(&logger,XTL_DEBUG,errnoval,program,"complete r=%d",retval); helper_stub_complete(retval,errnoval,0); xc_interface_close(xch); exit(0); } static struct restore_callbacks helper_restore_callbacks; int main(int argc, char **argv) { int r; int send_back_fd, recv_fd; #define NEXTARG (++argv, assert(*argv), *argv) const char *mode = *++argv; assert(mode); if (!strcmp(mode,"--save-domain")) { io_fd = atoi(NEXTARG); recv_fd = atoi(NEXTARG); uint32_t dom = strtoul(NEXTARG,0,10); uint32_t max_iters = strtoul(NEXTARG,0,10); uint32_t max_factor = strtoul(NEXTARG,0,10); uint32_t flags = strtoul(NEXTARG,0,10); int hvm = atoi(NEXTARG); unsigned cbflags = strtoul(NEXTARG,0,10); xc_migration_stream_t stream_type = strtoul(NEXTARG,0,10); assert(!*++argv); helper_setcallbacks_save(&helper_save_callbacks, cbflags); startup("save"); setup_signals(save_signal_handler); r = xc_domain_save(xch, io_fd, dom, max_iters, max_factor, flags, &helper_save_callbacks, hvm, stream_type, recv_fd); complete(r); } else if (!strcmp(mode,"--restore-domain")) { io_fd = atoi(NEXTARG); send_back_fd = atoi(NEXTARG); uint32_t dom = strtoul(NEXTARG,0,10); unsigned store_evtchn = strtoul(NEXTARG,0,10); domid_t store_domid = strtoul(NEXTARG,0,10); unsigned console_evtchn = strtoul(NEXTARG,0,10); domid_t console_domid = strtoul(NEXTARG,0,10); unsigned int hvm = strtoul(NEXTARG,0,10); unsigned int pae = strtoul(NEXTARG,0,10); int superpages = strtoul(NEXTARG,0,10); unsigned cbflags = strtoul(NEXTARG,0,10); xc_migration_stream_t stream_type = strtoul(NEXTARG,0,10); assert(!*++argv); helper_setcallbacks_restore(&helper_restore_callbacks, cbflags); unsigned long store_mfn = 0; unsigned long console_mfn = 0; startup("restore"); setup_signals(SIG_DFL); r = xc_domain_restore(xch, io_fd, dom, store_evtchn, &store_mfn, store_domid, console_evtchn, &console_mfn, console_domid, hvm, pae, superpages, stream_type, &helper_restore_callbacks, send_back_fd); helper_stub_restore_results(store_mfn,console_mfn,0); complete(r); } else { assert(!"unexpected mode argument"); } } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_genid.c0000664000175000017500000000636313256712137015363 0ustar smbsmb/* * Copyright (C) 2014 Citrix Systems R&D Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include #include /* * Generate a random VM generation ID. * * Returns ERROR_FAIL if a suitable source of random numbers is not * available. * * See Microsoft's "Virtual Machine Generation ID" specification for * further details, including when a new generation ID is required. * * http://www.microsoft.com/en-us/download/details.aspx?id=30707 */ int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id) { GC_INIT(ctx); int ret; ret = libxl__random_bytes(gc, id->bytes, LIBXL_MS_VM_GENID_LEN); GC_FREE; return ret; } /* * Is this VM generation ID all zeros? */ bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id) { static const libxl_ms_vm_genid zero; return memcmp(id->bytes, zero.bytes, LIBXL_MS_VM_GENID_LEN) == 0; } void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst, const libxl_ms_vm_genid *src) { memcpy(dst, src, LIBXL_MS_VM_GENID_LEN); } int libxl__ms_vm_genid_set(libxl__gc *gc, uint32_t domid, const libxl_ms_vm_genid *id) { libxl_ctx *ctx = libxl__gc_owner(gc); const char *dom_path; uint64_t genid[2]; uint64_t paddr = 0; int rc; memcpy(genid, id->bytes, LIBXL_MS_VM_GENID_LEN); /* * Set the "platform/generation-id" XenStore key to pass the ID to * hvmloader. */ dom_path = libxl__xs_get_dompath(gc, domid); if (!dom_path) { rc = ERROR_FAIL; goto out; } rc = libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/platform/generation-id", dom_path), "%"PRIu64 ":%" PRIu64, genid[0], genid[1]); if (rc < 0) goto out; /* * Update the ID in guest memory (if available). */ xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_VM_GENERATION_ID_ADDR, &paddr); if (paddr) { void *vaddr; vaddr = xc_map_foreign_range(ctx->xch, domid, XC_PAGE_SIZE, PROT_READ | PROT_WRITE, paddr >> XC_PAGE_SHIFT); if (vaddr == NULL) { rc = ERROR_FAIL; goto out; } memcpy(vaddr + (paddr & ~XC_PAGE_MASK), genid, 2 * sizeof(*genid)); munmap(vaddr, XC_PAGE_SIZE); /* * The spec requires an ACPI Notify event is injected into the * guest when the generation ID is changed. * * This is only called for domains that are suspended or newly * created and they won't be in a state to receive such an * event. */ } rc = 0; out: return rc; } xen-4.9.2/tools/libxl/libxl_linux.c0000664000175000017500000002122113256712137015422 0ustar smbsmb/* * Copyright (C) 2011 * Author Roger Pau Monne * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" int libxl__try_phy_backend(mode_t st_mode) { if (S_ISBLK(st_mode) || S_ISREG(st_mode)) { return 1; } return 0; } char *libxl__devid_to_localdev(libxl__gc *gc, int devid) { return libxl__devid_to_vdev(gc, devid); } /* Hotplug scripts helpers */ static char **get_hotplug_env(libxl__gc *gc, char *script, libxl__device *dev) { const char *type = libxl__device_kind_to_string(dev->backend_kind); char *be_path = libxl__device_backend_path(gc, dev); char **env; int nr = 0; const int arraysize = 15; GCNEW_ARRAY(env, arraysize); env[nr++] = "script"; env[nr++] = script; env[nr++] = "XENBUS_TYPE"; env[nr++] = (char *) type; env[nr++] = "XENBUS_PATH"; env[nr++] = GCSPRINTF("backend/%s/%u/%d", type, dev->domid, dev->devid); env[nr++] = "XENBUS_BASE_PATH"; env[nr++] = "backend"; if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) { libxl_nic_type nictype; char *gatewaydev; gatewaydev = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "gatewaydev")); env[nr++] = "netdev"; env[nr++] = gatewaydev ? : ""; if (libxl__nic_type(gc, dev, &nictype)) { LOGD(ERROR, dev->domid, "unable to get nictype"); return NULL; } switch (nictype) { case LIBXL_NIC_TYPE_VIF_IOEMU: env[nr++] = "INTERFACE"; env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid, dev->devid, LIBXL_NIC_TYPE_VIF_IOEMU); /* * We need to fall through because for PV_IOEMU nic types we need * to execute both the vif and the tap hotplug script, and we * don't know which one we are executing in this call, so provide * both env variables. */ case LIBXL_NIC_TYPE_VIF: env[nr++] = "vif"; env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid, dev->devid, LIBXL_NIC_TYPE_VIF); break; default: return NULL; } } env[nr++] = NULL; assert(nr <= arraysize); return env; } /* Hotplug scripts caller functions */ static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action, int num_exec) { char *be_path = libxl__device_backend_path(gc, dev); char *script; int nr = 0, rc = 0; libxl_nic_type nictype; script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "script")); if (!script) { LOGED(ERROR, dev->domid, "unable to read script from %s", be_path); rc = ERROR_FAIL; goto out; } rc = libxl__nic_type(gc, dev, &nictype); if (rc) { LOGD(ERROR, dev->domid, "error when fetching nic type"); rc = ERROR_FAIL; goto out; } if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) { rc = 0; goto out; } *env = get_hotplug_env(gc, script, dev); if (!*env) { rc = ERROR_FAIL; goto out; } const int arraysize = 4; GCNEW_ARRAY(*args, arraysize); (*args)[nr++] = script; if (nictype == LIBXL_NIC_TYPE_VIF_IOEMU && num_exec) { (*args)[nr++] = (char *) libxl__device_action_to_string(action); (*args)[nr++] = "type_if=tap"; (*args)[nr++] = NULL; } else { (*args)[nr++] = action == LIBXL__DEVICE_ACTION_ADD ? "online" : "offline"; (*args)[nr++] = "type_if=vif"; (*args)[nr++] = NULL; } assert(nr == arraysize); rc = 1; out: return rc; } static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action) { char *be_path = libxl__device_backend_path(gc, dev); char *script; int nr = 0, rc = 0; script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "script")); if (!script) { LOGEVD(ERROR, errno, dev->domid, "unable to read script from %s", be_path); rc = ERROR_FAIL; goto error; } *env = get_hotplug_env(gc, script, dev); if (!*env) { LOGD(ERROR, dev->domid, "Failed to get hotplug environment"); rc = ERROR_FAIL; goto error; } const int arraysize = 3; GCNEW_ARRAY(*args, arraysize); (*args)[nr++] = script; (*args)[nr++] = (char *) libxl__device_action_to_string(action); (*args)[nr++] = NULL; assert(nr == arraysize); LOGD(DEBUG, dev->domid, "Args and environment ready"); rc = 1; error: return rc; } int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action, int num_exec) { int rc; switch (dev->backend_kind) { case LIBXL__DEVICE_KIND_VBD: if (num_exec != 0) { LOGD(DEBUG, dev->domid, "num_exec %d, not running hotplug scripts", num_exec); rc = 0; goto out; } rc = libxl__hotplug_disk(gc, dev, args, env, action); break; case LIBXL__DEVICE_KIND_VIF: /* * If domain has a stubdom we don't have to execute hotplug scripts * for emulated interfaces */ if ((num_exec > 1) || (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) { LOGD(DEBUG, dev->domid, "num_exec %d, not running hotplug scripts", num_exec); rc = 0; goto out; } rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec); break; default: /* No need to execute any hotplug scripts */ LOGD(DEBUG, dev->domid, "backend_kind %d, no need to execute scripts", dev->backend_kind); rc = 0; break; } out: return rc; } libxl_device_model_version libxl__default_device_model(libxl__gc *gc) { return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN; } int libxl__pci_numdevs(libxl__gc *gc) { DIR *dir; struct dirent *entry; int num_devs = 0; dir = opendir("/sys/bus/pci/devices"); if (!dir) { LOGE(ERROR, "Cannot open /sys/bus/pci/devices"); return ERROR_FAIL; } while ((entry = readdir(dir))) { if (entry->d_name[0] == '.') continue; num_devs++; } closedir(dir); return num_devs; } int libxl__pci_topology_init(libxl__gc *gc, physdev_pci_device_t *devs, int num_devs) { DIR *dir; struct dirent *entry; int i, err = 0; dir = opendir("/sys/bus/pci/devices"); if (!dir) { LOGE(ERROR, "Cannot open /sys/bus/pci/devices"); return ERROR_FAIL; } i = 0; while ((entry = readdir(dir))) { unsigned int dom, bus, dev, func; if (entry->d_name[0] == '.') continue; if (i == num_devs) { LOG(ERROR, "Too many devices"); err = ERROR_FAIL; errno = -ENOSPC; goto out; } if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4) { LOGE(ERROR, "Error processing /sys/bus/pci/devices"); err = ERROR_FAIL; goto out; } devs[i].seg = dom; devs[i].bus = bus; devs[i].devfn = ((dev & 0x1f) << 3) | (func & 7); i++; } out: closedir(dir); return err; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_no_colo.c0000664000175000017500000000330113256712137015712 0ustar smbsmb/* * Copyright (C) 2016 * Author Wei Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" void libxl__colo_restore_setup(libxl__egc *egc, libxl__colo_restore_state *crs) { STATE_AO_GC(crs->ao); LOGD(ERROR, crs->domid, "COLO is not supported"); crs->callback(egc, crs, ERROR_FAIL); } void libxl__colo_restore_teardown(libxl__egc *egc, void *dcs_void, int ret, int retval, int errnoval) { /* Shouldn't be here because setup already failed */ abort(); } void libxl__colo_save_setup(libxl__egc *egc, libxl__colo_save_state *css) { libxl__domain_save_state *dss = CONTAINER_OF(css, *dss, css); STATE_AO_GC(dss->ao); LOGD(ERROR, dss->domid, "COLO is not supported"); dss->callback(egc, dss, ERROR_FAIL); } void libxl__colo_save_teardown(libxl__egc *egc, libxl__colo_save_state *css, int rc) { /* Shouldn't be here because setup already failed */ abort(); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl.h0000664000175000017500000025114513256712137014222 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * libxl API compatibility * * From Xen 4.2 onwards the API of libxl will be maintained in a * stable manner. This means that it should be possible to write an * application against the API provided by libxl in Xen 4.2 and expect * that it will continue to compile against future versions of Xen * without source modification. * * In order to make such compatibility possible it is required that * application which want to be exposed to a particular API #define * LIBXL_API_VERSION before including libxl.h or any other libxl * header. The syntax of the LIBXL_API_VERSION is: * 0xVVSSEE * where ($(XEN_xxx) from xen/Makefile): * VV is the Xen major release number, $(XEN_VERSION) * SS is the Xen sub version number, $(XEN_SUBVERSION) * EE is the Xen extra version digit, first numeric part of * $(XEN_EXTRAVERSION) not including the leading "." * For example the first stable API version, supported by Xen 4.2.0, * is 0x040200. * * Lack of LIBXL_API_VERSION means "the latest" which will * change. Specifying an unknown LIBXL_API_VERSION will result in a * compile time error. * * Identical versions of the libxl API will represented by the version * containing the earliest instance of that API. e.g. if 4.2.0 and * 4.3.0 contain an identical libxl API then only LIBXL_API_VERSION * 0x040200 will be valid. * * We will try especially hard to avoid changing the API during a * stable series, i.e. it should be unusual for the last byte of * LIBXL_API_VERSION to be non-zero. * * In the event that a change is required which cannot be made * backwards compatible in this manner a #define of the form * LIBXL_HAVE_ will always be added in order to make it * possible to write applications which build against any version of * libxl. Such changes are expected to be exceptional and used as a * last resort. The barrier for backporting such a change to a stable * branch will be very high. * * These guarantees apply only to stable releases of Xen. When an * incompatible change is made in the unstable tree then * LIBXL_API_VERSION will be bumped to the next expected stable * release number on the first such change only. Applications which * want to support building against Xen unstable are expected to track * API changes in that tree until it is released as a stable release. * * API compatibility will be maintained for all versions of Xen using * the same $(XEN_VERSION) (e.g. throughout a major release). */ /* LIBXL_HAVE_CONSOLE_NOTIFY_FD * * If this is defined, libxl_console_exec and * libxl_primary_console_exe take a notify_fd parameter. That * parameter will be used to notify the caller that the console is connected. */ #define LIBXL_HAVE_CONSOLE_NOTIFY_FD 1 /* LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS * * If this is defined, the copy functions have constified src parameter and the * length functions accept constified parameter. */ #define LIBXL_HAVE_CONST_COPY_AND_LENGTH_FUNCTIONS 1 /* LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO * * If this is defined, libxl_domain_need_memory no longer modifies * the b_info paseed in. */ #define LIBXL_HAVE_DOMAIN_NEED_MEMORY_CONST_B_INFO 1 /* LIBXL_HAVE_VNUMA * * If this is defined the type libxl_vnode_info exists, and a * field 'vnuma_nodes' is present in libxl_domain_build_info. */ #define LIBXL_HAVE_VNUMA 1 /* LIBXL_HAVE_USERDATA_UNLINK * * If it is defined, libxl has a library function called * libxl_userdata_unlink. */ #define LIBXL_HAVE_USERDATA_UNLINK 1 /* LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID * * If this is defined, libxl has a library function called * libxl_cpupool_qualifier_to_cpupoolid, which takes in a CPU pool * qualifier in the form of number or string, then returns the ID of * that CPU pool. */ #define LIBXL_HAVE_CPUPOOL_QUALIFIER_TO_CPUPOOLID 1 /* LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP * * If this is defined, libxl has two library functions called * libxl_cpupool_cpuadd_cpumap and libxl_cpupool_cpuremove_cpumap, * which allow to add to or remove from a cpupool all the cpus * specified in a bitmap. */ #define LIBXL_HAVE_CPUPOOL_ADD_REM_CPUMAP 1 /* * * LIBXL_HAVE_BITMAP_AND_OR * * If this is defined, libxl has two library functions, libxl_bitmap_and * and libxl_bitmap_or to compute the logical and and or of two bitmaps */ #define LIBXL_HAVE_BITMAP_AND_OR 1 /* * LIBXL_HAVE_FIRMWARE_PASSTHROUGH indicates the feature for * passing in SMBIOS and ACPI firmware to HVM guests is present * in the library. */ #define LIBXL_HAVE_FIRMWARE_PASSTHROUGH 1 /* * LIBXL_HAVE_DOMAIN_NODEAFFINITY indicates that a 'nodemap' field * (of libxl_bitmap type) is present in libxl_domain_build_info, * containing the node-affinity for the domain. */ #define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1 /* * LIBXL_HAVE_PVUSB indicates functions for plugging in USB devices * through pvusb -- both hotplug and at domain creation time.. */ #define LIBXL_HAVE_PVUSB 1 /* * LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE indicates that the * libxl_vendor_device field is present in the hvm sections of * libxl_domain_build_info. This field tells libxl which * flavour of xen-pvdevice to enable in QEMU. */ #define LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE 1 /* * The libxl_domain_build_info has the event_channels field. */ #define LIBXL_HAVE_BUILDINFO_EVENT_CHANNELS 1 /* * libxl_domain_build_info has the u.hvm.ms_vm_genid field. */ #define LIBXL_HAVE_BUILDINFO_HVM_MS_VM_GENID 1 /* * LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY indicates that a 'cpumap_soft' * field (of libxl_bitmap type) is present in libxl_vcpuinfo, * containing the soft affinity of a vcpu. */ #define LIBXL_HAVE_VCPUINFO_SOFT_AFFINITY 1 /* * LIBXL_HAVE_SET_VCPUAFFINITY_FORCE indicates that the * libxl_set_vcpuaffinity_force() library call is available. */ #define LIBXL_HAVE_SET_VCPUAFFINITY_FORCE 1 /* * LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE indicates that a * 'direct_io_safe' field (of boolean type) is present in * libxl_device_disk. */ #define LIBXL_HAVE_DEVICE_DISK_DIRECT_IO_SAFE 1 /* * The libxl_device_disk has the discard_enable field. */ #define LIBXL_HAVE_LIBXL_DEVICE_DISK_DISCARD_ENABLE 1 /* * LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN indicates that it is possible * to specify the start guest frame number used to map a range of I/O * memory machine frame numbers via the 'gfn' field (of type uint64) * of the 'iomem' structure. An array of iomem structures is embedded * in libxl_domain_build_info and used to map the indicated memory * ranges during domain build. */ #define LIBXL_HAVE_BUILDINFO_IOMEM_START_GFN 1 /* * LIBXL_HAVE_SCHED_RTDS indicates that the RTDS real time scheduler * is available. A 'budget' field added in libxl_domain_sched_params. */ #define LIBXL_HAVE_SCHED_RTDS 1 /* * LIBXL_HAVE_SCHED_NULL indicates that the 'null' static scheduler * is available. */ #define LIBXL_HAVE_SCHED_NULL 1 /* * libxl_domain_build_info has u.hvm.viridian_enable and _disable bitmaps * of the specified width. */ #define LIBXL_HAVE_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE 1 #define LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH 64 /* * libxl_domain_build_info has the u.hvm.mmio_hole_memkb field. */ #define LIBXL_HAVE_BUILDINFO_HVM_MMIO_HOLE_MEMKB 1 /* * libxl_domain_info returns ERROR_DOMAIN_NOTFOUND if the domain * is not present, instead of ERROR_INVAL. */ #define LIBXL_HAVE_ERROR_DOMAIN_NOTFOUND 1 /* * libxl_domain_build_info has device_tree and libxl_device_dtdev * exists. This mean Device Tree passthrough is supported for ARM */ #define LIBXL_HAVE_DEVICETREE_PASSTHROUGH 1 /* * libxl_domain_build_info has device_model_user to specify the user to * run the device model with. See docs/misc/qemu-deprivilege.txt. */ #define LIBXL_HAVE_DEVICE_MODEL_USER 1 /* * libxl_vcpu_sched_params is used to store per-vcpu params. */ #define LIBXL_HAVE_VCPU_SCHED_PARAMS 1 /* * LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS indicates RTDS scheduler * now supports per-vcpu settings. */ #define LIBXL_HAVE_SCHED_RTDS_VCPU_PARAMS 1 /* * libxl_domain_build_info has the arm.gic_version field. */ #define LIBXL_HAVE_BUILDINFO_ARM_GIC_VERSION 1 /* * LIBXL_HAVE_SOFT_RESET indicates that libxl supports performing * 'soft reset' for domains and there is 'soft_reset' shutdown reason * in enum libxl_shutdown_reason. */ #define LIBXL_HAVE_SOFT_RESET 1 /* * LIBXL_HAVE_APIC_ASSIST indicates that the 'apic_assist' value * is present in the viridian enlightenment enumeration. */ #define LIBXL_HAVE_APIC_ASSIST 1 /* * LIBXL_HAVE_BUILD_ID means that libxl_version_info has the extra * field for the hypervisor build_id. */ #define LIBXL_HAVE_BUILD_ID 1 /* * LIBXL_HAVE_QEMU_MONITOR_COMMAND indiactes the availability of the * libxl_qemu_monitor_command() function. */ #define LIBXL_HAVE_QEMU_MONITOR_COMMAND 1 /* * LIBXL_HAVE_SCHED_CREDIT2_PARAMS indicates the existance of a * libxl_sched_credit2_params structure, containing Credit2 scheduler * wide parameters (i.e., the ratelimiting value). */ #define LIBXL_HAVE_SCHED_CREDIT2_PARAMS 1 /* * LIBXL_HAVE_VIRIDIAN_CRASH_CTL indicates that the 'crash_ctl' value * is present in the viridian enlightenment enumeration. */ #define LIBXL_HAVE_VIRIDIAN_CRASH_CTL 1 /* * LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE indicates that * libxl_domain_build_info has the u.hvm.acpi_laptop_slate field. */ #define LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE 1 /* * libxl ABI compatibility * * The only guarantee which libxl makes regarding ABI compatibility * across releases is that the SONAME will always be bumped whenever * the ABI is changed in an incompatible way. * * This applies within stable branches as well as * development branches. It is possible that a new stable release of * Xen may require a rebuild of applications using the * library. However per the API compatibility gaurantees such a * rebuild should not normally require any source level changes. * * As with the API compatiblity the SONAME will only be bumped for the * first ABI incompatible change in a development branch. */ /* * libxl memory management * * From the point of view of the application (ie, libxl's caller), * struct libxl_ctx* is threadsafe, and all returned allocated * structures are obtained from malloc(), and must be freed by the * caller either directly or by calling an appropriate free function * provided by libxl. Ie the application does not get automatic * assistance from libxl in managing these allocations. * * Specific details are in the header comments which should be found * in libxl.h or libxlutil.h, next to the relevant function * declarations. * * Internally, libxl has a garbage collection scheme which allows much libxl * code to allocate strings etc. for internal use without needing to * free them. These are called "temporary allocations". * * The pool for these temporary allocations, along with any other * thread-specific data which is private to libxl but shared between * libxl functions (such as the current xenstore transaction), is * stored in the "gc context" which is a special enhanced context * structure allocated automatically by convenience macros at every * entry to libxl. * * Every libxl function falls into one of these categories: * * 1. Public functions (declared in libxl.h, libxlutil.h), which may * be called by libxl applications. If a public function returns * any allocated object to its caller, that object must have come * from malloc. * * The definitions of public functions MUST use the gc context * initialisation macros (or do the equivalent work themselves). * These macros will ensure that all temporary allocations will be * automatically freed before the function returns to its caller. * * A public function may be called from within libxl; the call * context initialisation macros will make sure that the internal * caller's context is reused (eg, so that the same xenstore * transaction is used). But in-libxl callers of libxl public * functions should note that any libxl public function may cause * recursively reentry into libxl via the application's event * callback hook. * * Public functions have names like libxl_foobar. * * 2. Private functions, which may not be called by libxl * applications; they are not declared in libxl.h or libxlutil.h * and they may not be called other than by other libxl functions. * * Private functions should not use the gc context initialisation * macros. * * Private functions have names like libxl__foobar (NB, two underscores). * Also the declaration of such functions must be preceeded by the _hidden * macro. * * Allocations made by a libxl function fall into one of the following * categories (where "object" includes any memory allocation): * * (a) Objects which are not returned to the function's caller. * These should be allocated from the temporary pool. * * (b) Objects which are intended for return to the calling * application. This includes all allocated objects returned by * any public function. * * It may also include objects allocated by an internal function * specifically for eventual return by the function's external * callers, but this situation should be clearly documented in * comments. * * These should be allocated from malloc() et al. and comments * near the function declaration should explain the memory * ownership. If a simple free() by the application is not * sufficient, a suitable public freeing function should be * provided. * * (c) Internal objects whose size and/or lifetime dictate explicit * memory management within libxl. This includes objects which * will be embedded in opaque structures which will be returned to * the libxl caller (more generally, any internal object whose * lifetime exceeds the libxl entrypoint which creates it) and * objects which are so large or numerous that explicit memory * management is required. * * These should be allocated from malloc() et al., and freed * explicitly at the appropriate point. The situation should be * documented in comments. * * (d) Objects which are allocated by internal-only functions and * returned to the function's (therefore, internal) caller but are * strictly for internal use by other parts of libxl. These * should be allocated from the temporary pool. * * Where a function's primary purpose is to return such an object, * it should have a libxl__gc * as it's first argument. * * Note that there are two ways to change an allocation from this * category to the "public" category. Either the implementation * is kept internal and a wrapper function duplicates all memory * allocations so that they are suitable for return to external * callers or the implementation uses plain malloc() et al calls * and an internal wrapper adds the relevant pointers to the gc. * The latter method is preferred for obvious performance reasons. * * No temporary objects allocated from the pool may be explicitly freed. * Therefore public functions which initialize a libxl__gc MUST call * libxl__free_all() before returning. * * Memory allocation failures are not handled gracefully. If malloc * (or realloc) fails, libxl will cause the entire process to print * a message to stderr and exit with status 255. */ /* * libxl types * * Most libxl types are defined by the libxl IDL (see * libxl_types.idl). The library provides a common set of methods for * initialising and freeing these types. * * IDL-generated libxl types should be used as follows: the user must * always call the "init" function before using a type, even if the * variable is simply being passed by reference as an out parameter * to a libxl function. The user must always calls "dispose" exactly * once afterwards, to clean up, regardless of whether operations on * this object succeeded or failed. See the xl code for examples. * * "init" and "dispose" are idempotent. * * void libxl__init( *p): * * Initialises the members of "p" to all defaults. These may either * be special value which indicates to the library that it should * select an appropriate default when using this field or actual * default values. * * Some fields within a data type (e.g. unions) cannot be sensibly * initialised without further information. In these cases a * separate subfield initialisation function is provided (see * below). * * An instance which has been initialised using this method can * always be safely passed to the dispose function (see * below). This is true even if the data type contains fields which * require a separate call to a subfield initialisation function. * * This method is provided for any aggregate type which is used as * an input parameter. * * void libxl__init_( *p, subfield): * * Initialise those parts of "p" which are not initialised by the * main init function due to the unknown value of "subfield". Sets * p->subfield as well as initialising any fields to their default * values. * * p->subfield must not have been previously initialised. * * This method is provided for any aggregate type. * * void libxl__dispose(instance *p): * * Frees any dynamically allocated memory used by the members of * "p" but not the storage used by "p" itself (this allows for the * allocation of arrays of types and for the composition of types). * * char *libxl__to_json(instance *p) * * Generates a JSON object from "p" in the form of a NULL terminated * string. * * libxl__from_json(const char *json) * int libxl__from_json(const char *json) * * Parses "json" and returns: * * an int value, if is enumeration type. The value is the enum value * representing the respective string in "json". * * an instance of , if is aggregate type. The returned * instance has its fields filled in by the parser according to "json". * * If the parsing fails, caller cannot rely on the value / instance * returned. */ #ifndef LIBXL_H #define LIBXL_H #include #include #include #include #include #include #include /* for pid_t */ #include typedef struct libxl__ctx libxl_ctx; #include #include <_libxl_list.h> /* API compatibility. */ #ifdef LIBXL_API_VERSION #if LIBXL_API_VERSION != 0x040200 && LIBXL_API_VERSION != 0x040300 && \ LIBXL_API_VERSION != 0x040400 && LIBXL_API_VERSION != 0x040500 #error Unknown LIBXL_API_VERSION #endif #endif /* LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION * * If this is defined we have libxl_retrieve_domain_configuration which * returns the current configuration of a domain, which can be used to * rebuild a domain. */ #define LIBXL_HAVE_RETRIEVE_DOMAIN_CONFIGURATION 1 /* * LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS * * If this is defined, then the libxl_domain_build_info structure will * contain two arrays of libxl_bitmap-s, with all the necessary information * to set the hard affinity (vcpu_hard_affinity) and the soft affinity * (vcpu_soft_affinity) of the VCPUs. * * Note that, if the vcpu_hard_affinity array is used, libxl will ignore * the content of the cpumap field of libxl_domain_build_info. That is to * say, if the array is allocated and used by the caller, it is it and * only it that determines the hard affinity of the domain's VCPUs. * * The number of libxl_bitmap-s in the arrays should be equal to the * maximum number of VCPUs of the domain. If there only are N elements in * an array, with N smaller the the maximum number of VCPUs, the hard or * soft affinity (depending on which array we are talking about) will be * set only for the first N VCPUs. The other VCPUs will just have affinity, * both hard and soft, with all the host PCPUs. * Each bitmap should be big enough to accommodate the maximum number of * PCPUs of the host. */ #define LIBXL_HAVE_BUILDINFO_VCPU_AFFINITY_ARRAYS 1 /* * LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST * * If this is defined, then the libxl_domain_build_info structure will * contain hvm.usbdevice_list, a libxl_string_list type that contains * a list of USB devices to specify on the qemu command-line. * * If it is set, callers may use either hvm.usbdevice or * hvm.usbdevice_list, but not both; if both are set, libxl will * throw an error. * * If this is not defined, callers can only use hvm.usbdevice. Note * that this means only one device can be added at domain build time. */ #define LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST 1 /* * LIBXL_HAVE_BUILDINFO_USBVERSION * * If this is defined, then the libxl_domain_build_info structure will * contain hvm.usbversion, a integer type that contains a USB * controller version to specify on the qemu upstream command-line. * * If it is set, callers may use hvm.usbversion to specify if the usb * controller is usb1, usb2 or usb3. * * If this is not defined, the hvm.usbversion field does not exist. */ #define LIBXL_HAVE_BUILDINFO_USBVERSION 1 /* * LIBXL_HAVE_DEVICE_BACKEND_DOMNAME * * If this is defined, libxl_device_* structures containing a backend_domid * field also contain a backend_domname field. If backend_domname is set, it is * resolved to a domain ID when the device is used and takes precedence over the * backend_domid field. * * If this is not defined, the backend_domname field does not exist. */ #define LIBXL_HAVE_DEVICE_BACKEND_DOMNAME 1 /* * LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG * * This argument was erroneously "const" in the 4.2 release despite * the requirement for the callback to free the event. */ #if LIBXL_API_VERSION != 0x040200 #define LIBXL_HAVE_NONCONST_EVENT_OCCURS_EVENT_ARG 1 #endif /* * LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE * * The return value of libxl_basename is malloc'ed but the erroneously * marked as "const" in releases before 4.5. */ #if !defined(LIBXL_API_VERSION) || LIBXL_API_VERSION >= 0x040500 #define LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE 1 #endif /* * LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES * * If this is defined, libxl_physinfo structure will contain an uint64 field * called outstanding_pages, containing the number of pages claimed but not * yet allocated for all domains. */ #define LIBXL_HAVE_PHYSINFO_OUTSTANDING_PAGES 1 /* * LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1 * * If this is defined, libxl_dominfo will contain a MemKB type field called * outstanding_memkb, containing the amount of claimed but not yet allocated * memory for a specific domain. */ #define LIBXL_HAVE_DOMINFO_OUTSTANDING_MEMKB 1 /* * LIBXL_HAVE_DOMINFO_NEVER_STOP * * If this is defined, libxl_dominfo will contain a flag called never_stop * indicating that the specific domain should never be stopped by the * toolstack. */ #define LIBXL_HAVE_DOMINFO_NEVER_STOP 1 /* * LIBXL_HAVE_QXL * * If defined, then the libxl_vga_interface_type will contain another value: * "QXL". This value define if qxl vga is supported. * * If this is not defined, the qxl vga support is missed. */ #define LIBXL_HAVE_QXL 1 /* * LIBXL_HAVE_SPICE_VDAGENT * * If defined, then the libxl_spice_info structure will contain a boolean type: * vdagent and clipboard_sharing. These values define if Spice vdagent and * clipboard sharing are enabled. * * If this is not defined, the Spice vdagent support is ignored. */ #define LIBXL_HAVE_SPICE_VDAGENT 1 /* * LIBXL_HAVE_SPICE_USBREDIRECTION * * If defined, then the libxl_spice_info structure will contain an integer type * field: usbredirection. This value defines if Spice usbredirection is enabled * and with how much channels. * * If this is not defined, the Spice usbredirection support is ignored. */ #define LIBXL_HAVE_SPICE_USBREDIREDIRECTION 1 /* * LIBXL_HAVE_SPICE_IMAGECOMPRESSION * * If defined, then the libxl_spice_info structure will contain a string type * field: image_compression. This value defines what Spice image compression * is used. * * If this is not defined, the Spice image compression setting support is ignored. */ #define LIBXL_HAVE_SPICE_IMAGECOMPRESSION 1 /* * LIBXL_HAVE_SPICE_STREAMINGVIDEO * * If defined, then the libxl_spice_info structure will contain a string type * field: streaming_video. This value defines what Spice streaming video setting * is used. * * If this is not defined, the Spice streaming video setting support is ignored. */ #define LIBXL_HAVE_SPICE_STREAMINGVIDEO 1 /* * LIBXL_HAVE_HVM_HDTYPE * * If defined, then the u.hvm structure will contain a enum type * hdtype. */ #define LIBXL_HAVE_HVM_HDTYPE 1 /* * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1 * * If this is defined, libxl_domain_create_restore()'s API has changed to * include a params structure. */ #define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS 1 /* * LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1 * * If this is defined, libxl_domain_create_restore()'s API includes the * send_back_fd param. This is used only with COLO, for the libxl migration * back channel; other callers should pass -1. */ #define LIBXL_HAVE_DOMAIN_CREATE_RESTORE_SEND_BACK_FD 1 /* * LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1 * * If this is defined, libxl_domain_create_info contains a driver_domain * field that can be used to tell libxl that the domain that is going * to be created is a driver domain, so the necessary actions are taken. */ #define LIBXL_HAVE_DRIVER_DOMAIN_CREATION 1 /* * LIBXL_HAVE_SIGCHLD_SELECTIVE_REAP * * If this is defined: * * Firstly, the enum libxl_sigchld_owner (in libxl_event.h) has the * value libxl_sigchld_owner_libxl_always_selective_reap which may be * passed to libxl_childproc_setmode in hooks->chldmode. * * Secondly, the function libxl_childproc_sigchld_occurred exists. */ #define LIBXL_HAVE_SIGCHLD_OWNER_SELECTIVE_REAP 1 /* * LIBXL_HAVE_SIGCHLD_SHARING * * If this is defined, it is permissible for multiple libxl ctxs * to simultaneously "own" SIGCHLD. See "Subprocess handling" * in libxl_event.h. */ #define LIBXL_HAVE_SIGCHLD_SHARING 1 /* * LIBXL_HAVE_NO_SUSPEND_RESUME * * Is this is defined then the platform has no support for saving, * restoring or migrating a domain. In this case the related functions * should be expected to return failure. That is: * - libxl_domain_suspend * - libxl_domain_resume * - libxl_domain_remus_start */ #if defined(__arm__) || defined(__aarch64__) #define LIBXL_HAVE_NO_SUSPEND_RESUME 1 #endif /* * LIBXL_HAVE_DEVICE_PCI_SEIZE * * If this is defined, then the libxl_device_pci struct will contain * the "seize" boolean field. If this field is set, libxl_pci_add will * check to see if the device is currently assigned to pciback, and if not, * it will attempt to do so (unbinding the device from the existing driver). */ #define LIBXL_HAVE_DEVICE_PCI_SEIZE 1 /* * LIBXL_HAVE_BUILDINFO_KERNEL * * If this is defined, then the libxl_domain_build_info structure will * contain 'kernel', 'ramdisk', 'cmdline' fields. 'kernel' is a string * to indicate kernel image location, 'ramdisk' is a string to indicate * ramdisk location, 'cmdline' is a string to indicate the paramters which * would be appended to kernel image. * * Both PV guest and HVM guest can use these fields for direct kernel boot. * But for compatibility reason, u.pv.kernel, u.pv.ramdisk and u.pv.cmdline * still exist. */ #define LIBXL_HAVE_BUILDINFO_KERNEL 1 /* * LIBXL_HAVE_DEVICE_CHANNEL * * If this is defined, then the libxl_device_channel struct exists * and channels can be attached to a domain. Channels manifest as consoles * with names, see docs/misc/console.txt. */ #define LIBXL_HAVE_DEVICE_CHANNEL 1 /* * LIBXL_HAVE_AO_ABORT indicates the availability of libxl_ao_abort */ #define LIBXL_HAVE_AO_ABORT 1 /* Functions annotated with LIBXL_EXTERNAL_CALLERS_ONLY may not be * called from within libxl itself. Callers outside libxl, who * do not #include libxl_internal.h, are fine. */ #ifndef LIBXL_EXTERNAL_CALLERS_ONLY #define LIBXL_EXTERNAL_CALLERS_ONLY /* disappears for callers outside libxl */ #endif /* * LIBXL_HAVE_UUID_COPY_CTX_PARAM * * If this is defined, libxl_uuid_copy has changed to take a libxl_ctx * structure. */ #define LIBXL_HAVE_UUID_COPY_CTX_PARAM 1 /* * LIBXL_HAVE_SSID_LABEL * * If this is defined, then libxl IDL contains string of XSM security * label in all XSM related structures. * * If set this string takes precedence over the numeric field. */ #define LIBXL_HAVE_SSID_LABEL 1 /* * LIBXL_HAVE_CPUPOOL_NAME * * If this is defined, then libxl IDL contains string of CPU pool * name in all CPU pool related structures. * * If set this string takes precedence over the numeric field. */ #define LIBXL_HAVE_CPUPOOL_NAME 1 /* * LIBXL_HAVE_BUILDINFO_SERIAL_LIST * * If this is defined, then the libxl_domain_build_info structure will * contain hvm.serial_list, a libxl_string_list type that contains * a list of serial ports to specify on the qemu command-line. * * If it is set, callers may use either hvm.serial or * hvm.serial_list, but not both; if both are set, libxl will * throw an error. * * If this is not defined, callers can only use hvm.serial. Note * that this means only one serial port can be added at domain build time. */ #define LIBXL_HAVE_BUILDINFO_SERIAL_LIST 1 /* * LIBXL_HAVE_ALTP2M * If this is defined, then libxl supports alternate p2m functionality. */ #define LIBXL_HAVE_ALTP2M 1 /* * LIBXL_HAVE_REMUS * If this is defined, then libxl supports remus. */ #define LIBXL_HAVE_REMUS 1 /* * LIBXL_HAVE_COLO_USERSPACE_PROXY * If this is defined, then libxl supports COLO userspace proxy. */ #define LIBXL_HAVE_COLO_USERSPACE_PROXY 1 typedef uint8_t libxl_mac[6]; #define LIBXL_MAC_FMT "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx" #define LIBXL_MAC_FMTLEN ((2*6)+5) /* 6 hex bytes plus 5 colons */ #define LIBXL_MAC_BYTES(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src); #if defined(__i386__) || defined(__x86_64__) /* * LIBXL_HAVE_PSR_CMT * * If this is defined, the Cache Monitoring Technology feature is supported. */ #define LIBXL_HAVE_PSR_CMT 1 /* * LIBXL_HAVE_PSR_MBM * * If this is defined, the Memory Bandwidth Monitoring feature is supported. */ #define LIBXL_HAVE_PSR_MBM 1 /* * LIBXL_HAVE_PSR_CAT * * If this is defined, the Cache Allocation Technology feature is supported. */ #define LIBXL_HAVE_PSR_CAT 1 /* * LIBXL_HAVE_PSR_CDP * * If this is defined, the Code and Data Prioritization feature is supported. */ #define LIBXL_HAVE_PSR_CDP 1 #endif /* * LIBXL_HAVE_PCITOPOLOGY * * If this is defined, then interface to query hypervisor about PCI device * topology is available. */ #define LIBXL_HAVE_PCITOPOLOGY 1 /* * LIBXL_HAVE_SOCKET_BITMAP * * If this is defined, then libxl_socket_bitmap_alloc and * libxl_get_online_socketmap exist. */ #define LIBXL_HAVE_SOCKET_BITMAP 1 /* * LIBXL_HAVE_SRM_V2 * * If this is defined, then the libxl_domain_create_restore() interface takes * a "stream_version" parameter and supports a value of 2. * * libxl_domain_suspend() will produce a v2 stream. */ #define LIBXL_HAVE_SRM_V2 1 /* * LIBXL_HAVE_SRM_V1 * * In the case that LIBXL_HAVE_SRM_V2 is set, LIBXL_HAVE_SRM_V1 * indicates that libxl_domain_create_restore() can handle a "stream_version" * parameter of 1, and convert the stream format automatically. */ #define LIBXL_HAVE_SRM_V1 1 /* * libxl_domain_build_info has the u.hvm.gfx_passthru_kind field and * the libxl_gfx_passthru_kind enumeration is defined. */ #define LIBXL_HAVE_GFX_PASSTHRU_KIND /* * LIBXL_HAVE_DEVICE_MODEL_VERSION_NONE * * In the case that LIBXL_HAVE_DEVICE_MODEL_VERSION_NONE is set libxl * allows the creation of HVM guests without a device model. */ #define LIBXL_HAVE_DEVICE_MODEL_VERSION_NONE 1 /* * LIBXL_HAVE_CHECKPOINTED_STREAM * * If this is defined, then libxl_checkpointed_stream exists. */ #define LIBXL_HAVE_CHECKPOINTED_STREAM 1 /* * LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE * * libxl_domain_build_info has u.hvm.system_firmware field which can be use * to provide a different firmware blob (like SeaBIOS or OVMF). */ #define LIBXL_HAVE_BUILDINFO_HVM_SYSTEM_FIRMWARE /* * ERROR_REMUS_XXX error code only exists from Xen 4.5, Xen 4.6 and it * is changed to ERROR_CHECKPOINT_XXX in Xen 4.7 */ #if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040500 \ && LIBXL_API_VERSION < 0x040700 #define ERROR_REMUS_DEVOPS_DOES_NOT_MATCH \ ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH #define ERROR_REMUS_DEVICE_NOT_SUPPORTED \ ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED #endif /* * LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN * * In the case that LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN is set the * libxl_vga_interface_type enumeration type contains a * LIBXL_VGA_INTERFACE_TYPE_UNKNOWN identifier. This is used to signal * that a libxl_vga_interface_type type has not been initialized yet. */ #define LIBXL_HAVE_VGA_INTERFACE_TYPE_UNKNOWN 1 /* * LIBXL_HAVE_BYTEARRAY_UUID * * If this is defined, the internal member of libxl_uuid is defined * as a 16 byte array that contains the UUID in big endian format. * Also, the same structure layout is used across all OSes. */ #define LIBXL_HAVE_BYTEARRAY_UUID 1 /* * LIBXL_HAVE_MEMKB_64BITS * * If this is defined libxl_set_memory_target(), libxl_domain_setmaxmem() * and libxl_wait_for_free_memory() will take a 64 bit value for the memory * size parameter. * From Xen 4.8 on libxl_get_memory_target(), libxl_domain_need_memory() and * libxl_get_free_memory() return the memory size in a 64 bit value, too. */ #define LIBXL_HAVE_MEMKB_64BITS 1 /* * LIBXL_HAVE_QED * * If this is defined QED disk formats can be used for both HVM and PV guests. */ #define LIBXL_HAVE_QED 1 typedef char **libxl_string_list; void libxl_string_list_dispose(libxl_string_list *sl); int libxl_string_list_length(const libxl_string_list *sl); void libxl_string_list_copy(libxl_ctx *ctx, libxl_string_list *dst, const libxl_string_list *src); typedef char **libxl_key_value_list; void libxl_key_value_list_dispose(libxl_key_value_list *kvl); int libxl_key_value_list_length(const libxl_key_value_list *kvl); void libxl_key_value_list_copy(libxl_ctx *ctx, libxl_key_value_list *dst, const libxl_key_value_list *src); typedef uint32_t libxl_hwcap[8]; void libxl_hwcap_copy(libxl_ctx *ctx, libxl_hwcap *dst, const libxl_hwcap *src); typedef uint64_t libxl_ev_user; typedef struct { uint32_t size; /* number of bytes in map */ uint8_t *map; } libxl_bitmap; void libxl_bitmap_init(libxl_bitmap *map); void libxl_bitmap_dispose(libxl_bitmap *map); /* libxl_cpuid_policy_list is a dynamic array storing CPUID policies * for multiple leafs. It is terminated with an entry holding * XEN_CPUID_INPUT_UNUSED in input[0] */ typedef struct libxl__cpuid_policy libxl_cpuid_policy; typedef libxl_cpuid_policy * libxl_cpuid_policy_list; void libxl_cpuid_dispose(libxl_cpuid_policy_list *cpuid_list); int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *l); void libxl_cpuid_policy_list_copy(libxl_ctx *ctx, libxl_cpuid_policy_list *dst, const libxl_cpuid_policy_list *src); #define LIBXL_PCI_FUNC_ALL (~0U) typedef uint32_t libxl_domid; typedef int libxl_devid; /* * Formatting Enumerations. * * Each enumeration type libxl_E declares an associated lookup table * libxl_E_string_table and a lookup function libxl_E_from_string. */ typedef struct { const char *s; int v; } libxl_enum_string_table; struct libxl_event; typedef LIBXL_TAILQ_ENTRY(struct libxl_event) libxl_ev_link; /* * A boolean variable with an explicit default state. * * Users should treat this struct as opaque and use the following * defined macros and accessor functions. * * To allow users of the library to naively select all defaults this * state is represented as 0. False is < 0 and True is > 0. */ typedef struct { int val; } libxl_defbool; void libxl_defbool_set(libxl_defbool *db, bool b); /* Resets to default */ void libxl_defbool_unset(libxl_defbool *db); /* Sets db only if it is currently == default */ void libxl_defbool_setdefault(libxl_defbool *db, bool b); bool libxl_defbool_is_default(libxl_defbool db); /* db must not be == default */ bool libxl_defbool_val(libxl_defbool db); const char *libxl_defbool_to_string(libxl_defbool b); #define LIBXL_TIMER_MODE_DEFAULT -1 #define LIBXL_MEMKB_DEFAULT ~0ULL /* * We'd like to set a memory boundary to determine if we need to check * any overlap with reserved device memory. */ #define LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT (2048 * 1024) #define LIBXL_MS_VM_GENID_LEN 16 typedef struct { uint8_t bytes[LIBXL_MS_VM_GENID_LEN]; } libxl_ms_vm_genid; #include "_libxl_types.h" const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx); /* * Some libxl operations can take a long time. These functions take a * parameter to control their concurrency: * libxl_asyncop_how *ao_how * * If ao_how==NULL, the function will be synchronous. * * If ao_how!=NULL, the function will set the operation going, and if * this is successful will return 0. In this case the zero error * response does NOT mean that the operation was successful; it just * means that it has been successfully started. It will finish later, * perhaps with an error. * * If ao_how->callback!=NULL, the callback will be called when the * operation completes. The same rules as for libxl_event_hooks * apply, including the reentrancy rules and the possibility of * "disaster", except that libxl calls ao_how->callback instead of * libxl_event_hooks.event_occurs. (See libxl_event.h.) * * If ao_how->callback==NULL, a libxl_event will be generated which * can be obtained from libxl_event_wait or libxl_event_check. The * event will have type OPERATION_COMPLETE (which is not used * elsewhere). * * Note that it is possible for an asynchronous operation which is to * result in a callback to complete during its initiating function * call. In this case the initiating function will return 0 * indicating the at the operation is "in progress", even though by * the time it returns the operation is complete and the callback has * already happened. * * The application must set and use ao_how->for_event (which will be * copied into libxl_event.for_user) or ao_how->for_callback (passed * to the callback) to determine which operation finished, and it must * of course check the rc value for errors. * * *ao_how does not need to remain valid after the initiating function * returns. All other parameters must remain valid for the lifetime of * the asynchronous operation, unless otherwise specified. * * Callbacks may occur on any thread in which the application calls * libxl. */ typedef struct { void (*callback)(libxl_ctx *ctx, int rc, void *for_callback); union { libxl_ev_user for_event; /* used if callback==NULL */ void *for_callback; /* passed to callback */ } u; } libxl_asyncop_how; /* * Some more complex asynchronous operations can report intermediate * progress. How this is to be reported is controlled, for each * function, by a parameter * libxl_asyncprogress_how *aop_FOO_how; * for each kind of progress FOO supported by that function. Each * such kind of progress is associated with an event type. * * The function description will document whether, when, and how * many times, the intermediate progress will be reported, and * what the corresponding event type(s) are. * * If aop_FOO_how==NULL, intermediate progress reports are discarded. * * If aop_FOO_how->callback==NULL, intermediate progress reports * generate libxl events which can be obtained from libxl_event_wait * or libxl_event_check. * * If aop_FOO_how->callback!=NULL, libxl will report intermediate * progress by calling callback(ctx, &event, for_callback). * * The rules for these events are otherwise the same as those for * ordinary events. The reentrancy and threading rules for the * callback are the same as those for ao completion callbacks. * * Note that the callback, if provided, is responsible for freeing * the event. * * If callbacks are requested, they will be made, and returned, before * the long-running libxl operation is considered finished (so if the * long-running libxl operation was invoked with ao_how==NULL then any * callbacks will occur strictly before the long-running operation * returns). However, the callbacks may occur on any thread. * * In general, otherwise, no promises are made about the relative * order of callbacks in a multithreaded program. In particular * different callbacks relating to the same long-running operation may * be delivered out of order. */ typedef struct { void (*callback)(libxl_ctx *ctx, libxl_event*, void *for_callback); libxl_ev_user for_event; /* always used */ void *for_callback; /* passed to callback */ } libxl_asyncprogress_how; /* * It is sometimes possible to abort an asynchronous operation. * * libxl_ao_abort searches for an ongoing asynchronous operation whose * ao_how is identical to *how, and tries to abort it. The return * values from libxl_ao_abort are as follows: * * 0 * * The operation was found, and attempts are being made to cut it * short. However, it may still take some time to stop. It is * also possible that the operation will nevertheless complete * successfully. * * ERROR_NOTFOUND * * No matching ongoing operation was found. This might happen * for an actual operation if the operation has already completed * (perhaps on another thread). The call to libxl_ao_abort has * had no effect. * * ERROR_ABORTED * * The operation has already been the subject of at least one * call to libxl_ao_abort. * * If the operation was indeed cut short due to the abort request, it * will complete, at some point in the future, with ERROR_ABORTED. In * that case, depending on the operation it have performed some of the * work in question and left the operation half-done. Consult the * documentation for individual operations. * * Note that an aborted operation might still fail for other reasons * even after the abort was requested. * * If your application is multithreaded you must not reuse an * ao_how->for_event or ao_how->for_callback value (with a particular * ao_how->callback) unless you are sure that none of your other * threads are going to abort the previous operation using that * value; otherwise you risk aborting the wrong operation if the * intended target of the abort request completes in the meantime. * * It is possible to abort even an operation which is being performed * synchronously, but since in that case how==NULL you had better only * have one such operation, because it is not possible to tell them * apart (and libxl_ao_abort will abort only the first one it finds). * (And, if you want to do this, obviously the abort would have to be * requested on a different thread.) */ int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how) LIBXL_EXTERNAL_CALLERS_ONLY; #define LIBXL_VERSION 0 /* context functions */ int libxl_ctx_alloc(libxl_ctx **pctx, int version, unsigned flags /* none currently defined */, xentoollog_logger *lg); int libxl_ctx_free(libxl_ctx *ctx /* 0 is OK */); /* domain related functions */ /* If the result is ERROR_ABORTED, the domain may or may not exist * (in a half-created state). *domid will be valid and will be the * domain id, or -1, as appropriate */ int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t *domid, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t *domid, int restore_fd, int send_back_fd, const libxl_domain_restore_params *params, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) LIBXL_EXTERNAL_CALLERS_ONLY; #if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040400 static inline int libxl_domain_create_restore_0x040200( libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t *domid, int restore_fd, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) LIBXL_EXTERNAL_CALLERS_ONLY { libxl_domain_restore_params params; int ret; libxl_domain_restore_params_init(¶ms); ret = libxl_domain_create_restore( ctx, d_config, domid, restore_fd, -1, ¶ms, ao_how, aop_console_how); libxl_domain_restore_params_dispose(¶ms); return ret; } #define libxl_domain_create_restore libxl_domain_create_restore_0x040200 #elif defined(LIBXL_API_VERSION) && LIBXL_API_VERSION >= 0x040400 \ && LIBXL_API_VERSION < 0x040700 static inline int libxl_domain_create_restore_0x040400( libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t *domid, int restore_fd, const libxl_domain_restore_params *params, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) LIBXL_EXTERNAL_CALLERS_ONLY { return libxl_domain_create_restore(ctx, d_config, domid, restore_fd, -1, params, ao_how, aop_console_how); } #define libxl_domain_create_restore libxl_domain_create_restore_0x040400 #endif int libxl_domain_soft_reset(libxl_ctx *ctx, libxl_domain_config *d_config, uint32_t domid, const libxl_asyncop_how *ao_how, const libxl_asyncprogress_how *aop_console_how) LIBXL_EXTERNAL_CALLERS_ONLY; /* A progress report will be made via ao_console_how, of type * domain_create_console_available, when the domain's primary * console is available and can be connected to. */ void libxl_domain_config_init(libxl_domain_config *d_config); void libxl_domain_config_dispose(libxl_domain_config *d_config); /* * Retrieve domain configuration and filled it in d_config. The * returned configuration can be used to rebuild a domain. It only * works with DomU. */ int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid, libxl_domain_config *d_config) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags, /* LIBXL_SUSPEND_* */ const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; #define LIBXL_SUSPEND_DEBUG 1 #define LIBXL_SUSPEND_LIVE 2 /* @param suspend_cancel [from xenctrl.h:xc_domain_resume( @param fast )] * If this parameter is true, use co-operative resume. The guest * must support this. */ int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; /* * This function doesn't return unless something has gone wrong with * the replication to the secondary. If this function returns then the * caller should resume the (primary) domain. */ int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info, uint32_t domid, int send_fd, int recv_fd, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid); int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid); int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid, libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid); /* get max. number of cpus supported by hypervisor */ int libxl_get_max_cpus(libxl_ctx *ctx); /* get the actual number of currently online cpus on the host */ int libxl_get_online_cpus(libxl_ctx *ctx); /* Beware that no locking or serialization is provided by libxl, * so the information can be outdated as far as the function * returns. If there are other entities in the system capable * of onlining/offlining CPUs, it is up to the application * to guarantee consistency, if that is important. */ /* get max. number of NUMA nodes supported by hypervisor */ int libxl_get_max_nodes(libxl_ctx *ctx); int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid, const char *old_name, const char *new_name); /* if old_name is NULL, any old name is OK; otherwise we check * transactionally that the domain has the old old name; if * trans is not 0 we use caller's transaction and caller must do retries */ int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid); int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid); int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid, const char *filename, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t target_memkb); int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid, int64_t target_memkb, int relative, int enforce); int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid, uint64_t *out_target); int libxl_get_memory_target_0x040700(libxl_ctx *ctx, uint32_t domid, uint32_t *out_target) LIBXL_EXTERNAL_CALLERS_ONLY; /* * WARNING * This memory management API is unstable even in Xen 4.2. * It has a numer of deficiencies and we intend to replace it. * * The semantics of these functions should not be relied on to be very * coherent or stable. We will however endeavour to keep working * existing programs which use them in roughly the same way as libxl. */ /* how much free memory in the system a domain needs to be built */ int libxl_domain_need_memory(libxl_ctx *ctx, const libxl_domain_build_info *b_info_in, uint64_t *need_memkb); int libxl_domain_need_memory_0x040700(libxl_ctx *ctx, const libxl_domain_build_info *b_info_in, uint32_t *need_memkb) LIBXL_EXTERNAL_CALLERS_ONLY; /* how much free memory is available in the system */ int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb); int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb) LIBXL_EXTERNAL_CALLERS_ONLY; /* wait for a given amount of memory to be free in the system */ int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint64_t memory_kb, int wait_secs); /* * Wait for the memory target of a domain to be reached. Does not * decrement wait_secs if the domain is making progress toward reaching * the target. If the domain is not making progress, wait_secs is * decremented. If the timeout expires before the target is reached, the * function returns ERROR_FAIL. * * Older versions of this function (Xen 4.5 and older), decremented * wait_secs even if the domain was making progress, resulting in far * lower overall wait times. To make sure that your calling routine * works with new and old implementations of the function, pass enough * time for the guest to reach its target as an argument. */ int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs); #if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800 #define libxl_get_memory_target libxl_get_memory_target_0x040700 #define libxl_domain_need_memory libxl_domain_need_memory_0x040700 #define libxl_get_free_memory libxl_get_free_memory_0x040700 #endif int libxl_vncviewer_exec(libxl_ctx *ctx, uint32_t domid, int autopass); /* * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity * the caller that it has connected to the guest console. */ int libxl_console_exec(libxl_ctx *ctx, uint32_t domid, int cons_num, libxl_console_type type, int notify_fd); /* libxl_primary_console_exec finds the domid and console number * corresponding to the primary console of the given vm, then calls * libxl_console_exec with the right arguments (domid might be different * if the guest is using stubdoms). * This function can be called after creating the device model, in * case of HVM guests, and before libxl_run_bootloader in case of PV * guests using pygrub. * If notify_fd is not -1, xenconsole will write 0x00 to it to nofity * the caller that it has connected to the guest console. */ int libxl_primary_console_exec(libxl_ctx *ctx, uint32_t domid_vm, int notify_fd); #if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040800 static inline int libxl_console_exec_0x040700(libxl_ctx *ctx, uint32_t domid, int cons_num, libxl_console_type type) { return libxl_console_exec(ctx, domid, cons_num, type, -1); } #define libxl_console_exec libxl_console_exec_0x040700 static inline int libxl_primary_console_exec_0x040700(libxl_ctx *ctx, uint32_t domid_vm) { return libxl_primary_console_exec(ctx, domid_vm, -1); } #define libxl_primary_console_exec libxl_primary_console_exec_0x040700 #endif /* libxl_console_get_tty retrieves the specified domain's console tty path * and stores it in path. Caller is responsible for freeing the memory. */ int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num, libxl_console_type type, char **path); /* libxl_primary_console_get_tty retrieves the specified domain's primary * console tty path and stores it in path. Caller is responsible for freeing * the memory. */ int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm, char **path); /* May be called with info_r == NULL to check for domain's existence. * Returns ERROR_DOMAIN_NOTFOUND if domain does not exist (used to return * ERROR_INVAL for this scenario). */ int libxl_domain_info(libxl_ctx*, libxl_dominfo *info_r, uint32_t domid); /* These functions each return (on success) an array of elements, * and the length via the int* out parameter. These arrays and * their contents come from malloc, and must be freed with the * corresponding libxl_THING_list_free function. */ libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out); void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain); libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out); void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool); libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out); void libxl_vminfo_list_free(libxl_vminfo *list, int nb_vm); #define LIBXL_CPUTOPOLOGY_INVALID_ENTRY (~(uint32_t)0) libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out); void libxl_cputopology_list_free(libxl_cputopology *, int nb_cpu); #define LIBXL_PCITOPOLOGY_INVALID_ENTRY (~(uint32_t)0) libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs); void libxl_pcitopology_list_free(libxl_pcitopology *, int num_devs); #define LIBXL_NUMAINFO_INVALID_ENTRY (~(uint32_t)0) libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr); void libxl_numainfo_list_free(libxl_numainfo *, int nr); libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid, int *nb_vcpu, int *nr_cpus_out); void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus); void libxl_device_vtpm_list_free(libxl_device_vtpm*, int nr_vtpms); void libxl_vtpminfo_list_free(libxl_vtpminfo *, int nr_vtpms); /* * Devices * ======= * * Each device is represented by a libxl_device_ data structure * which is defined via the IDL. In addition some devices have an * additional data type libxl_device__getinfo which contains * further runtime information about the device. * * In addition to the general methods available for libxl types (see * "libxl types" above) a common set of methods are available for each * device type. These are described below. * * Querying * -------- * * libxl_device__list(ctx, domid, nr): * * Returns an array of libxl_device_ length nr representing * the devices attached to the specified domain. * * libxl_device__getinfo(ctx, domid, device, info): * * Initialises info with details of the given device which must be * attached to the specified domain. * * Creation / Control * ------------------ * * libxl_device__add(ctx, domid, device): * * Adds the given device to the specified domain. This can be called * while the guest is running (hotplug) or before boot (coldplug). * * This function only sets up the device but does not wait for the * domain to connect to the device and therefore cannot block on the * guest. * * device is an in/out parameter: fields left unspecified when the * structure is passed in are filled in with appropriate values for * the device created. * * libxl_device__remove(ctx, domid, device): * * Removes the given device from the specified domain by performing * an orderly unplug with guest co-operation. This requires that the * guest is running. * * This method is currently synchronous and therefore can block * while interacting with the guest. * * libxl_device__destroy(ctx, domid, device): * * Removes the given device from the specified domain without guest * co-operation. It is guest specific what affect this will have on * a running guest. * * This function does not interact with the guest and therefore * cannot block on the guest. * * Controllers * ----------- * * Most devices are treated individually. Some classes of device, * however, like USB or SCSI, inherently have the need to have a * hierarchy of different levels, with lower-level devices "attached" * to higher-level ones. USB for instance has "controllers" at the * top, which have buses, on which are devices, which consist of * multiple interfaces. SCSI has "hosts" at the top, then buses, * targets, and LUNs. * * In that case, for each , there will be a set of functions * and types for each . For example, for =usb, there * may be ctrl (controller) and dev (device), with ctrl being * level 0. * * libxl_device__ will act more or * less like top-level non-bus devices: they will either create or * accept a libxl_devid which will be unique within the * libxl_devid namespace. * * Lower-level devices must have a unique way to be identified. One * way to do this would be to name it via the name of the next level * up plus an index; for instance, . Another * way would be to have another devid namespace for that level. This * identifier will be used for queries and removals. * * Lower-level devices will include in their * libxl_device_ struct a field referring to the unique * index of the level above. For instance, libxl_device_usbdev might * contain the controller devid. * * In the case where there are multiple different ways to implement a * given device -- for instance, one which is fully PV and one which * uses an emulator -- the controller will contain a field which * specifies what type of implementation is used. The implementations * of individual devices will be known by the controller to which they * are attached. * * If libxl_device__add receives an empty reference to * the level above, it may return an error. Or it may (but is not * required to) automatically choose a suitable device in the level * above to which to attach the new device at this level. It may also * (but is not required to) automatically create a new device at the * level above if no suitable devices exist. Each class should * document its behavior. * * libxl_device__list will list all devices of * at in the domain. For example, libxl_device_usbctrl_list * will list all usb controllers; libxl_class_usbdev_list will list * all usb devices across all controllers. * * For each class, the domain config file will contain a single list * for each level. libxl will first iterate through the list of * top-level devices, then iterate through each level down in turn, * adding devices to devices in the level above. For instance, there * will be one list for all usb controllers, and one list for all usb * devices. * * If libxl_device__add automatically creates * higher-level devices as necessary, then it is permissible for the * higher-level lists to be empty and the device list to have devices * with the field containing a reference to the higher level device * uninitialized. */ /* Disks */ int libxl_device_disk_add(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_disk_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_disk_destroy(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx, uint32_t domid, int *num); int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, libxl_diskinfo *diskinfo); /* * Insert a CD-ROM device. A device corresponding to disk must already * be attached to the guest. */ int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; /* * USB * * For each device removed or added, one of these protocols is available: * - PV (i.e., PVUSB) * - DEVICEMODEL (i.e, qemu) * * PV is available for either PV or HVM domains. DEVICEMODEL is only * available for HVM domains. The caller can additionally specify * "AUTO", in which case the library will try to determine the best * protocol automatically. * * At the moment, the only protocol implemented is PV. * * One can add/remove USB controllers to/from guest, and attach/detach USB * devices to/from USB controllers. * * To add USB controllers and USB devices, one can adding USB controllers * first and then attaching USB devices to some USB controller, or adding * USB devices to guest directly, it will automatically create a USB * controller for USB devices to attach. * * To remove USB controllers or USB devices, one can remove USB devices * under USB controller one by one and then remove USB controller, or * remove USB controller directly, it will remove all USB devices under * it automatically. * */ /* USB Controllers*/ int libxl_device_usbctrl_add(libxl_ctx *ctx, uint32_t domid, libxl_device_usbctrl *usbctrl, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_usbctrl_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_usbctrl *usbctrl, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_usbctrl_destroy(libxl_ctx *ctx, uint32_t domid, libxl_device_usbctrl *usbctrl, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; libxl_device_usbctrl *libxl_device_usbctrl_list(libxl_ctx *ctx, uint32_t domid, int *num); void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr); int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_usbctrl *usbctrl, libxl_usbctrlinfo *usbctrlinfo); /* USB Devices */ int libxl_device_usbdev_add(libxl_ctx *ctx, uint32_t domid, libxl_device_usbdev *usbdev, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_usbdev *usbdev, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; libxl_device_usbdev * libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num); void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr); /* Network Interfaces */ int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_nic_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_nic_destroy(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx, uint32_t domid, int *num); int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic, libxl_nicinfo *nicinfo); /* * Virtual Channels * Channels manifest as consoles with names, see docs/misc/channels.txt */ libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx, uint32_t domid, int *num); int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_channel *channel, libxl_channelinfo *channelinfo); /* Virtual TPMs */ int libxl_device_vtpm_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_vtpm_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_vtpm_destroy(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; libxl_device_vtpm *libxl_device_vtpm_list(libxl_ctx *ctx, uint32_t domid, int *num); int libxl_device_vtpm_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm, libxl_vtpminfo *vtpminfo); /* Keyboard */ int libxl_device_vkb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_vkb_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_vkb_destroy(libxl_ctx *ctx, uint32_t domid, libxl_device_vkb *vkb, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; /* Framebuffer */ int libxl_device_vfb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_vfb_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_vfb_destroy(libxl_ctx *ctx, uint32_t domid, libxl_device_vfb *vfb, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; /* 9pfs */ int libxl_device_p9_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_p9 *p9, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_p9_destroy(libxl_ctx *ctx, uint32_t domid, libxl_device_p9 *p9, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; /* PCI Passthrough */ int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; int libxl_device_pci_destroy(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; libxl_device_pci *libxl_device_pci_list(libxl_ctx *ctx, uint32_t domid, int *num); /* * Turns the current process into a backend device service daemon * for a driver domain. * * From a libxl API point of view, this starts a long-running * operation. That operation consists of "being a driver domain" * and never completes. * * Attempting to abort this operation is not advisable; proper * shutdown of the driver domain task is not supported. */ int libxl_device_events_handler(libxl_ctx *ctx, const libxl_asyncop_how *ao_how) LIBXL_EXTERNAL_CALLERS_ONLY; /* * Functions related to making devices assignable -- that is, bound to * the pciback driver, ready to be given to a guest via * libxl_pci_device_add. * * - ..._add() will unbind the device from its current driver (if * already bound) and re-bind it to pciback; at that point it will be * ready to be assigned to a VM. If rebind is set, it will store the * path to the old driver in xenstore so that it can be handed back to * dom0 on restore. * * - ..._remove() will unbind the device from pciback, and if * rebind is non-zero, attempt to assign it back to the driver * from whence it came. * * - ..._list() will return a list of the PCI devices available to be * assigned. * * add and remove are idempotent: if the device in question is already * added or is not bound, the functions will emit a warning but return * SUCCESS. */ int libxl_device_pci_assignable_add(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind); int libxl_device_pci_assignable_remove(libxl_ctx *ctx, libxl_device_pci *pcidev, int rebind); libxl_device_pci *libxl_device_pci_assignable_list(libxl_ctx *ctx, int *num); /* CPUID handling */ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str); int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid, const char* str); void libxl_cpuid_apply_policy(libxl_ctx *ctx, uint32_t domid); void libxl_cpuid_set(libxl_ctx *ctx, uint32_t domid, libxl_cpuid_policy_list cpuid); /* * Functions for allowing users of libxl to store private data * relating to a domain. The data is an opaque sequence of bytes and * is not interpreted or used by libxl. * * Data is indexed by the userdata userid, which is a short printable * ASCII string. The following list is a registry of userdata userids * (the registry may be updated by posting a patch to xen-devel): * * userid Data contents * "xl" domain config file in xl format, Unix line endings * "libvirt-xml" domain config file in libvirt XML format. See * http://libvirt.org/formatdomain.html * "domain-userdata-lock" lock file to protect domain userdata in libxl. * It's a per-domain lock. Applications should * not touch this file. * "libxl-json" libxl_domain_config object in JSON format, generated * by libxl. Applications should not access this file * directly. This file is protected by domain-userdata-lock * for against Read-Modify-Write operation and domain * destruction. * * libxl does not enforce the registration of userdata userids or the * semantics of the data. For specifications of the data formats * see the code or documentation for the libxl caller in question. */ int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid, const char *userdata_userid, const uint8_t *data, int datalen) LIBXL_EXTERNAL_CALLERS_ONLY; /* If datalen==0, data is not used and the user data for * that domain and userdata_userid is deleted. */ int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid, const char *userdata_userid, uint8_t **data_r, int *datalen_r) LIBXL_EXTERNAL_CALLERS_ONLY; /* On successful return, *data_r is from malloc. * If there is no data for that domain and userdata_userid, * *data_r and *datalen_r will be set to 0. * data_r and datalen_r may be 0. * On error return, *data_r and *datalen_r are undefined. */ int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid, const char *userdata_userid); int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo); int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid, const libxl_bitmap *cpumap_hard, const libxl_bitmap *cpumap_soft); int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid, const libxl_bitmap *cpumap_hard, const libxl_bitmap *cpumap_soft); int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid, unsigned int max_vcpus, const libxl_bitmap *cpumap_hard, const libxl_bitmap *cpumap_soft); #if defined (LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500 #define libxl_set_vcpuaffinity(ctx, domid, vcpuid, map) \ libxl_set_vcpuaffinity((ctx), (domid), (vcpuid), (map), NULL) #define libxl_set_vcpuaffinity_all(ctx, domid, max_vcpus, map) \ libxl_set_vcpuaffinity_all((ctx), (domid), (max_vcpus), (map), NULL) #endif int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid, libxl_bitmap *nodemap); int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid, libxl_bitmap *nodemap); int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid, libxl_bitmap *cpumap); /* A return value less than 0 should be interpreted as a libxl_error, while a * return value greater than or equal to 0 should be interpreted as a * libxl_scheduler. */ int libxl_get_scheduler(libxl_ctx *ctx); /* Per-scheduler parameters */ int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid, libxl_sched_credit_params *scinfo); int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid, libxl_sched_credit_params *scinfo); int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid, libxl_sched_credit2_params *scinfo); int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid, libxl_sched_credit2_params *scinfo); /* Scheduler Per-domain parameters */ #define LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT -1 #define LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT -1 #define LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT -1 #define LIBXL_DOMAIN_SCHED_PARAM_SLICE_DEFAULT -1 #define LIBXL_DOMAIN_SCHED_PARAM_LATENCY_DEFAULT -1 #define LIBXL_DOMAIN_SCHED_PARAM_EXTRATIME_DEFAULT -1 #define LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT -1 /* Per-VCPU parameters */ #define LIBXL_SCHED_PARAM_VCPU_INDEX_DEFAULT -1 /* Get the per-domain scheduling parameters. * For schedulers that support per-vcpu settings (e.g., RTDS), * calling *_domain_get functions will get default scheduling * parameters. */ int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid, libxl_domain_sched_params *params); /* Set the per-domain scheduling parameters. * For schedulers that support per-vcpu settings (e.g., RTDS), * calling *_domain_set functions will set all vcpus with the same * scheduling parameters. */ int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid, const libxl_domain_sched_params *params); /* Get the per-vcpu scheduling parameters */ int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid, libxl_vcpu_sched_params *params); /* Get the per-vcpu scheduling parameters of all vcpus of a domain */ int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid, libxl_vcpu_sched_params *params); /* Set the per-vcpu scheduling parameters */ int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid, const libxl_vcpu_sched_params *params); /* Set the per-vcpu scheduling parameters of all vcpus of a domain */ int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid, const libxl_vcpu_sched_params *params); int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid, libxl_trigger trigger, uint32_t vcpuid); int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq); int libxl_send_debug_keys(libxl_ctx *ctx, char *keys); typedef struct libxl__xen_console_reader libxl_xen_console_reader; libxl_xen_console_reader * libxl_xen_console_read_start(libxl_ctx *ctx, int clear); int libxl_xen_console_read_line(libxl_ctx *ctx, libxl_xen_console_reader *cr, char **line_r); void libxl_xen_console_read_finish(libxl_ctx *ctx, libxl_xen_console_reader *cr); uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid); char *libxl_tmem_list(libxl_ctx *ctx, uint32_t domid, int use_long); int libxl_tmem_freeze(libxl_ctx *ctx, uint32_t domid); int libxl_tmem_thaw(libxl_ctx *ctx, uint32_t domid); int libxl_tmem_set(libxl_ctx *ctx, uint32_t domid, char* name, uint32_t set); int libxl_tmem_shared_auth(libxl_ctx *ctx, uint32_t domid, char* uuid, int auth); int libxl_tmem_freeable(libxl_ctx *ctx); int libxl_get_freecpus(libxl_ctx *ctx, libxl_bitmap *cpumap); /* * Set poolid to LIBXL_CPUOOL_POOLID_ANY to have Xen choose a * free poolid for you. */ #define LIBXL_CPUPOOL_POOLID_ANY 0xFFFFFFFF int libxl_cpupool_create(libxl_ctx *ctx, const char *name, libxl_scheduler sched, libxl_bitmap cpumap, libxl_uuid *uuid, uint32_t *poolid); int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid); int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid); int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu); int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid, const libxl_bitmap *cpumap); int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu); int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus); int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid, const libxl_bitmap *cpumap); int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid); int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid); int libxl_domid_valid_guest(uint32_t domid); int libxl_flask_context_to_sid(libxl_ctx *ctx, char *buf, size_t len, uint32_t *ssidref); int libxl_flask_sid_to_context(libxl_ctx *ctx, uint32_t ssidref, char **buf, size_t *len); int libxl_flask_getenforce(libxl_ctx *ctx); int libxl_flask_setenforce(libxl_ctx *ctx, int mode); int libxl_flask_loadpolicy(libxl_ctx *ctx, void *policy, uint32_t size); int libxl_ms_vm_genid_generate(libxl_ctx *ctx, libxl_ms_vm_genid *id); bool libxl_ms_vm_genid_is_zero(const libxl_ms_vm_genid *id); void libxl_ms_vm_genid_copy(libxl_ctx *ctx, libxl_ms_vm_genid *dst, const libxl_ms_vm_genid *src); #ifdef LIBXL_HAVE_PSR_CMT int libxl_psr_cmt_attach(libxl_ctx *ctx, uint32_t domid); int libxl_psr_cmt_detach(libxl_ctx *ctx, uint32_t domid); int libxl_psr_cmt_domain_attached(libxl_ctx *ctx, uint32_t domid); int libxl_psr_cmt_enabled(libxl_ctx *ctx); int libxl_psr_cmt_get_total_rmid(libxl_ctx *ctx, uint32_t *total_rmid); int libxl_psr_cmt_get_l3_cache_size(libxl_ctx *ctx, uint32_t socketid, uint32_t *l3_cache_size); int libxl_psr_cmt_get_cache_occupancy(libxl_ctx *ctx, uint32_t domid, uint32_t socketid, uint32_t *l3_cache_occupancy); #endif #ifdef LIBXL_HAVE_PSR_MBM int libxl_psr_cmt_type_supported(libxl_ctx *ctx, libxl_psr_cmt_type type); int libxl_psr_cmt_get_sample(libxl_ctx *ctx, uint32_t domid, libxl_psr_cmt_type type, uint64_t scope, uint64_t *sample_r, uint64_t *tsc_r); #endif #ifdef LIBXL_HAVE_PSR_CAT /* * Function to set a domain's cbm. It operates on a single or multiple * target(s) defined in 'target_map'. The definition of 'target_map' is * related to 'type': * 'L3_CBM': 'target_map' specifies all the sockets to be operated on. */ int libxl_psr_cat_set_cbm(libxl_ctx *ctx, uint32_t domid, libxl_psr_cbm_type type, libxl_bitmap *target_map, uint64_t cbm); /* * Function to get a domain's cbm. It operates on a single 'target'. * The definition of 'target' is related to 'type': * 'L3_CBM': 'target' specifies which socket to be operated on. */ int libxl_psr_cat_get_cbm(libxl_ctx *ctx, uint32_t domid, libxl_psr_cbm_type type, uint32_t target, uint64_t *cbm_r); /* * On success, the function returns an array of elements in 'info', * and the length in 'nr'. */ int libxl_psr_cat_get_l3_info(libxl_ctx *ctx, libxl_psr_cat_info **info, int *nr); void libxl_psr_cat_info_list_free(libxl_psr_cat_info *list, int nr); #endif /* misc */ /* Each of these sets or clears the flag according to whether the * 2nd parameter is nonzero. On failure, they log, and * return ERROR_FAIL, but also leave errno valid. */ int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec); int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock); /* * Issue a qmp monitor command to the device model of the specified domain. * The function returns the output of the command in a new allocated buffer * via output. */ int libxl_qemu_monitor_command(libxl_ctx *ctx, uint32_t domid, const char *command_line, char **output); #include #endif /* LIBXL_H */ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_freebsd.c0000664000175000017500000001660113256712137015703 0ustar smbsmb/* * Copyright (C) 2014 * Author Roger Pau Monne * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" int libxl__try_phy_backend(mode_t st_mode) { if (S_ISREG(st_mode) || S_ISBLK(st_mode) || S_ISCHR(st_mode)) return 1; return 0; } char *libxl__devid_to_localdev(libxl__gc *gc, int devid) { /* This translation table has been copied from the FreeBSD blkfront code. */ const static struct vdev_info { int major; int shift; int base; const char *name; } info[] = { {3, 6, 0, "ada"}, /* ide0 */ {22, 6, 2, "ada"}, /* ide1 */ {33, 6, 4, "ada"}, /* ide2 */ {34, 6, 6, "ada"}, /* ide3 */ {56, 6, 8, "ada"}, /* ide4 */ {57, 6, 10, "ada"}, /* ide5 */ {88, 6, 12, "ada"}, /* ide6 */ {89, 6, 14, "ada"}, /* ide7 */ {90, 6, 16, "ada"}, /* ide8 */ {91, 6, 18, "ada"}, /* ide9 */ {8, 4, 0, "da"}, /* scsi disk0 */ {65, 4, 16, "da"}, /* scsi disk1 */ {66, 4, 32, "da"}, /* scsi disk2 */ {67, 4, 48, "da"}, /* scsi disk3 */ {68, 4, 64, "da"}, /* scsi disk4 */ {69, 4, 80, "da"}, /* scsi disk5 */ {70, 4, 96, "da"}, /* scsi disk6 */ {71, 4, 112, "da"}, /* scsi disk7 */ {128, 4, 128, "da"}, /* scsi disk8 */ {129, 4, 144, "da"}, /* scsi disk9 */ {130, 4, 160, "da"}, /* scsi disk10 */ {131, 4, 176, "da"}, /* scsi disk11 */ {132, 4, 192, "da"}, /* scsi disk12 */ {133, 4, 208, "da"}, /* scsi disk13 */ {134, 4, 224, "da"}, /* scsi disk14 */ {135, 4, 240, "da"}, /* scsi disk15 */ {202, 4, 0, "xbd"}, /* xbd */ {0, 0, 0, NULL}, }; int major = devid >> 8; int minor = devid & 0xff; int i; if (devid & (1 << 28)) return GCSPRINTF("%s%d", "xbd", (devid & ((1 << 28) - 1)) >> 8); for (i = 0; info[i].major; i++) if (info[i].major == major) return GCSPRINTF("%s%d", info[i].name, info[i].base + (minor >> info[i].shift)); return GCSPRINTF("%s%d", "xbd", minor >> 4); } /* Hotplug scripts caller functions */ static int libxl__hotplug_env_nic(libxl__gc *gc, libxl__device *dev, char ***env, int num_exec) { int nr = 0; const int arraysize = 5; libxl_nic_type type; assert(dev->backend_kind == LIBXL__DEVICE_KIND_VIF); /* * On the first pass the PV interface is added to the bridge, * on the second pass the tap interface will also be added. */ type = num_exec == 0 ? LIBXL_NIC_TYPE_VIF : LIBXL_NIC_TYPE_VIF_IOEMU; GCNEW_ARRAY(*env, arraysize); (*env)[nr++] = "iface_dev"; (*env)[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid, dev->devid, type); (*env)[nr++] = "emulated"; (*env)[nr++] = type == LIBXL_NIC_TYPE_VIF_IOEMU ? "1" : "0"; (*env)[nr++] = NULL; assert(nr == arraysize); return 0; } static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action, int num_exec) { libxl_nic_type nictype; char *be_path = libxl__device_backend_path(gc, dev); char *script; int nr = 0, rc; rc = libxl__nic_type(gc, dev, &nictype); if (rc) { LOGD(ERROR, dev->domid, "error when fetching nic type"); rc = ERROR_FAIL; goto out; } /* * For PV domains only one pass is needed (because there's no emulated * interface). For HVM domains two passes are needed in order to add * both the PV and the tap interfaces to the bridge. */ if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) { rc = 0; goto out; } rc = libxl__hotplug_env_nic(gc, dev, env, num_exec); if (rc) goto out; script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "script")); if (!script) { LOGEVD(ERROR, errno, dev->domid, "unable to read script from %s", be_path); rc = ERROR_FAIL; goto out; } const int arraysize = 4; GCNEW_ARRAY(*args, arraysize); (*args)[nr++] = script; (*args)[nr++] = be_path; (*args)[nr++] = (char *) libxl__device_action_to_string(action); (*args)[nr++] = NULL; assert(nr == arraysize); rc = 1; out: return rc; } static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action) { char *be_path = libxl__device_backend_path(gc, dev); char *script; int nr = 0, rc; script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "script")); if (!script) { LOGEVD(ERROR, errno, dev->domid, "unable to read script from %s", be_path); rc = ERROR_FAIL; goto out; } const int arraysize = 4; GCNEW_ARRAY(*args, arraysize); (*args)[nr++] = script; (*args)[nr++] = be_path; (*args)[nr++] = (char *) libxl__device_action_to_string(action); (*args)[nr++] = NULL; assert(nr == arraysize); rc = 1; out: return rc; } int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action, int num_exec) { int rc; switch (dev->backend_kind) { case LIBXL__DEVICE_KIND_VIF: /* * If domain has a stubdom we don't have to execute hotplug scripts * for emulated interfaces */ if ((num_exec > 1) || (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) { rc = 0; goto out; } rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec); break; case LIBXL__DEVICE_KIND_VBD: if (num_exec != 0) { rc = 0; goto out; } rc = libxl__hotplug_disk(gc, dev, args, env, action); break; default: /* No need to execute any hotplug scripts */ rc = 0; break; } out: return rc; } libxl_device_model_version libxl__default_device_model(libxl__gc *gc) { return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN; } int libxl__pci_numdevs(libxl__gc *gc) { return ERROR_NI; } int libxl__pci_topology_init(libxl__gc *gc, physdev_pci_device_t *devs, int num_devs) { return ERROR_NI; } xen-4.9.2/tools/libxl/libxl_netbsd.c0000664000175000017500000000703713256712137015553 0ustar smbsmb/* * Copyright (C) 2011 * Author Roger Pau Monne * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" int libxl__try_phy_backend(mode_t st_mode) { if (S_ISREG(st_mode) || S_ISBLK(st_mode)) return 1; return 0; } char *libxl__devid_to_localdev(libxl__gc *gc, int devid) { /* TODO */ return NULL; } /* Hotplug scripts caller functions */ static int libxl__hotplug(libxl__gc *gc, libxl__device *dev, char ***args, libxl__device_action action) { char *be_path = libxl__device_backend_path(gc, dev); char *script; int nr = 0, rc = 0, arraysize = 4; script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "script")); if (!script) { LOGEVD(ERROR, errno, dev->domid, "unable to read script from %s", be_path); rc = ERROR_FAIL; goto out; } GCNEW_ARRAY(*args, arraysize); (*args)[nr++] = script; (*args)[nr++] = be_path; (*args)[nr++] = GCSPRINTF("%d", action == LIBXL__DEVICE_ACTION_ADD ? XenbusStateInitWait : XenbusStateClosed); (*args)[nr++] = NULL; assert(nr == arraysize); out: return rc; } int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action, int num_exec) { int rc; switch (dev->backend_kind) { case LIBXL__DEVICE_KIND_VBD: if (num_exec != 0) { LOGD(DEBUG, dev->domid, "num_exec %d, not running hotplug scripts", num_exec); rc = 0; goto out; } rc = libxl__hotplug(gc, dev, args, action); if (!rc) rc = 1; break; case LIBXL__DEVICE_KIND_VIF: /* * If domain has a stubdom we don't have to execute hotplug scripts * for emulated interfaces * * NetBSD let QEMU call a script to plug emulated nic, so * only test if num_exec == 0 in that case. */ if ((num_exec != 0) || (libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) { LOGD(DEBUG, dev->domid, "num_exec %d, not running hotplug scripts", num_exec); rc = 0; goto out; } rc = libxl__hotplug(gc, dev, args, action); if (!rc) rc = 1; break; default: /* If no need to execute any hotplug scripts, * call the callback manually */ rc = 0; break; } out: return rc; } libxl_device_model_version libxl__default_device_model(libxl__gc *gc) { return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL; } int libxl__pci_numdevs(libxl__gc *gc) { return ERROR_NI; } int libxl__pci_topology_init(libxl__gc *gc, physdev_pci_device_t *devs, int num_devs) { return ERROR_NI; } xen-4.9.2/tools/libxl/libxlu_cfg_l.c0000664000175000017500000016730213256712137015535 0ustar smbsmb#line 2 "libxlu_cfg_l.c" #line 4 "libxlu_cfg_l.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 39 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE xlu__cfg_yyrestart(yyin ,yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires * access to the local variable yy_act. Since yyless() is a macro, it would break * existing scanners that call yyless() from OUTSIDE xlu__cfg_yylex. * One obvious solution it to make yy_act a global. I tried that, and saw * a 5% performance hit in a non-yylineno scanner, because yy_act is * normally declared as a register variable-- so it is not worth it. */ #define YY_LESS_LINENO(n) \ do { \ int yyl;\ for ( yyl = n; yyl < yyleng; ++yyl )\ if ( yytext[yyl] == '\n' )\ --yylineno;\ }while(0) #define YY_LINENO_REWIND_TO(dst) \ do {\ const char *p;\ for ( p = yy_cp-1; p >= (dst); --p)\ if ( *p == '\n' )\ --yylineno;\ }while(0) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via xlu__cfg_yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void xlu__cfg_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void xlu__cfg_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__cfg_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void xlu__cfg_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void xlu__cfg_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void xlu__cfg_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void xlu__cfg_yypop_buffer_state (yyscan_t yyscanner ); static void xlu__cfg_yyensure_buffer_stack (yyscan_t yyscanner ); static void xlu__cfg_yy_load_buffer_state (yyscan_t yyscanner ); static void xlu__cfg_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); #define YY_FLUSH_BUFFER xlu__cfg_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) YY_BUFFER_STATE xlu__cfg_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__cfg_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__cfg_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); void *xlu__cfg_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *xlu__cfg_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void xlu__cfg_yyfree (void * ,yyscan_t yyscanner ); #define yy_new_buffer xlu__cfg_yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ xlu__cfg_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ xlu__cfg_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ xlu__cfg_yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ xlu__cfg_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) #define xlu__cfg_yywrap(yyscanner) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); static int yy_get_next_buffer (yyscan_t yyscanner ); static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyg->yytext_ptr -= yyg->yy_more_len; \ yyleng = (size_t) (yy_cp - yyg->yytext_ptr); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 16 #define YY_END_OF_BUFFER 17 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[35] = { 0, 0, 0, 14, 14, 17, 13, 3, 9, 13, 13, 13, 12, 4, 2, 8, 7, 5, 6, 1, 14, 14, 15, 0, 11, 0, 0, 9, 0, 10, 0, 2, 1, 14, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 4, 5, 1, 1, 1, 6, 7, 7, 1, 7, 8, 7, 9, 1, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 7, 11, 1, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 13, 14, 15, 1, 16, 1, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 18, 18, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[19] = { 0, 1, 2, 3, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 4, 4, 4 } ; static yyconst flex_int16_t yy_base[41] = { 0, 0, 0, 17, 19, 44, 58, 58, 58, 19, 28, 18, 58, 58, 17, 58, 58, 58, 58, 0, 0, 58, 58, 21, 58, 0, 26, 58, 22, 58, 0, 20, 0, 0, 58, 37, 41, 45, 49, 22, 53 } ; static yyconst flex_int16_t yy_def[41] = { 0, 34, 1, 35, 35, 34, 34, 34, 34, 36, 37, 38, 34, 34, 34, 34, 34, 34, 34, 39, 40, 34, 34, 36, 34, 36, 37, 34, 38, 34, 38, 34, 39, 40, 0, 34, 34, 34, 34, 34, 34 } ; static yyconst flex_int16_t yy_nxt[77] = { 0, 6, 7, 8, 9, 10, 11, 12, 13, 12, 14, 15, 16, 17, 6, 18, 6, 19, 19, 21, 22, 21, 22, 24, 29, 24, 32, 31, 29, 27, 31, 27, 30, 25, 31, 25, 30, 31, 20, 20, 20, 20, 23, 23, 34, 23, 26, 26, 26, 26, 28, 28, 34, 28, 33, 34, 34, 33, 5, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34 } ; static yyconst flex_int16_t yy_chk[77] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4, 9, 11, 23, 39, 14, 28, 26, 31, 10, 11, 9, 14, 23, 28, 31, 35, 35, 35, 35, 36, 36, 5, 36, 37, 37, 37, 37, 38, 38, 0, 38, 40, 0, 0, 40, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34 } ; /* Table of booleans, true if rule could match eol. */ static yyconst flex_int32_t yy_rule_can_match_eol[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, }; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() (yyg->yy_more_flag = 1) #define YY_MORE_ADJ yyg->yy_more_len #define YY_RESTORE_YY_MORE_OFFSET #line 1 "libxlu_cfg_l.l" /* -*- fundamental -*- */ /* * libxlu_cfg_l.l - xl configuration file parsing: lexer * * Copyright (C) 2010 Citrix Ltd. * Author Ian Jackson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #line 20 "libxlu_cfg_l.l" #include "libxlu_cfg_i.h" #define ctx ((CfgParseContext*)yyextra) #define YY_NO_INPUT #define GOT(x) do{ \ yylloc->first_line= yylineno; \ return (x); \ }while(0) /* Some versions of flex have a bug (Fedora bugzilla 612465) which causes * it to fail to declare these functions, which it defines. So declare * them ourselves. Hopefully we won't have to simultaneously support * a flex version which declares these differently somehow. */ int xlu__cfg_yyget_column(yyscan_t yyscanner); void xlu__cfg_yyset_column(int column_no, yyscan_t yyscanner); #line 525 "libxlu_cfg_l.c" #define INITIAL 0 #define lexerr 1 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; yy_size_t yy_n_chars; yy_size_t yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; YYLTYPE * yylloc_r; }; /* end struct yyguts_t */ static int yy_init_globals (yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r # define yylloc yyg->yylloc_r int xlu__cfg_yylex_init (yyscan_t* scanner); int xlu__cfg_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int xlu__cfg_yylex_destroy (yyscan_t yyscanner ); int xlu__cfg_yyget_debug (yyscan_t yyscanner ); void xlu__cfg_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE xlu__cfg_yyget_extra (yyscan_t yyscanner ); void xlu__cfg_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *xlu__cfg_yyget_in (yyscan_t yyscanner ); void xlu__cfg_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *xlu__cfg_yyget_out (yyscan_t yyscanner ); void xlu__cfg_yyset_out (FILE * out_str ,yyscan_t yyscanner ); yy_size_t xlu__cfg_yyget_leng (yyscan_t yyscanner ); char *xlu__cfg_yyget_text (yyscan_t yyscanner ); int xlu__cfg_yyget_lineno (yyscan_t yyscanner ); void xlu__cfg_yyset_lineno (int line_number ,yyscan_t yyscanner ); int xlu__cfg_yyget_column (yyscan_t yyscanner ); void xlu__cfg_yyset_column (int column_no ,yyscan_t yyscanner ); YYSTYPE * xlu__cfg_yyget_lval (yyscan_t yyscanner ); void xlu__cfg_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); YYLTYPE *xlu__cfg_yyget_lloc (yyscan_t yyscanner ); void xlu__cfg_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int xlu__cfg_yywrap (yyscan_t yyscanner ); #else extern int xlu__cfg_yywrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner ); #else static int input (yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int xlu__cfg_yylex \ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); #define YY_DECL int xlu__cfg_yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; yylloc = yylloc_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { xlu__cfg_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = xlu__cfg_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } xlu__cfg_yy_load_buffer_state(yyscanner ); } { #line 53 "libxlu_cfg_l.l" #line 808 "libxlu_cfg_l.c" while ( 1 ) /* loops until end-of-file is reached */ { yyg->yy_more_len = 0; if ( yyg->yy_more_flag ) { yyg->yy_more_len = yyg->yy_c_buf_p - yyg->yytext_ptr; yyg->yy_more_flag = 0; } yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 35 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_current_state != 34 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) { yy_size_t yyl; for ( yyl = yyg->yy_more_len; yyl < yyleng; ++yyl ) if ( yytext[yyl] == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; } do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 55 "libxlu_cfg_l.l" { yylval->string= xlu__cfgl_strdup(ctx,yytext); GOT(IDENT); } YY_BREAK case 2: YY_RULE_SETUP #line 59 "libxlu_cfg_l.l" { yylval->string= xlu__cfgl_strdup(ctx,yytext); GOT(NUMBER); } YY_BREAK case 3: YY_RULE_SETUP #line 64 "libxlu_cfg_l.l" YY_BREAK case 4: YY_RULE_SETUP #line 66 "libxlu_cfg_l.l" { GOT(','); } YY_BREAK case 5: YY_RULE_SETUP #line 67 "libxlu_cfg_l.l" { GOT('['); } YY_BREAK case 6: YY_RULE_SETUP #line 68 "libxlu_cfg_l.l" { GOT(']'); } YY_BREAK case 7: YY_RULE_SETUP #line 69 "libxlu_cfg_l.l" { GOT('='); } YY_BREAK case 8: YY_RULE_SETUP #line 70 "libxlu_cfg_l.l" { GOT(';'); } YY_BREAK case 9: /* rule 9 can match eol */ YY_RULE_SETUP #line 72 "libxlu_cfg_l.l" { yylloc->first_line= yylineno-1; return NEWLINE; } YY_BREAK case 10: YY_RULE_SETUP #line 74 "libxlu_cfg_l.l" { yylval->string= xlu__cfgl_dequote(ctx,yytext); GOT(STRING); } YY_BREAK case 11: YY_RULE_SETUP #line 78 "libxlu_cfg_l.l" { yylval->string= xlu__cfgl_dequote(ctx,yytext); GOT(STRING); } YY_BREAK case 12: YY_RULE_SETUP #line 83 "libxlu_cfg_l.l" { ctx->likely_python= 1; BEGIN(lexerr); yymore(); } YY_BREAK case 13: YY_RULE_SETUP #line 89 "libxlu_cfg_l.l" { BEGIN(lexerr); yymore(); } YY_BREAK case 14: YY_RULE_SETUP #line 94 "libxlu_cfg_l.l" { xlu__cfgl_lexicalerror(ctx,"lexical error"); BEGIN(0); } YY_BREAK case 15: /* rule 15 can match eol */ YY_RULE_SETUP #line 99 "libxlu_cfg_l.l" { xlu__cfgl_lexicalerror(ctx,"lexical error"); BEGIN(0); GOT(NEWLINE); } YY_BREAK case 16: YY_RULE_SETUP #line 104 "libxlu_cfg_l.l" YY_FATAL_ERROR( "flex scanner jammed" ); YY_BREAK #line 987 "libxlu_cfg_l.c" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(lexerr): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * xlu__cfg_yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( xlu__cfg_yywrap(yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of xlu__cfg_yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = yyg->yytext_ptr; register int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { yy_size_t new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ xlu__cfg_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; xlu__cfg_yyrestart(yyin ,yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) xlu__cfg_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { register yy_state_type yy_current_state; register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 35 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { register int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ register char *yy_cp = yyg->yy_c_buf_p; register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 35 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 34); (void)yyg; return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ xlu__cfg_yyrestart(yyin ,yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( xlu__cfg_yywrap(yyscanner ) ) return EOF; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; if ( c == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void xlu__cfg_yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ xlu__cfg_yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = xlu__cfg_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); } xlu__cfg_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); xlu__cfg_yy_load_buffer_state(yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void xlu__cfg_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * xlu__cfg_yypop_buffer_state(); * xlu__cfg_yypush_buffer_state(new_buffer); */ xlu__cfg_yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; xlu__cfg_yy_load_buffer_state(yyscanner ); /* We don't actually know whether we did this switch during * EOF (xlu__cfg_yywrap()) processing, but the only time this flag * is looked at is after xlu__cfg_yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void xlu__cfg_yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE xlu__cfg_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) xlu__cfg_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in xlu__cfg_yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) xlu__cfg_yyalloc(b->yy_buf_size + 2 ,yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in xlu__cfg_yy_create_buffer()" ); b->yy_is_our_buffer = 1; xlu__cfg_yy_init_buffer(b,file ,yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with xlu__cfg_yy_create_buffer() * @param yyscanner The scanner object. */ void xlu__cfg_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) xlu__cfg_yyfree((void *) b->yy_ch_buf ,yyscanner ); xlu__cfg_yyfree((void *) b ,yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a xlu__cfg_yyrestart() or at EOF. */ static void xlu__cfg_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; xlu__cfg_yy_flush_buffer(b ,yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then xlu__cfg_yy_init_buffer was _probably_ * called from xlu__cfg_yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void xlu__cfg_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) xlu__cfg_yy_load_buffer_state(yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void xlu__cfg_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; xlu__cfg_yyensure_buffer_stack(yyscanner); /* This block is copied from xlu__cfg_yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from xlu__cfg_yy_switch_to_buffer. */ xlu__cfg_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void xlu__cfg_yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; xlu__cfg_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { xlu__cfg_yy_load_buffer_state(yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void xlu__cfg_yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; yyg->yy_buffer_stack = (struct yy_buffer_state**)xlu__cfg_yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in xlu__cfg_yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)xlu__cfg_yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in xlu__cfg_yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE xlu__cfg_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) xlu__cfg_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in xlu__cfg_yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; xlu__cfg_yy_switch_to_buffer(b ,yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to xlu__cfg_yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * xlu__cfg_yy_scan_bytes() instead. */ YY_BUFFER_STATE xlu__cfg_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) { return xlu__cfg_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to xlu__cfg_yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE xlu__cfg_yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; yy_size_t i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) xlu__cfg_yyalloc(n ,yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in xlu__cfg_yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = xlu__cfg_yy_scan_buffer(buf,n ,yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in xlu__cfg_yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE xlu__cfg_yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int xlu__cfg_yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int xlu__cfg_yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *xlu__cfg_yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *xlu__cfg_yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ yy_size_t xlu__cfg_yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *xlu__cfg_yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void xlu__cfg_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param line_number * @param yyscanner The scanner object. */ void xlu__cfg_yyset_lineno (int line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "xlu__cfg_yyset_lineno called with no buffer" ); yylineno = line_number; } /** Set the current column. * @param line_number * @param yyscanner The scanner object. */ void xlu__cfg_yyset_column (int column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "xlu__cfg_yyset_column called with no buffer" ); yycolumn = column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * @param yyscanner The scanner object. * @see xlu__cfg_yy_switch_to_buffer */ void xlu__cfg_yyset_in (FILE * in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = in_str ; } void xlu__cfg_yyset_out (FILE * out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = out_str ; } int xlu__cfg_yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void xlu__cfg_yyset_debug (int bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * xlu__cfg_yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void xlu__cfg_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } YYLTYPE *xlu__cfg_yyget_lloc (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylloc; } void xlu__cfg_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylloc = yylloc_param; } /* User-visible API */ /* xlu__cfg_yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int xlu__cfg_yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) xlu__cfg_yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* xlu__cfg_yylex_init_extra has the same functionality as xlu__cfg_yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to xlu__cfg_yyalloc in * the yyextra field. */ int xlu__cfg_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; xlu__cfg_yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) xlu__cfg_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); xlu__cfg_yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from xlu__cfg_yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = 0; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = (char *) 0; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * xlu__cfg_yylex_init() */ return 0; } /* xlu__cfg_yylex_destroy is for both reentrant and non-reentrant scanners. */ int xlu__cfg_yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ xlu__cfg_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; xlu__cfg_yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ xlu__cfg_yyfree(yyg->yy_buffer_stack ,yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ xlu__cfg_yyfree(yyg->yy_start_stack ,yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * xlu__cfg_yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ xlu__cfg_yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *xlu__cfg_yyalloc (yy_size_t size , yyscan_t yyscanner) { return (void *) malloc( size ); } void *xlu__cfg_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void xlu__cfg_yyfree (void * ptr , yyscan_t yyscanner) { free( (char *) ptr ); /* see xlu__cfg_yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 103 "libxlu_cfg_l.l" xen-4.9.2/tools/libxl/libxl_mem.c0000664000175000017500000004064413256712137015053 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" #include "libxl_arch.h" /* * Set the maximum memory size of the domain in the hypervisor. There is no * change of the current memory size involved. The specified memory size can * even be above the configured maxmem size of the domain, but the related * Xenstore entry memory/static-max isn't modified! */ int libxl_domain_setmaxmem(libxl_ctx *ctx, uint32_t domid, uint64_t max_memkb) { GC_INIT(ctx); char *mem, *endptr; uint64_t memorykb, size; char *dompath = libxl__xs_get_dompath(gc, domid); int rc = 1; libxl__domain_userdata_lock *lock = NULL; libxl_domain_config d_config; libxl_domain_config_init(&d_config); CTX_LOCK; lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } mem = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target", dompath)); if (!mem) { LOGED(ERROR, domid, "Cannot get memory info from %s/memory/target", dompath); goto out; } memorykb = strtoull(mem, &endptr, 10); if (*endptr != '\0') { LOGED(ERROR, domid, "Invalid memory %s from %s/memory/target\n", mem, dompath); goto out; } if (max_memkb < memorykb) { LOGED(ERROR, domid, "memory_static_max must be greater than or or equal to memory_dynamic_max"); goto out; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc < 0) { LOGE(ERROR, "unable to retrieve domain configuration"); goto out; } rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size); if (rc < 0) { LOGE(ERROR, "Couldn't get arch extra constant memory size"); goto out; } rc = xc_domain_setmaxmem(ctx->xch, domid, max_memkb + size); if (rc != 0) { LOGED(ERROR, domid, "xc_domain_setmaxmem domid=%d memkb=%"PRIu64" failed ""rc=%d\n", domid, max_memkb + size, rc); goto out; } rc = 0; out: libxl_domain_config_dispose(&d_config); if (lock) libxl__unlock_domain_userdata(lock); CTX_UNLOCK; GC_FREE; return rc; } static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint64_t *target_memkb, uint64_t *max_memkb) { int rc; libxl_dominfo info; libxl_physinfo physinfo; char *target = NULL, *staticmax = NULL, *endptr = NULL; char *target_path = "/local/domain/0/memory/target"; char *max_path = "/local/domain/0/memory/static-max"; xs_transaction_t t; libxl_ctx *ctx = libxl__gc_owner(gc); libxl_dominfo_init(&info); retry_transaction: t = xs_transaction_start(ctx->xsh); target = libxl__xs_read(gc, t, target_path); staticmax = libxl__xs_read(gc, t, max_path); if (target && staticmax) { rc = 0; goto out; } if (target) { *target_memkb = strtoull(target, &endptr, 10); if (*endptr != '\0') { LOGED(ERROR, 0, "Invalid memory target %s from %s\n", target, target_path); rc = ERROR_FAIL; goto out; } } if (staticmax) { *max_memkb = strtoull(staticmax, &endptr, 10); if (*endptr != '\0') { LOGED(ERROR, 0, "Invalid memory static-max %s from %s\n", staticmax, max_path); rc = ERROR_FAIL; goto out; } } libxl_dominfo_dispose(&info); libxl_dominfo_init(&info); rc = libxl_domain_info(ctx, &info, 0); if (rc < 0) goto out; rc = libxl_get_physinfo(ctx, &physinfo); if (rc < 0) goto out; if (target == NULL) { libxl__xs_printf(gc, t, target_path, "%"PRIu64, info.current_memkb); *target_memkb = info.current_memkb; } if (staticmax == NULL) { libxl__xs_printf(gc, t, max_path, "%"PRIu64, info.max_memkb); *max_memkb = info.max_memkb; } rc = 0; out: if (!xs_transaction_end(ctx->xsh, t, 0)) { if (errno == EAGAIN) goto retry_transaction; else rc = ERROR_FAIL; } libxl_dominfo_dispose(&info); return rc; } int libxl_set_memory_target(libxl_ctx *ctx, uint32_t domid, int64_t target_memkb, int relative, int enforce) { GC_INIT(ctx); int rc, r, lrc, abort_transaction = 0; uint64_t memorykb, size; uint64_t videoram = 0; uint64_t current_target_memkb = 0, new_target_memkb = 0; uint64_t current_max_memkb = 0; char *memmax, *endptr, *videoram_s = NULL, *target = NULL; char *dompath = libxl__xs_get_dompath(gc, domid); xc_domaininfo_t info; libxl_dominfo ptr; char *uuid; xs_transaction_t t; libxl__domain_userdata_lock *lock; libxl_domain_config d_config; libxl_domain_config_init(&d_config); CTX_LOCK; lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out_no_transaction; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc < 0) { LOGE(ERROR, "unable to retrieve domain configuration"); goto out_no_transaction; } rc = libxl__arch_extra_memory(gc, &d_config.b_info, &size); if (rc < 0) { LOGE(ERROR, "Couldn't get arch extra constant memory size"); goto out_no_transaction; } retry_transaction: t = xs_transaction_start(ctx->xsh); target = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/target", dompath)); if (!target && !domid) { if (!xs_transaction_end(ctx->xsh, t, 1)) { rc = ERROR_FAIL; goto out_no_transaction; } lrc = libxl__fill_dom0_memory_info(gc, ¤t_target_memkb, ¤t_max_memkb); if (lrc < 0) { rc = ERROR_FAIL; goto out_no_transaction; } goto retry_transaction; } else if (!target) { LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target", dompath); abort_transaction = 1; rc = ERROR_FAIL; goto out; } else { current_target_memkb = strtoull(target, &endptr, 10); if (*endptr != '\0') { LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n", target, dompath); abort_transaction = 1; rc = ERROR_FAIL; goto out; } } memmax = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/static-max", dompath)); if (!memmax) { LOGED(ERROR, domid, "Cannot get memory info from %s/memory/static-max", dompath); abort_transaction = 1; rc = ERROR_FAIL; goto out; } memorykb = strtoull(memmax, &endptr, 10); if (*endptr != '\0') { LOGED(ERROR, domid, "Invalid max memory %s from %s/memory/static-max\n", memmax, dompath); abort_transaction = 1; rc = ERROR_FAIL; goto out; } videoram_s = libxl__xs_read(gc, t, GCSPRINTF("%s/memory/videoram", dompath)); videoram = videoram_s ? atoi(videoram_s) : 0; if (relative) { if (target_memkb < 0 && llabs(target_memkb) > current_target_memkb) new_target_memkb = 0; else new_target_memkb = current_target_memkb + target_memkb; } else new_target_memkb = target_memkb - videoram; if (new_target_memkb > memorykb) { LOGD(ERROR, domid, "memory_dynamic_max must be less than or equal to" " memory_static_max\n"); abort_transaction = 1; rc = ERROR_INVAL; goto out; } if (!domid && new_target_memkb < LIBXL_MIN_DOM0_MEM) { LOGD(ERROR, domid, "New target %"PRIu64" for dom0 is below the minimum threshold", new_target_memkb); abort_transaction = 1; rc = ERROR_INVAL; goto out; } if (enforce) { memorykb = new_target_memkb + videoram; r = xc_domain_setmaxmem(ctx->xch, domid, memorykb + size); if (r != 0) { LOGED(ERROR, domid, "xc_domain_setmaxmem memkb=%"PRIu64" failed ""rc=%d\n", memorykb + size, r); abort_transaction = 1; rc = ERROR_FAIL; goto out; } } r = xc_domain_set_pod_target(ctx->xch, domid, (new_target_memkb + size) / 4, NULL, NULL, NULL); if (r != 0) { LOGED(ERROR, domid, "xc_domain_set_pod_target memkb=%"PRIu64" failed rc=%d\n", (new_target_memkb + size) / 4, r); abort_transaction = 1; rc = ERROR_FAIL; goto out; } libxl__xs_printf(gc, t, GCSPRINTF("%s/memory/target", dompath), "%"PRIu64, new_target_memkb); r = xc_domain_getinfolist(ctx->xch, domid, 1, &info); if (r != 1 || info.domain != domid) { abort_transaction = 1; rc = ERROR_FAIL; goto out; } libxl_dominfo_init(&ptr); libxl__xcinfo2xlinfo(ctx, &info, &ptr); uuid = libxl__uuid2string(gc, ptr.uuid); libxl__xs_printf(gc, t, GCSPRINTF("/vm/%s/memory", uuid), "%"PRIu64, new_target_memkb / 1024); libxl_dominfo_dispose(&ptr); rc = 0; out: if (!xs_transaction_end(ctx->xsh, t, abort_transaction) && !abort_transaction) if (errno == EAGAIN) goto retry_transaction; out_no_transaction: libxl_domain_config_dispose(&d_config); if (lock) libxl__unlock_domain_userdata(lock); CTX_UNLOCK; GC_FREE; return rc; } /* out_target_memkb and out_max_memkb can be NULL */ int libxl__get_memory_target(libxl__gc *gc, uint32_t domid, uint64_t *out_target_memkb, uint64_t *out_max_memkb) { int rc; char *target = NULL, *static_max = NULL, *endptr = NULL; char *dompath = libxl__xs_get_dompath(gc, domid); uint64_t target_memkb, max_memkb; target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target", dompath)); static_max = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/static-max", dompath)); rc = ERROR_FAIL; if ((!target || !static_max) && !domid) { rc = libxl__fill_dom0_memory_info(gc, &target_memkb, &max_memkb); if (rc < 0) goto out; } else if (!target) { LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target", dompath); goto out; } else if (!static_max) { LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/static-max", dompath); goto out; } else { target_memkb = strtoull(target, &endptr, 10); if (*endptr != '\0') { LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n", target, dompath); goto out; } max_memkb = strtoull(static_max, &endptr, 10); if (*endptr != '\0') { LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/static-max\n", static_max, dompath); goto out; } } if (out_target_memkb) *out_target_memkb = target_memkb; if (out_max_memkb) *out_max_memkb = max_memkb; rc = 0; out: return rc; } static int libxl__memkb_64to32(libxl_ctx *ctx, int rc, uint64_t val64, uint32_t *ptr32) { GC_INIT(ctx); if (rc) goto out; *ptr32 = val64; if (*ptr32 == val64) goto out; LOGE(ERROR, "memory size %"PRIu64" too large for 32 bit value\n", val64); rc = ERROR_FAIL; out: GC_FREE; return rc; } int libxl_get_memory_target(libxl_ctx *ctx, uint32_t domid, uint64_t *out_target) { GC_INIT(ctx); int rc; rc = libxl__get_memory_target(gc, domid, out_target, NULL); GC_FREE; return rc; } int libxl_get_memory_target_0x040700( libxl_ctx *ctx, uint32_t domid, uint32_t *out_target) { uint64_t my_out_target; int rc; rc = libxl_get_memory_target(ctx, domid, &my_out_target); return libxl__memkb_64to32(ctx, rc, my_out_target, out_target); } int libxl_domain_need_memory(libxl_ctx *ctx, const libxl_domain_build_info *b_info_in, uint64_t *need_memkb) { GC_INIT(ctx); libxl_domain_build_info b_info[1]; int rc; libxl_domain_build_info_init(b_info); libxl_domain_build_info_copy(ctx, b_info, b_info_in); rc = libxl__domain_build_info_setdefault(gc, b_info); if (rc) goto out; *need_memkb = b_info->target_memkb; switch (b_info->type) { case LIBXL_DOMAIN_TYPE_HVM: *need_memkb += b_info->shadow_memkb + LIBXL_HVM_EXTRA_MEMORY; if (libxl_defbool_val(b_info->device_model_stubdomain)) *need_memkb += 32 * 1024; break; case LIBXL_DOMAIN_TYPE_PV: *need_memkb += b_info->shadow_memkb + LIBXL_PV_EXTRA_MEMORY; break; default: rc = ERROR_INVAL; goto out; } if (*need_memkb % (2 * 1024)) *need_memkb += (2 * 1024) - (*need_memkb % (2 * 1024)); rc = 0; out: GC_FREE; libxl_domain_build_info_dispose(b_info); return rc; } int libxl_domain_need_memory_0x040700(libxl_ctx *ctx, const libxl_domain_build_info *b_info_in, uint32_t *need_memkb) { uint64_t my_need_memkb; int rc; rc = libxl_domain_need_memory(ctx, b_info_in, &my_need_memkb); return libxl__memkb_64to32(ctx, rc, my_need_memkb, need_memkb); } int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb) { int rc = 0; libxl_physinfo info; GC_INIT(ctx); rc = libxl_get_physinfo(ctx, &info); if (rc < 0) goto out; *memkb = (info.free_pages + info.scrub_pages) * 4; out: GC_FREE; return rc; } int libxl_get_free_memory_0x040700(libxl_ctx *ctx, uint32_t *memkb) { uint64_t my_memkb; int rc; rc = libxl_get_free_memory(ctx, &my_memkb); return libxl__memkb_64to32(ctx, rc, my_memkb, memkb); } int libxl_wait_for_free_memory(libxl_ctx *ctx, uint32_t domid, uint64_t memory_kb, int wait_secs) { int rc = 0; libxl_physinfo info; GC_INIT(ctx); while (wait_secs > 0) { rc = libxl_get_physinfo(ctx, &info); if (rc < 0) goto out; if (info.free_pages * 4 >= memory_kb) { rc = 0; goto out; } wait_secs--; sleep(1); } rc = ERROR_NOMEM; out: GC_FREE; return rc; } int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs) { int rc = 0; uint64_t target_memkb = 0; uint64_t current_memkb, prev_memkb; libxl_dominfo info; rc = libxl_get_memory_target(ctx, domid, &target_memkb); if (rc < 0) return rc; libxl_dominfo_init(&info); prev_memkb = UINT64_MAX; do { sleep(2); libxl_dominfo_dispose(&info); libxl_dominfo_init(&info); rc = libxl_domain_info(ctx, &info, domid); if (rc < 0) goto out; current_memkb = info.current_memkb + info.outstanding_memkb; if (current_memkb > prev_memkb) { rc = ERROR_FAIL; goto out; } else if (current_memkb == prev_memkb) wait_secs -= 2; /* if current_memkb < prev_memkb loop for free as progress has * been made */ prev_memkb = current_memkb; } while (wait_secs > 0 && current_memkb > target_memkb); if (current_memkb <= target_memkb) rc = 0; else rc = ERROR_FAIL; out: libxl_dominfo_dispose(&info); return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_uuid.h0000664000175000017500000000477613256712137015256 0ustar smbsmb/* * Copyright (C) 2008,2010 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef __LIBXL_UUID_H__ #define __LIBXL_UUID_H__ #define LIBXL_UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" #define LIBXL_UUID_FMTLEN ((2*16)+4) /* 16 hex bytes plus 4 hypens */ #define LIBXL__UUID_BYTES(uuid) uuid[0], uuid[1], uuid[2], uuid[3], \ uuid[4], uuid[5], uuid[6], uuid[7], \ uuid[8], uuid[9], uuid[10], uuid[11], \ uuid[12], uuid[13], uuid[14], uuid[15] #define LIBXL_UUID_BYTES(arg) LIBXL__UUID_BYTES((arg).uuid) typedef struct { /* UUID as an octet stream in big-endian byte-order. */ unsigned char uuid[16]; } libxl_uuid; #if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040700 #if defined(__linux__) #include #include #elif defined(__FreeBSD__) || defined(__NetBSD__) #include #include #include #include #include #include #else #error "Please update libxl_uuid.h for your OS" #endif #endif int libxl_uuid_is_nil(const libxl_uuid *uuid); void libxl_uuid_generate(libxl_uuid *uuid); int libxl_uuid_from_string(libxl_uuid *uuid, const char *in); void libxl_uuid_copy(libxl_ctx *ctx_opt, libxl_uuid *dst, const libxl_uuid *src); #if defined(LIBXL_API_VERSION) && LIBXL_API_VERSION < 0x040500 static inline void libxl_uuid_copy_0x040400(libxl_uuid *dst, const libxl_uuid *src) { libxl_uuid_copy(NULL, dst, src); } #define libxl_uuid_copy libxl_uuid_copy_0x040400 #endif void libxl_uuid_clear(libxl_uuid *uuid); int libxl_uuid_compare(const libxl_uuid *uuid1, const libxl_uuid *uuid2); const uint8_t *libxl_uuid_bytearray_const(const libxl_uuid *uuid); uint8_t *libxl_uuid_bytearray(libxl_uuid *uuid); #endif /* __LIBXL_UUID_H__ */ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_dom.c0000664000175000017500000013537413256712137015061 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include #include "libxl_internal.h" #include "libxl_arch.h" #include #include #include #include #include "_paths.h" libxl_domain_type libxl__domain_type(libxl__gc *gc, uint32_t domid) { libxl_ctx *ctx = libxl__gc_owner(gc); xc_domaininfo_t info; int ret; ret = xc_domain_getinfolist(ctx->xch, domid, 1, &info); if (ret != 1 || info.domain != domid) { LOG(ERROR, "unable to get domain type for domid=%"PRIu32, domid); return LIBXL_DOMAIN_TYPE_INVALID; } if (info.flags & XEN_DOMINF_hvm_guest) return LIBXL_DOMAIN_TYPE_HVM; else return LIBXL_DOMAIN_TYPE_PV; } int libxl__domain_cpupool(libxl__gc *gc, uint32_t domid) { xc_domaininfo_t info; int ret; ret = xc_domain_getinfolist(CTX->xch, domid, 1, &info); if (ret != 1) { LOGE(ERROR, "getinfolist failed %d", ret); return ERROR_FAIL; } if (info.domain != domid) { LOGE(ERROR, "got info for dom%d, wanted dom%d\n", info.domain, domid); return ERROR_FAIL; } return info.cpupool; } libxl_scheduler libxl__domain_scheduler(libxl__gc *gc, uint32_t domid) { int cpupool = libxl__domain_cpupool(gc, domid); libxl_cpupoolinfo poolinfo; libxl_scheduler sched = LIBXL_SCHEDULER_UNKNOWN; int rc; if (cpupool < 0) return sched; libxl_cpupoolinfo_init(&poolinfo); rc = libxl_cpupool_info(CTX, &poolinfo, cpupool); if (rc < 0) goto out; sched = poolinfo.sched; out: libxl_cpupoolinfo_dispose(&poolinfo); return sched; } /* * Two NUMA placement candidates are compared by means of the following * heuristics: * - the number of vcpus runnable on the candidates is considered, and * candidates with fewer of them are preferred. If two candidate have * the same number of runnable vcpus, * - the amount of free memory in the candidates is considered, and the * candidate with greater amount of it is preferred. * * In fact, leaving larger memory holes, maximizes the probability of being * able to put other domains on the node. That hopefully means many domains * will benefit from local memory accesses, but also introduces the risk of * overloading large (from a memory POV) nodes. That's right the effect * that counting the vcpus able to run on the nodes tries to prevent. * * Note that this completely ignore the number of nodes each candidate span, * as the fact that fewer nodes is better is already accounted for in the * algorithm. */ static int numa_cmpf(const libxl__numa_candidate *c1, const libxl__numa_candidate *c2) { if (c1->nr_vcpus != c2->nr_vcpus) return c1->nr_vcpus - c2->nr_vcpus; return c2->free_memkb - c1->free_memkb; } /* The actual automatic NUMA placement routine */ static int numa_place_domain(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *info) { int found; libxl__numa_candidate candidate; libxl_bitmap cpupool_nodemap; libxl_cpupoolinfo cpupool_info; int i, cpupool, rc = 0; uint64_t memkb; libxl__numa_candidate_init(&candidate); libxl_bitmap_init(&cpupool_nodemap); libxl_cpupoolinfo_init(&cpupool_info); /* * Extract the cpumap from the cpupool the domain belong to. In fact, * it only makes sense to consider the cpus/nodes that are in there * for placement. */ rc = cpupool = libxl__domain_cpupool(gc, domid); if (rc < 0) goto out; rc = libxl_cpupool_info(CTX, &cpupool_info, cpupool); if (rc) goto out; rc = libxl_domain_need_memory(CTX, info, &memkb); if (rc) goto out; if (libxl_node_bitmap_alloc(CTX, &cpupool_nodemap, 0)) { rc = ERROR_FAIL; goto out; } /* Find the best candidate with enough free memory and at least * as much pcpus as the domain has vcpus. */ rc = libxl__get_numa_candidate(gc, memkb, info->max_vcpus, 0, 0, &cpupool_info.cpumap, numa_cmpf, &candidate, &found); if (rc) goto out; /* Not even a suitable placement candidate! Let's just don't touch the * domain's info->cpumap. It will have affinity with all nodes/cpus. */ if (found == 0) goto out; /* Map the candidate's node map to the domain's info->nodemap */ libxl__numa_candidate_get_nodemap(gc, &candidate, &info->nodemap); /* Avoid trying to set the affinity to nodes that might be in the * candidate's nodemap but out of our cpupool. */ rc = libxl_cpumap_to_nodemap(CTX, &cpupool_info.cpumap, &cpupool_nodemap); if (rc) goto out; libxl_for_each_set_bit(i, info->nodemap) { if (!libxl_bitmap_test(&cpupool_nodemap, i)) libxl_bitmap_reset(&info->nodemap, i); } LOG(DETAIL, "NUMA placement candidate with %d nodes, %d cpus and " "%"PRIu64" KB free selected", candidate.nr_nodes, candidate.nr_cpus, candidate.free_memkb / 1024); out: libxl__numa_candidate_dispose(&candidate); libxl_bitmap_dispose(&cpupool_nodemap); libxl_cpupoolinfo_dispose(&cpupool_info); return rc; } static unsigned long timer_mode(const libxl_domain_build_info *info) { const libxl_timer_mode mode = info->u.hvm.timer_mode; assert(mode >= LIBXL_TIMER_MODE_DELAY_FOR_MISSED_TICKS && mode <= LIBXL_TIMER_MODE_ONE_MISSED_TICK_PENDING); return ((unsigned long)mode); } #if defined(__i386__) || defined(__x86_64__) static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *const info) { libxl_bitmap enlightenments; libxl_viridian_enlightenment v; uint64_t mask = 0; libxl_bitmap_init(&enlightenments); libxl_bitmap_alloc(CTX, &enlightenments, LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH); if (libxl_defbool_val(info->u.hvm.viridian)) { /* Enable defaults */ libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE); libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ); libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT); libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST); libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL); } libxl_for_each_set_bit(v, info->u.hvm.viridian_enable) { if (libxl_bitmap_test(&info->u.hvm.viridian_disable, v)) { LOG(ERROR, "%s group both enabled and disabled", libxl_viridian_enlightenment_to_string(v)); goto err; } if (libxl_viridian_enlightenment_to_string(v)) /* check validity */ libxl_bitmap_set(&enlightenments, v); } libxl_for_each_set_bit(v, info->u.hvm.viridian_disable) if (libxl_viridian_enlightenment_to_string(v)) /* check validity */ libxl_bitmap_reset(&enlightenments, v); /* The base set is a pre-requisite for all others */ if (!libxl_bitmap_is_empty(&enlightenments) && !libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) { LOG(ERROR, "base group not enabled"); goto err; } libxl_for_each_set_bit(v, enlightenments) LOG(DETAIL, "%s group enabled", libxl_viridian_enlightenment_to_string(v)); if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_BASE)) { mask |= HVMPV_base_freq; if (!libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_FREQ)) mask |= HVMPV_no_freq; } if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT)) mask |= HVMPV_time_ref_count; if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_REFERENCE_TSC)) mask |= HVMPV_reference_tsc; if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_HCALL_REMOTE_TLB_FLUSH)) mask |= HVMPV_hcall_remote_tlb_flush; if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST)) mask |= HVMPV_apic_assist; if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL)) mask |= HVMPV_crash_ctl; if (mask != 0 && xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_VIRIDIAN, mask) != 0) { LOGE(ERROR, "Couldn't set viridian feature mask (0x%"PRIx64")", mask); goto err; } libxl_bitmap_dispose(&enlightenments); return 0; err: libxl_bitmap_dispose(&enlightenments); return ERROR_FAIL; } #endif static void hvm_set_conf_params(xc_interface *handle, uint32_t domid, libxl_domain_build_info *const info) { xc_hvm_param_set(handle, domid, HVM_PARAM_PAE_ENABLED, libxl_defbool_val(info->u.hvm.pae)); #if defined(__i386__) || defined(__x86_64__) xc_hvm_param_set(handle, domid, HVM_PARAM_HPET_ENABLED, libxl_defbool_val(info->u.hvm.hpet)); #endif xc_hvm_param_set(handle, domid, HVM_PARAM_TIMER_MODE, timer_mode(info)); xc_hvm_param_set(handle, domid, HVM_PARAM_VPT_ALIGN, libxl_defbool_val(info->u.hvm.vpt_align)); xc_hvm_param_set(handle, domid, HVM_PARAM_NESTEDHVM, libxl_defbool_val(info->u.hvm.nested_hvm)); } int libxl__build_pre(libxl__gc *gc, uint32_t domid, libxl_domain_config *d_config, libxl__domain_build_state *state) { libxl_domain_build_info *const info = &d_config->b_info; libxl_ctx *ctx = libxl__gc_owner(gc); char *xs_domid, *con_domid; int rc; uint64_t size; if (xc_domain_max_vcpus(ctx->xch, domid, info->max_vcpus) != 0) { LOG(ERROR, "Couldn't set max vcpu count"); return ERROR_FAIL; } /* * Check if the domain has any CPU or node affinity already. If not, try * to build up the latter via automatic NUMA placement. In fact, in case * numa_place_domain() manage to find a placement, in info->nodemap is * updated accordingly; if it does not manage, info->nodemap is just left * alone. It is then the the subsequent call to * libxl_domain_set_nodeaffinity() that enacts the actual placement. * * As far as scheduling is concerned, we achieve NUMA-aware scheduling * by having the results of placement affect the soft affinity of all * the vcpus of the domain. Of course, we want that iff placement is * enabled and actually happens, so we only change info->cpumap_soft to * reflect the placement result if that is the case */ if (libxl_defbool_val(info->numa_placement)) { if (info->cpumap.size || info->num_vcpu_hard_affinity || info->num_vcpu_soft_affinity) LOG(WARN, "Can't run NUMA placement, as an (hard or soft) " "affinity has been specified explicitly"); else if (info->nodemap.size) LOG(WARN, "Can't run NUMA placement, as the domain has " "NUMA node affinity set already"); else { libxl_bitmap cpumap_soft; rc = libxl_node_bitmap_alloc(ctx, &info->nodemap, 0); if (rc) return rc; libxl_bitmap_set_any(&info->nodemap); rc = libxl_cpu_bitmap_alloc(ctx, &cpumap_soft, 0); if (rc) return rc; rc = numa_place_domain(gc, domid, info); if (rc) { libxl_bitmap_dispose(&cpumap_soft); return rc; } /* * All we need to do now is converting the result of automatic * placement from nodemap to cpumap, and then use such cpumap * as the soft affinity for all the vcpus of the domain. * * When calling libxl_set_vcpuaffinity_all(), it is ok to use * NULL as hard affinity, as we know we don't have one, or we * won't be here. */ libxl_nodemap_to_cpumap(ctx, &info->nodemap, &cpumap_soft); libxl_set_vcpuaffinity_all(ctx, domid, info->max_vcpus, NULL, &cpumap_soft); libxl_bitmap_dispose(&cpumap_soft); /* * Placement has run, so avoid for it to be re-run, if this * same config we are using and building here is ever re-used. * This means that people re-using configs will get the same * results, consistently, across every re-use, which is what * we expect most people to want. */ libxl_defbool_set(&info->numa_placement, false); } } if (info->nodemap.size) libxl_domain_set_nodeaffinity(ctx, domid, &info->nodemap); if (info->num_vcpu_hard_affinity || info->num_vcpu_soft_affinity) { libxl_bitmap *hard_affinity, *soft_affinity; int i, n_vcpus; n_vcpus = info->num_vcpu_hard_affinity > info->num_vcpu_soft_affinity ? info->num_vcpu_hard_affinity : info->num_vcpu_soft_affinity; for (i = 0; i < n_vcpus; i++) { /* * Prepare hard and soft affinity pointers in a way that allows * us to issue only one call to libxl_set_vcpuaffinity(), setting, * for each vcpu, both hard and soft affinity "atomically". */ hard_affinity = NULL; if (info->num_vcpu_hard_affinity && i < info->num_vcpu_hard_affinity) hard_affinity = &info->vcpu_hard_affinity[i]; soft_affinity = NULL; if (info->num_vcpu_soft_affinity && i < info->num_vcpu_soft_affinity) soft_affinity = &info->vcpu_soft_affinity[i]; if (libxl_set_vcpuaffinity(ctx, domid, i, hard_affinity, soft_affinity)) { LOG(ERROR, "setting affinity failed on vcpu `%d'", i); return ERROR_FAIL; } } } rc = libxl__arch_extra_memory(gc, info, &size); if (rc < 0) { LOGE(ERROR, "Couldn't get arch extra constant memory size"); return ERROR_FAIL; } if (xc_domain_setmaxmem(ctx->xch, domid, info->target_memkb + size) < 0) { LOGE(ERROR, "Couldn't set max memory"); return ERROR_FAIL; } xs_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenstored/domid", NULL); state->store_domid = xs_domid ? atoi(xs_domid) : 0; free(xs_domid); con_domid = xs_read(ctx->xsh, XBT_NULL, "/tool/xenconsoled/domid", NULL); state->console_domid = con_domid ? atoi(con_domid) : 0; free(con_domid); state->store_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->store_domid); state->console_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->console_domid); if (info->type == LIBXL_DOMAIN_TYPE_HVM) { hvm_set_conf_params(ctx->xch, domid, info); #if defined(__i386__) || defined(__x86_64__) rc = hvm_set_viridian_features(gc, domid, info); if (rc) return rc; #endif } /* Alternate p2m support on x86 is available only for HVM guests. */ if (info->type == LIBXL_DOMAIN_TYPE_HVM) { /* The config parameter "altp2m" replaces the parameter "altp2mhvm". For * legacy reasons, both parameters are accepted on x86 HVM guests. * * If the legacy field info->u.hvm.altp2m is set, activate altp2m. * Otherwise set altp2m based on the field info->altp2m. */ if (info->altp2m == LIBXL_ALTP2M_MODE_DISABLED && libxl_defbool_val(info->u.hvm.altp2m)) xc_hvm_param_set(ctx->xch, domid, HVM_PARAM_ALTP2M, libxl_defbool_val(info->u.hvm.altp2m)); else xc_hvm_param_set(ctx->xch, domid, HVM_PARAM_ALTP2M, info->altp2m); } rc = libxl__arch_domain_create(gc, d_config, domid); return rc; } static int set_vnuma_affinity(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *info) { libxl_bitmap cpumap; libxl_vnode_info *v; unsigned int i, j; int rc = 0; libxl_bitmap_init(&cpumap); rc = libxl_cpu_bitmap_alloc(CTX, &cpumap, 0); if (rc) { LOG(ERROR, "Can't allocate nodemap"); goto out; } /* * For each vcpu in each vnode, set its soft affinity to * the pcpus belonging to the pnode the vnode is on */ for (i = 0; i < info->num_vnuma_nodes; i++) { v = &info->vnuma_nodes[i]; rc = libxl_node_to_cpumap(CTX, v->pnode, &cpumap); if (rc) { LOG(ERROR, "Can't get cpumap for vnode %d", i); goto out; } libxl_for_each_set_bit(j, v->vcpus) { rc = libxl_set_vcpuaffinity(CTX, domid, j, NULL, &cpumap); if (rc) { LOG(ERROR, "Can't set cpu affinity for %d", j); goto out; } } } out: libxl_bitmap_dispose(&cpumap); return rc; } int libxl__build_post(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *info, libxl__domain_build_state *state, char **vms_ents, char **local_ents) { libxl_ctx *ctx = libxl__gc_owner(gc); char *dom_path, *vm_path; xs_transaction_t t; char **ents; int i, rc; if (info->num_vnuma_nodes && !info->num_vcpu_soft_affinity) { rc = set_vnuma_affinity(gc, domid, info); if (rc) return rc; } rc = libxl_domain_sched_params_set(CTX, domid, &info->sched_params); if (rc) return rc; rc = xc_domain_set_max_evtchn(ctx->xch, domid, info->event_channels); if (rc) { LOG(ERROR, "Failed to set event channel limit to %d (%d)", info->event_channels, rc); return ERROR_FAIL; } libxl_cpuid_apply_policy(ctx, domid); if (info->cpuid != NULL) libxl_cpuid_set(ctx, domid, info->cpuid); if (info->type == LIBXL_DOMAIN_TYPE_HVM && !libxl_ms_vm_genid_is_zero(&info->u.hvm.ms_vm_genid)) { rc = libxl__ms_vm_genid_set(gc, domid, &info->u.hvm.ms_vm_genid); if (rc) { LOG(ERROR, "Failed to set VM Generation ID"); return rc; } } ents = libxl__calloc(gc, 12 + (info->max_vcpus * 2) + 2, sizeof(char *)); ents[0] = "memory/static-max"; ents[1] = GCSPRINTF("%"PRId64, info->max_memkb); ents[2] = "memory/target"; ents[3] = GCSPRINTF("%"PRId64, info->target_memkb - libxl__get_targetmem_fudge(gc, info)); ents[4] = "memory/videoram"; ents[5] = GCSPRINTF("%"PRId64, info->video_memkb); ents[6] = "domid"; ents[7] = GCSPRINTF("%d", domid); ents[8] = "store/port"; ents[9] = GCSPRINTF("%"PRIu32, state->store_port); ents[10] = "store/ring-ref"; ents[11] = GCSPRINTF("%lu", state->store_mfn); for (i = 0; i < info->max_vcpus; i++) { ents[12+(i*2)] = GCSPRINTF("cpu/%d/availability", i); ents[12+(i*2)+1] = libxl_bitmap_test(&info->avail_vcpus, i) ? "online" : "offline"; } dom_path = libxl__xs_get_dompath(gc, domid); if (!dom_path) { return ERROR_FAIL; } vm_path = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/vm", dom_path), NULL); retry_transaction: t = xs_transaction_start(ctx->xsh); libxl__xs_writev(gc, t, dom_path, ents); libxl__xs_writev(gc, t, dom_path, local_ents); libxl__xs_writev(gc, t, vm_path, vms_ents); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; xs_introduce_domain(ctx->xsh, domid, state->store_mfn, state->store_port); free(vm_path); return 0; } static int set_vnuma_info(libxl__gc *gc, uint32_t domid, const libxl_domain_build_info *info, const libxl__domain_build_state *state) { int rc = 0; unsigned int i, nr_vdistance; unsigned int *vcpu_to_vnode, *vnode_to_pnode, *vdistance = NULL; vcpu_to_vnode = libxl__calloc(gc, info->max_vcpus, sizeof(unsigned int)); vnode_to_pnode = libxl__calloc(gc, info->num_vnuma_nodes, sizeof(unsigned int)); nr_vdistance = info->num_vnuma_nodes * info->num_vnuma_nodes; vdistance = libxl__calloc(gc, nr_vdistance, sizeof(unsigned int)); for (i = 0; i < info->num_vnuma_nodes; i++) { libxl_vnode_info *v = &info->vnuma_nodes[i]; int j; /* vnode to pnode mapping */ vnode_to_pnode[i] = v->pnode; /* vcpu to vnode mapping */ libxl_for_each_set_bit(j, v->vcpus) vcpu_to_vnode[j] = i; /* node distances */ assert(info->num_vnuma_nodes == v->num_distances); memcpy(vdistance + (i * info->num_vnuma_nodes), v->distances, v->num_distances * sizeof(unsigned int)); } if (xc_domain_setvnuma(CTX->xch, domid, info->num_vnuma_nodes, state->num_vmemranges, info->max_vcpus, state->vmemranges, vdistance, vcpu_to_vnode, vnode_to_pnode) < 0) { LOGE(ERROR, "xc_domain_setvnuma failed"); rc = ERROR_FAIL; } return rc; } static int libxl__build_dom(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *info, libxl__domain_build_state *state, struct xc_dom_image *dom) { uint64_t mem_kb; int ret; if ( (ret = xc_dom_boot_xen_init(dom, CTX->xch, domid)) != 0 ) { LOGE(ERROR, "xc_dom_boot_xen_init failed"); goto out; } #ifdef GUEST_RAM_BASE if ( (ret = xc_dom_rambase_init(dom, GUEST_RAM_BASE)) != 0 ) { LOGE(ERROR, "xc_dom_rambase failed"); goto out; } #endif if ( (ret = xc_dom_parse_image(dom)) != 0 ) { LOGE(ERROR, "xc_dom_parse_image failed"); goto out; } if ( (ret = libxl__arch_domain_init_hw_description(gc, info, state, dom)) != 0 ) { LOGE(ERROR, "libxl__arch_domain_init_hw_description failed"); goto out; } mem_kb = dom->container_type == XC_DOM_HVM_CONTAINER ? (info->max_memkb - info->video_memkb) : info->target_memkb; if ( (ret = xc_dom_mem_init(dom, mem_kb / 1024)) != 0 ) { LOGE(ERROR, "xc_dom_mem_init failed"); goto out; } if ( (ret = xc_dom_boot_mem_init(dom)) != 0 ) { LOGE(ERROR, "xc_dom_boot_mem_init failed"); goto out; } if ( (ret = libxl__arch_domain_finalise_hw_description(gc, info, dom)) != 0 ) { LOGE(ERROR, "libxl__arch_domain_finalise_hw_description failed"); goto out; } if ( (ret = xc_dom_build_image(dom)) != 0 ) { LOGE(ERROR, "xc_dom_build_image failed"); goto out; } if ( (ret = xc_dom_boot_image(dom)) != 0 ) { LOGE(ERROR, "xc_dom_boot_image failed"); goto out; } if ( (ret = xc_dom_gnttab_init(dom)) != 0 ) { LOGE(ERROR, "xc_dom_gnttab_init failed"); goto out; } out: return ret != 0 ? ERROR_FAIL : 0; } int libxl__build_pv(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *info, libxl__domain_build_state *state) { libxl_ctx *ctx = libxl__gc_owner(gc); struct xc_dom_image *dom; int ret; int flags = 0; xc_dom_loginit(ctx->xch); dom = xc_dom_allocate(ctx->xch, state->pv_cmdline, info->u.pv.features); if (!dom) { LOGE(ERROR, "xc_dom_allocate failed"); return ERROR_FAIL; } dom->container_type = XC_DOM_PV_CONTAINER; LOG(DEBUG, "pv kernel mapped %d path %s", state->pv_kernel.mapped, state->pv_kernel.path); if (state->pv_kernel.mapped) { ret = xc_dom_kernel_mem(dom, state->pv_kernel.data, state->pv_kernel.size); if ( ret != 0) { LOGE(ERROR, "xc_dom_kernel_mem failed"); goto out; } } else { ret = xc_dom_kernel_file(dom, state->pv_kernel.path); if ( ret != 0) { LOGE(ERROR, "xc_dom_kernel_file failed"); goto out; } } if ( state->pv_ramdisk.path && strlen(state->pv_ramdisk.path) ) { if (state->pv_ramdisk.mapped) { if ( (ret = xc_dom_ramdisk_mem(dom, state->pv_ramdisk.data, state->pv_ramdisk.size)) != 0 ) { LOGE(ERROR, "xc_dom_ramdisk_mem failed"); goto out; } } else { if ( (ret = xc_dom_ramdisk_file(dom, state->pv_ramdisk.path)) != 0 ) { LOGE(ERROR, "xc_dom_ramdisk_file failed"); goto out; } } } dom->flags = flags; dom->console_evtchn = state->console_port; dom->console_domid = state->console_domid; dom->xenstore_evtchn = state->store_port; dom->xenstore_domid = state->store_domid; dom->claim_enabled = libxl_defbool_val(info->claim_mode); if (info->num_vnuma_nodes != 0) { unsigned int i; ret = libxl__vnuma_build_vmemrange_pv(gc, domid, info, state); if (ret) { LOGE(ERROR, "cannot build vmemranges"); goto out; } ret = libxl__vnuma_config_check(gc, info, state); if (ret) goto out; ret = set_vnuma_info(gc, domid, info, state); if (ret) goto out; dom->nr_vmemranges = state->num_vmemranges; dom->vmemranges = xc_dom_malloc(dom, sizeof(*dom->vmemranges) * dom->nr_vmemranges); for (i = 0; i < dom->nr_vmemranges; i++) { dom->vmemranges[i].start = state->vmemranges[i].start; dom->vmemranges[i].end = state->vmemranges[i].end; dom->vmemranges[i].flags = state->vmemranges[i].flags; dom->vmemranges[i].nid = state->vmemranges[i].nid; } dom->nr_vnodes = info->num_vnuma_nodes; dom->vnode_to_pnode = xc_dom_malloc(dom, sizeof(*dom->vnode_to_pnode) * dom->nr_vnodes); for (i = 0; i < info->num_vnuma_nodes; i++) dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode; } ret = libxl__build_dom(gc, domid, info, state, dom); if (ret != 0) goto out; if (xc_dom_translated(dom)) { state->console_mfn = dom->console_pfn; state->store_mfn = dom->xenstore_pfn; } else { state->console_mfn = xc_dom_p2m(dom, dom->console_pfn); state->store_mfn = xc_dom_p2m(dom, dom->xenstore_pfn); } ret = 0; out: xc_dom_release(dom); return ret == 0 ? 0 : ERROR_FAIL; } static int hvm_build_set_params(xc_interface *handle, uint32_t domid, libxl_domain_build_info *info, int store_evtchn, unsigned long *store_mfn, int console_evtchn, unsigned long *console_mfn, domid_t store_domid, domid_t console_domid) { struct hvm_info_table *va_hvm; uint8_t *va_map, sum; uint64_t str_mfn, cons_mfn; int i; if (info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_NONE) { va_map = xc_map_foreign_range(handle, domid, XC_PAGE_SIZE, PROT_READ | PROT_WRITE, HVM_INFO_PFN); if (va_map == NULL) return ERROR_FAIL; va_hvm = (struct hvm_info_table *)(va_map + HVM_INFO_OFFSET); va_hvm->apic_mode = libxl_defbool_val(info->u.hvm.apic); va_hvm->nr_vcpus = info->max_vcpus; memset(va_hvm->vcpu_online, 0, sizeof(va_hvm->vcpu_online)); memcpy(va_hvm->vcpu_online, info->avail_vcpus.map, info->avail_vcpus.size); for (i = 0, sum = 0; i < va_hvm->length; i++) sum += ((uint8_t *) va_hvm)[i]; va_hvm->checksum -= sum; munmap(va_map, XC_PAGE_SIZE); } xc_hvm_param_get(handle, domid, HVM_PARAM_STORE_PFN, &str_mfn); xc_hvm_param_get(handle, domid, HVM_PARAM_CONSOLE_PFN, &cons_mfn); xc_hvm_param_set(handle, domid, HVM_PARAM_STORE_EVTCHN, store_evtchn); xc_hvm_param_set(handle, domid, HVM_PARAM_CONSOLE_EVTCHN, console_evtchn); *store_mfn = str_mfn; *console_mfn = cons_mfn; xc_dom_gnttab_hvm_seed(handle, domid, *console_mfn, *store_mfn, console_domid, store_domid); return 0; } static int hvm_build_set_xs_values(libxl__gc *gc, uint32_t domid, struct xc_dom_image *dom, const libxl_domain_build_info *info) { char *path = NULL; int ret = 0; if (dom->smbios_module.guest_addr_out) { path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_ADDRESS, domid); ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64, dom->smbios_module.guest_addr_out); if (ret) goto err; path = GCSPRINTF("/local/domain/%d/"HVM_XS_SMBIOS_PT_LENGTH, domid); ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x", dom->smbios_module.length); if (ret) goto err; } /* Only one module can be passed. PVHv2 guests do not support this. */ if (dom->acpi_modules[0].guest_addr_out && info->device_model_version !=LIBXL_DEVICE_MODEL_VERSION_NONE) { path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_ADDRESS, domid); ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64, dom->acpi_modules[0].guest_addr_out); if (ret) goto err; path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_LENGTH, domid); ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%x", dom->acpi_modules[0].length); if (ret) goto err; } return 0; err: LOG(ERROR, "failed to write firmware xenstore value, err: %d", ret); return ret; } static int libxl__load_hvm_firmware_module(libxl__gc *gc, const char *filename, const char *what, struct xc_hvm_firmware_module *m) { int datalen = 0; void *data = NULL; int r, rc; LOG(DEBUG, "Loading %s: %s", what, filename); r = libxl_read_file_contents(CTX, filename, &data, &datalen); if (r) { /* * Print a message only on ENOENT, other errors are logged by the * function libxl_read_file_contents(). */ if (r == ENOENT) LOGEV(ERROR, r, "failed to read %s file", what); rc = ERROR_FAIL; goto out; } libxl__ptr_add(gc, data); if (datalen) { /* Only accept non-empty files */ m->data = data; m->length = datalen; } else { LOG(ERROR, "file %s for %s is empty", filename, what); rc = ERROR_INVAL; goto out; } rc = 0; out: return rc; } static int libxl__domain_firmware(libxl__gc *gc, libxl_domain_build_info *info, struct xc_dom_image *dom) { libxl_ctx *ctx = libxl__gc_owner(gc); const char *firmware = NULL; int e, rc; int datalen = 0; void *data; const char *bios_filename = NULL; if (info->u.hvm.firmware) firmware = info->u.hvm.firmware; else { switch (info->device_model_version) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: firmware = "hvmloader"; break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: firmware = "hvmloader"; break; case LIBXL_DEVICE_MODEL_VERSION_NONE: if (info->kernel == NULL) { LOG(ERROR, "no device model requested without a kernel"); rc = ERROR_FAIL; goto out; } break; default: LOG(ERROR, "invalid device model version %d", info->device_model_version); rc = ERROR_FAIL; goto out; } } if (info->kernel != NULL && info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) { /* Try to load a kernel instead of the firmware. */ rc = xc_dom_kernel_file(dom, info->kernel); if (rc == 0 && info->ramdisk != NULL) rc = xc_dom_ramdisk_file(dom, info->ramdisk); } else { rc = xc_dom_kernel_file(dom, libxl__abs_path(gc, firmware, libxl__xenfirmwaredir_path())); } if (rc != 0) { LOGE(ERROR, "xc_dom_{kernel_file/ramdisk_file} failed"); goto out; } if (info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { if (info->u.hvm.system_firmware) { bios_filename = info->u.hvm.system_firmware; } else { switch (info->u.hvm.bios) { case LIBXL_BIOS_TYPE_SEABIOS: bios_filename = libxl__seabios_path(); break; case LIBXL_BIOS_TYPE_OVMF: bios_filename = libxl__ovmf_path(); break; case LIBXL_BIOS_TYPE_ROMBIOS: default: abort(); } } } if (bios_filename) { rc = libxl__load_hvm_firmware_module(gc, bios_filename, "BIOS", &dom->system_firmware_module); if (rc) goto out; } if (info->u.hvm.smbios_firmware) { data = NULL; e = libxl_read_file_contents(ctx, info->u.hvm.smbios_firmware, &data, &datalen); if (e) { LOGEV(ERROR, e, "failed to read SMBIOS firmware file %s", info->u.hvm.smbios_firmware); rc = ERROR_FAIL; goto out; } libxl__ptr_add(gc, data); if (datalen) { /* Only accept non-empty files */ dom->smbios_module.data = data; dom->smbios_module.length = (uint32_t)datalen; } } if (info->u.hvm.acpi_firmware) { if (info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) { LOGE(ERROR, "PVH guests do not allow loading ACPI modules"); rc = ERROR_FAIL; goto out; } data = NULL; e = libxl_read_file_contents(ctx, info->u.hvm.acpi_firmware, &data, &datalen); if (e) { LOGEV(ERROR, e, "failed to read ACPI firmware file %s", info->u.hvm.acpi_firmware); rc = ERROR_FAIL; goto out; } libxl__ptr_add(gc, data); if (datalen) { /* Only accept a non-empty file */ dom->acpi_modules[0].data = data; dom->acpi_modules[0].length = (uint32_t)datalen; } } return 0; out: assert(rc != 0); return rc; } int libxl__build_hvm(libxl__gc *gc, uint32_t domid, libxl_domain_config *d_config, libxl__domain_build_state *state) { libxl_ctx *ctx = libxl__gc_owner(gc); int rc; uint64_t mmio_start, lowmem_end, highmem_end, mem_size; libxl_domain_build_info *const info = &d_config->b_info; struct xc_dom_image *dom = NULL; bool device_model = info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_NONE ? true : false; xc_dom_loginit(ctx->xch); dom = xc_dom_allocate(ctx->xch, info->cmdline, NULL); if (!dom) { LOGE(ERROR, "xc_dom_allocate failed"); rc = ERROR_NOMEM; goto out; } dom->container_type = XC_DOM_HVM_CONTAINER; /* The params from the configuration file are in Mb, which are then * multiplied by 1 Kb. This was then divided off when calling * the old xc_hvm_build_target_mem() which then turned them to bytes. * Do all this in one step here... */ mem_size = (uint64_t)(info->max_memkb - info->video_memkb) << 10; dom->target_pages = (uint64_t)(info->target_memkb - info->video_memkb) >> 2; dom->claim_enabled = libxl_defbool_val(info->claim_mode); if (info->u.hvm.mmio_hole_memkb) { uint64_t max_ram_below_4g = (1ULL << 32) - (info->u.hvm.mmio_hole_memkb << 10); if (max_ram_below_4g < HVM_BELOW_4G_MMIO_START) dom->mmio_size = info->u.hvm.mmio_hole_memkb << 10; } rc = libxl__domain_firmware(gc, info, dom); if (rc != 0) { LOG(ERROR, "initializing domain firmware failed"); goto out; } if (dom->target_pages == 0) dom->target_pages = mem_size >> XC_PAGE_SHIFT; if (dom->mmio_size == 0 && device_model) dom->mmio_size = HVM_BELOW_4G_MMIO_LENGTH; else if (dom->mmio_size == 0 && !device_model) { #if defined(__i386__) || defined(__x86_64__) if (libxl_defbool_val(info->u.hvm.apic)) { /* Make sure LAPIC_BASE_ADDRESS is below special pages */ assert(((((X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES) << XC_PAGE_SHIFT) - LAPIC_BASE_ADDRESS)) >= XC_PAGE_SIZE); dom->mmio_size = GB(4) - LAPIC_BASE_ADDRESS; } else dom->mmio_size = GB(4) - ((X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES) << XC_PAGE_SHIFT); #else assert(1); #endif } lowmem_end = mem_size; highmem_end = 0; mmio_start = (1ull << 32) - dom->mmio_size; if (lowmem_end > mmio_start) { highmem_end = (1ull << 32) + (lowmem_end - mmio_start); lowmem_end = mmio_start; } dom->lowmem_end = lowmem_end; dom->highmem_end = highmem_end; dom->mmio_start = mmio_start; dom->vga_hole_size = device_model ? LIBXL_VGA_HOLE_SIZE : 0; dom->device_model = device_model; rc = libxl__domain_device_construct_rdm(gc, d_config, info->u.hvm.rdm_mem_boundary_memkb*1024, dom); if (rc) { LOG(ERROR, "checking reserved device memory failed"); goto out; } if (info->num_vnuma_nodes != 0) { int i; rc = libxl__vnuma_build_vmemrange_hvm(gc, domid, info, state, dom); if (rc != 0) { LOG(ERROR, "hvm build vmemranges failed"); goto out; } rc = libxl__vnuma_config_check(gc, info, state); if (rc != 0) goto out; rc = set_vnuma_info(gc, domid, info, state); if (rc != 0) goto out; dom->nr_vmemranges = state->num_vmemranges; dom->vmemranges = libxl__malloc(gc, sizeof(*dom->vmemranges) * dom->nr_vmemranges); for (i = 0; i < dom->nr_vmemranges; i++) { dom->vmemranges[i].start = state->vmemranges[i].start; dom->vmemranges[i].end = state->vmemranges[i].end; dom->vmemranges[i].flags = state->vmemranges[i].flags; dom->vmemranges[i].nid = state->vmemranges[i].nid; } dom->nr_vnodes = info->num_vnuma_nodes; dom->vnode_to_pnode = libxl__malloc(gc, sizeof(*dom->vnode_to_pnode) * dom->nr_vnodes); for (i = 0; i < dom->nr_vnodes; i++) dom->vnode_to_pnode[i] = info->vnuma_nodes[i].pnode; } rc = libxl__build_dom(gc, domid, info, state, dom); if (rc != 0) goto out; rc = libxl__arch_domain_construct_memmap(gc, d_config, domid, dom); if (rc != 0) { LOG(ERROR, "setting domain memory map failed"); goto out; } rc = hvm_build_set_params(ctx->xch, domid, info, state->store_port, &state->store_mfn, state->console_port, &state->console_mfn, state->store_domid, state->console_domid); if (rc != 0) { LOG(ERROR, "hvm build set params failed"); goto out; } rc = hvm_build_set_xs_values(gc, domid, dom, info); if (rc != 0) { LOG(ERROR, "hvm build set xenstore values failed"); goto out; } xc_dom_release(dom); return 0; out: assert(rc != 0); if (dom != NULL) xc_dom_release(dom); return rc; } int libxl__qemu_traditional_cmd(libxl__gc *gc, uint32_t domid, const char *cmd) { char *path = NULL; uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid); path = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, "/command"); return libxl__xs_printf(gc, XBT_NULL, path, "%s", cmd); } /*==================== Miscellaneous ====================*/ char *libxl__uuid2string(libxl__gc *gc, const libxl_uuid uuid) { return GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(uuid)); } const char *libxl__userdata_path(libxl__gc *gc, uint32_t domid, const char *userdata_userid, const char *wh) { libxl_ctx *ctx = libxl__gc_owner(gc); char *uuid_string, *path; libxl_dominfo info; int rc; libxl_dominfo_init(&info); rc = libxl_domain_info(ctx, &info, domid); if (rc) { LOGE(ERROR, "unable to find domain info for domain %"PRIu32, domid); path = NULL; goto out; } uuid_string = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid)); path = GCSPRINTF(XEN_LIB_DIR "/userdata-%s.%u.%s.%s", wh, domid, uuid_string, userdata_userid); out: libxl_dominfo_dispose(&info); return path; } static int userdata_delete(libxl__gc *gc, const char *path) { int r; r = unlink(path); if (r) { LOGE(ERROR, "remove failed for %s", path); return errno; } return 0; } void libxl__userdata_destroyall(libxl__gc *gc, uint32_t domid) { const char *pattern; glob_t gl; int r, i; pattern = libxl__userdata_path(gc, domid, "*", "?"); if (!pattern) goto out; gl.gl_pathc = 0; gl.gl_pathv = 0; gl.gl_offs = 0; r = glob(pattern, GLOB_ERR|GLOB_NOSORT|GLOB_MARK, 0, &gl); if (r == GLOB_NOMATCH) goto out; if (r) LOGE(ERROR, "glob failed for %s", pattern); /* Note: don't delete domain-userdata-lock, it will be handled by * unlock function. */ for (i=0; i= 0) { e = errno; close(fd); errno = e; } if (rc) LOGE(ERROR, "cannot write/rename %s for %s", newfilename, filename); out: return rc; } int libxl_userdata_store(libxl_ctx *ctx, uint32_t domid, const char *userdata_userid, const uint8_t *data, int datalen) { GC_INIT(ctx); int rc; libxl__domain_userdata_lock *lock; CTX_LOCK; lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__userdata_store(gc, domid, userdata_userid, data, datalen); libxl__unlock_domain_userdata(lock); out: CTX_UNLOCK; GC_FREE; return rc; } int libxl__userdata_retrieve(libxl__gc *gc, uint32_t domid, const char *userdata_userid, uint8_t **data_r, int *datalen_r) { const char *filename; int e, rc; int datalen = 0; void *data = 0; filename = libxl__userdata_path(gc, domid, userdata_userid, "d"); if (!filename) { rc = ERROR_NOMEM; goto out; } e = libxl_read_file_contents(CTX, filename, data_r ? &data : 0, &datalen); if (e && errno != ENOENT) { rc = ERROR_FAIL; goto out; } if (!e && !datalen) { LOG(ERROR, "userdata file %s is empty", filename); if (data_r) assert(!*data_r); rc = ERROR_FAIL; goto out; } if (data_r) *data_r = data; if (datalen_r) *datalen_r = datalen; rc = 0; out: return rc; } int libxl_userdata_retrieve(libxl_ctx *ctx, uint32_t domid, const char *userdata_userid, uint8_t **data_r, int *datalen_r) { GC_INIT(ctx); int rc; libxl__domain_userdata_lock *lock; CTX_LOCK; lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__userdata_retrieve(gc, domid, userdata_userid, data_r, datalen_r); libxl__unlock_domain_userdata(lock); out: CTX_UNLOCK; GC_FREE; return rc; } int libxl_userdata_unlink(libxl_ctx *ctx, uint32_t domid, const char *userdata_userid) { GC_INIT(ctx); CTX_LOCK; int rc; libxl__domain_userdata_lock *lock = NULL; const char *filename; lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } filename = libxl__userdata_path(gc, domid, userdata_userid, "d"); if (!filename) { rc = ERROR_FAIL; goto out; } if (unlink(filename)) { LOGE(ERROR, "error deleting userdata file: %s", filename); rc = ERROR_FAIL; goto out; } rc = 0; out: if (lock) libxl__unlock_domain_userdata(lock); CTX_UNLOCK; GC_FREE; return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/test_common.c0000664000175000017500000000252113256712137015422 0ustar smbsmb#include "test_common.h" libxl_ctx *ctx; void test_common_setup(int level) { xentoollog_logger_stdiostream *logger_s = xtl_createlogger_stdiostream(stderr, level, 0); assert(logger_s); xentoollog_logger *logger = (xentoollog_logger*)logger_s; int rc = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, logger); assert(!rc); } struct timeval now; void test_common_get_now(void) { int r = gettimeofday(&now, 0); assert(!r); } int poll_nfds, poll_nfds_allocd; struct pollfd *poll_fds; int poll_timeout; void test_common_beforepoll(void) { for (;;) { test_common_get_now(); poll_timeout = -1; poll_nfds = poll_nfds_allocd; int rc = libxl_osevent_beforepoll(ctx, &poll_nfds, poll_fds, &poll_timeout, now); if (!rc) return; assert(rc == ERROR_BUFFERFULL); assert(poll_nfds > poll_nfds_allocd); poll_fds = realloc(poll_fds, poll_nfds * sizeof(poll_fds[0])); assert(poll_fds); poll_nfds_allocd = poll_nfds; } } void test_common_dopoll(void) { errno = 0; int r = poll(poll_fds, poll_nfds, poll_timeout); fprintf(stderr, "poll: r=%d errno=%s\n", r, strerror(errno)); } void test_common_afterpoll(void) { test_common_get_now(); libxl_osevent_afterpoll(ctx, poll_nfds, poll_fds, now); } xen-4.9.2/tools/libxl/libxl_sched.c0000664000175000017500000006462013256712137015363 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" static int libxl__set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid, const libxl_bitmap *cpumap_hard, const libxl_bitmap *cpumap_soft, unsigned flags) { GC_INIT(ctx); libxl_bitmap hard, soft; int rc; libxl_bitmap_init(&hard); libxl_bitmap_init(&soft); if (!cpumap_hard && !cpumap_soft && !flags) { rc = ERROR_INVAL; goto out; } /* * Xen wants writable hard and/or soft cpumaps, to put back in them * the effective hard and/or soft affinity that will be used. */ if (cpumap_hard) { rc = libxl_cpu_bitmap_alloc(ctx, &hard, 0); if (rc) goto out; libxl__bitmap_copy_best_effort(gc, &hard, cpumap_hard); flags |= XEN_VCPUAFFINITY_HARD; } if (cpumap_soft) { rc = libxl_cpu_bitmap_alloc(ctx, &soft, 0); if (rc) goto out; libxl__bitmap_copy_best_effort(gc, &soft, cpumap_soft); flags |= XEN_VCPUAFFINITY_SOFT; } if (xc_vcpu_setaffinity(ctx->xch, domid, vcpuid, cpumap_hard ? hard.map : NULL, cpumap_soft ? soft.map : NULL, flags)) { LOGED(ERROR, domid, "Setting vcpu affinity"); rc = ERROR_FAIL; goto out; } /* * Let's check the results. Hard affinity will never be empty, but it * is possible that Xen will use something different from what we asked * for various reasons. If that's the case, report it. */ if (cpumap_hard && !libxl_bitmap_equal(cpumap_hard, &hard, 0)) LOGD(DEBUG, domid, "New hard affinity for vcpu %d has unreachable cpus", vcpuid); /* * Soft affinity can both be different from what asked and empty. Check * for (and report) both. */ if (cpumap_soft) { if (!libxl_bitmap_equal(cpumap_soft, &soft, 0)) LOGD(DEBUG, domid, "New soft affinity for vcpu %d has unreachable cpus", vcpuid); if (libxl_bitmap_is_empty(&soft)) LOGD(WARN, domid, "All cpus in soft affinity of vcpu %d are unreachable." " Only hard affinity will be considered for scheduling", vcpuid); } rc = 0; out: libxl_bitmap_dispose(&hard); libxl_bitmap_dispose(&soft); GC_FREE; return rc; } int libxl_set_vcpuaffinity(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid, const libxl_bitmap *cpumap_hard, const libxl_bitmap *cpumap_soft) { return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard, cpumap_soft, 0); } int libxl_set_vcpuaffinity_force(libxl_ctx *ctx, uint32_t domid, uint32_t vcpuid, const libxl_bitmap *cpumap_hard, const libxl_bitmap *cpumap_soft) { return libxl__set_vcpuaffinity(ctx, domid, vcpuid, cpumap_hard, cpumap_soft, XEN_VCPUAFFINITY_FORCE); } int libxl_set_vcpuaffinity_all(libxl_ctx *ctx, uint32_t domid, unsigned int max_vcpus, const libxl_bitmap *cpumap_hard, const libxl_bitmap *cpumap_soft) { GC_INIT(ctx); int i, rc = 0; for (i = 0; i < max_vcpus; i++) { if (libxl_set_vcpuaffinity(ctx, domid, i, cpumap_hard, cpumap_soft)) { LOGD(WARN, domid, "Failed to set affinity for %d", i); rc = ERROR_FAIL; } } GC_FREE; return rc; } int libxl_domain_set_nodeaffinity(libxl_ctx *ctx, uint32_t domid, libxl_bitmap *nodemap) { GC_INIT(ctx); if (xc_domain_node_setaffinity(ctx->xch, domid, nodemap->map)) { LOGED(ERROR, domid, "Setting node affinity"); GC_FREE; return ERROR_FAIL; } GC_FREE; return 0; } int libxl_domain_get_nodeaffinity(libxl_ctx *ctx, uint32_t domid, libxl_bitmap *nodemap) { GC_INIT(ctx); if (xc_domain_node_getaffinity(ctx->xch, domid, nodemap->map)) { LOGED(ERROR, domid, "Getting node affinity"); GC_FREE; return ERROR_FAIL; } GC_FREE; return 0; } int libxl_get_scheduler(libxl_ctx *ctx) { int r, sched; GC_INIT(ctx); r = xc_sched_id(ctx->xch, &sched); if (r != 0) { LOGE(ERROR, "getting current scheduler id"); sched = ERROR_FAIL; } GC_FREE; return sched; } static int sched_arinc653_domain_set(libxl__gc *gc, uint32_t domid, const libxl_domain_sched_params *scinfo) { /* Currently, the ARINC 653 scheduler does not take any domain-specific configuration, so we simply return success. */ return 0; } static int sched_null_domain_set(libxl__gc *gc, uint32_t domid, const libxl_domain_sched_params *scinfo) { /* There aren't any domain-specific parameters to be set. */ return 0; } static int sched_null_domain_get(libxl__gc *gc, uint32_t domid, libxl_domain_sched_params *scinfo) { /* There aren't any domain-specific parameters to return. */ return 0; } static int sched_credit_domain_get(libxl__gc *gc, uint32_t domid, libxl_domain_sched_params *scinfo) { struct xen_domctl_sched_credit sdom; int rc; rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom); if (rc != 0) { LOGED(ERROR, domid, "Getting domain sched credit"); return ERROR_FAIL; } libxl_domain_sched_params_init(scinfo); scinfo->sched = LIBXL_SCHEDULER_CREDIT; scinfo->weight = sdom.weight; scinfo->cap = sdom.cap; return 0; } static int sched_credit_domain_set(libxl__gc *gc, uint32_t domid, const libxl_domain_sched_params *scinfo) { struct xen_domctl_sched_credit sdom; xc_domaininfo_t domaininfo; int rc; rc = xc_domain_getinfolist(CTX->xch, domid, 1, &domaininfo); if (rc < 0) { LOGED(ERROR, domid, "Getting domain info list"); return ERROR_FAIL; } if (rc != 1 || domaininfo.domain != domid) return ERROR_INVAL; rc = xc_sched_credit_domain_get(CTX->xch, domid, &sdom); if (rc != 0) { LOGED(ERROR, domid, "Getting domain sched credit"); return ERROR_FAIL; } if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) { if (scinfo->weight < 1 || scinfo->weight > 65535) { LOGD(ERROR, domid, "Cpu weight out of range, " "valid values are within range from 1 to 65535"); return ERROR_INVAL; } sdom.weight = scinfo->weight; } if (scinfo->cap != LIBXL_DOMAIN_SCHED_PARAM_CAP_DEFAULT) { if (scinfo->cap < 0 || scinfo->cap > (domaininfo.max_vcpu_id + 1) * 100) { LOGD(ERROR, domid, "Cpu cap out of range, " "valid range is from 0 to %d for specified number of vcpus", ((domaininfo.max_vcpu_id + 1) * 100)); return ERROR_INVAL; } sdom.cap = scinfo->cap; } rc = xc_sched_credit_domain_set(CTX->xch, domid, &sdom); if ( rc < 0 ) { LOGED(ERROR, domid, "Setting domain sched credit"); return ERROR_FAIL; } return 0; } static int sched_ratelimit_check(libxl__gc *gc, int ratelimit) { if (ratelimit != 0 && (ratelimit < XEN_SYSCTL_SCHED_RATELIMIT_MIN || ratelimit > XEN_SYSCTL_SCHED_RATELIMIT_MAX)) { LOG(ERROR, "Ratelimit out of range, valid range is from %d to %d", XEN_SYSCTL_SCHED_RATELIMIT_MIN, XEN_SYSCTL_SCHED_RATELIMIT_MAX); return ERROR_INVAL; } return 0; } int libxl_sched_credit_params_get(libxl_ctx *ctx, uint32_t poolid, libxl_sched_credit_params *scinfo) { struct xen_sysctl_credit_schedule sparam; int r, rc; GC_INIT(ctx); r = xc_sched_credit_params_get(ctx->xch, poolid, &sparam); if (r < 0) { LOGE(ERROR, "getting Credit scheduler parameters"); rc = ERROR_FAIL; goto out; } scinfo->tslice_ms = sparam.tslice_ms; scinfo->ratelimit_us = sparam.ratelimit_us; rc = 0; out: GC_FREE; return rc; } int libxl_sched_credit_params_set(libxl_ctx *ctx, uint32_t poolid, libxl_sched_credit_params *scinfo) { struct xen_sysctl_credit_schedule sparam; int r, rc; GC_INIT(ctx); if (scinfo->tslice_ms < XEN_SYSCTL_CSCHED_TSLICE_MIN || scinfo->tslice_ms > XEN_SYSCTL_CSCHED_TSLICE_MAX) { LOG(ERROR, "Time slice out of range, valid range is from %d to %d", XEN_SYSCTL_CSCHED_TSLICE_MIN, XEN_SYSCTL_CSCHED_TSLICE_MAX); rc = ERROR_INVAL; goto out; } rc = sched_ratelimit_check(gc, scinfo->ratelimit_us); if (rc) { goto out; } if (scinfo->ratelimit_us > scinfo->tslice_ms*1000) { LOG(ERROR, "Ratelimit cannot be greater than timeslice"); rc = ERROR_INVAL; goto out; } sparam.tslice_ms = scinfo->tslice_ms; sparam.ratelimit_us = scinfo->ratelimit_us; r = xc_sched_credit_params_set(ctx->xch, poolid, &sparam); if ( r < 0 ) { LOGE(ERROR, "Setting Credit scheduler parameters"); rc = ERROR_FAIL; goto out; } scinfo->tslice_ms = sparam.tslice_ms; scinfo->ratelimit_us = sparam.ratelimit_us; rc = 0; out: GC_FREE; return rc; } int libxl_sched_credit2_params_get(libxl_ctx *ctx, uint32_t poolid, libxl_sched_credit2_params *scinfo) { struct xen_sysctl_credit2_schedule sparam; int r, rc; GC_INIT(ctx); r = xc_sched_credit2_params_get(ctx->xch, poolid, &sparam); if (r < 0) { LOGE(ERROR, "getting Credit2 scheduler parameters"); rc = ERROR_FAIL; goto out; } scinfo->ratelimit_us = sparam.ratelimit_us; rc = 0; out: GC_FREE; return rc; } int libxl_sched_credit2_params_set(libxl_ctx *ctx, uint32_t poolid, libxl_sched_credit2_params *scinfo) { struct xen_sysctl_credit2_schedule sparam; int r, rc; GC_INIT(ctx); rc = sched_ratelimit_check(gc, scinfo->ratelimit_us); if (rc) goto out; sparam.ratelimit_us = scinfo->ratelimit_us; r = xc_sched_credit2_params_set(ctx->xch, poolid, &sparam); if (r < 0) { LOGE(ERROR, "Setting Credit2 scheduler parameters"); rc = ERROR_FAIL; goto out; } scinfo->ratelimit_us = sparam.ratelimit_us; rc = 0; out: GC_FREE; return rc; } static int sched_credit2_domain_get(libxl__gc *gc, uint32_t domid, libxl_domain_sched_params *scinfo) { struct xen_domctl_sched_credit2 sdom; int rc; rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom); if (rc != 0) { LOGED(ERROR, domid, "Getting domain sched credit2"); return ERROR_FAIL; } libxl_domain_sched_params_init(scinfo); scinfo->sched = LIBXL_SCHEDULER_CREDIT2; scinfo->weight = sdom.weight; return 0; } static int sched_credit2_domain_set(libxl__gc *gc, uint32_t domid, const libxl_domain_sched_params *scinfo) { struct xen_domctl_sched_credit2 sdom; int rc; rc = xc_sched_credit2_domain_get(CTX->xch, domid, &sdom); if (rc != 0) { LOGED(ERROR, domid, "Getting domain sched credit2"); return ERROR_FAIL; } if (scinfo->weight != LIBXL_DOMAIN_SCHED_PARAM_WEIGHT_DEFAULT) { if (scinfo->weight < 1 || scinfo->weight > 65535) { LOGD(ERROR, domid, "Cpu weight out of range, " "valid values are within range from 1 to 65535"); return ERROR_INVAL; } sdom.weight = scinfo->weight; } rc = xc_sched_credit2_domain_set(CTX->xch, domid, &sdom); if ( rc < 0 ) { LOGED(ERROR, domid, "Setting domain sched credit2"); return ERROR_FAIL; } return 0; } static int sched_rtds_validate_params(libxl__gc *gc, int period, int budget) { int rc; if (period < 1) { LOG(ERROR, "Invalid VCPU period of %d (it should be >= 1)", period); rc = ERROR_INVAL; goto out; } if (budget < 1) { LOG(ERROR, "Invalid VCPU budget of %d (it should be >= 1)", budget); rc = ERROR_INVAL; goto out; } if (budget > period) { LOG(ERROR, "VCPU budget must be smaller than or equal to period, " "but %d > %d", budget, period); rc = ERROR_INVAL; goto out; } rc = 0; out: return rc; } /* Get the RTDS scheduling parameters of vcpu(s) */ static int sched_rtds_vcpu_get(libxl__gc *gc, uint32_t domid, libxl_vcpu_sched_params *scinfo) { uint32_t num_vcpus; int i, r, rc; xc_dominfo_t info; struct xen_domctl_schedparam_vcpu *vcpus; r = xc_domain_getinfo(CTX->xch, domid, 1, &info); if (r < 0) { LOGED(ERROR, domid, "Getting domain info"); rc = ERROR_FAIL; goto out; } if (scinfo->num_vcpus <= 0) { rc = ERROR_INVAL; goto out; } else { num_vcpus = scinfo->num_vcpus; GCNEW_ARRAY(vcpus, num_vcpus); for (i = 0; i < num_vcpus; i++) { if (scinfo->vcpus[i].vcpuid < 0 || scinfo->vcpus[i].vcpuid > info.max_vcpu_id) { LOGD(ERROR, domid, "VCPU index is out of range, " "valid values are within range from 0 to %d", info.max_vcpu_id); rc = ERROR_INVAL; goto out; } vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid; } } r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus); if (r != 0) { LOGED(ERROR, domid, "Getting vcpu sched rtds"); rc = ERROR_FAIL; goto out; } scinfo->sched = LIBXL_SCHEDULER_RTDS; for (i = 0; i < num_vcpus; i++) { scinfo->vcpus[i].period = vcpus[i].u.rtds.period; scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget; scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid; } rc = 0; out: return rc; } /* Get the RTDS scheduling parameters of all vcpus of a domain */ static int sched_rtds_vcpu_get_all(libxl__gc *gc, uint32_t domid, libxl_vcpu_sched_params *scinfo) { uint32_t num_vcpus; int i, r, rc; xc_dominfo_t info; struct xen_domctl_schedparam_vcpu *vcpus; r = xc_domain_getinfo(CTX->xch, domid, 1, &info); if (r < 0) { LOGED(ERROR, domid, "Getting domain info"); rc = ERROR_FAIL; goto out; } if (scinfo->num_vcpus > 0) { rc = ERROR_INVAL; goto out; } else { num_vcpus = info.max_vcpu_id + 1; GCNEW_ARRAY(vcpus, num_vcpus); for (i = 0; i < num_vcpus; i++) vcpus[i].vcpuid = i; } r = xc_sched_rtds_vcpu_get(CTX->xch, domid, vcpus, num_vcpus); if (r != 0) { LOGED(ERROR, domid, "Getting vcpu sched rtds"); rc = ERROR_FAIL; goto out; } scinfo->sched = LIBXL_SCHEDULER_RTDS; scinfo->num_vcpus = num_vcpus; scinfo->vcpus = libxl__calloc(NOGC, num_vcpus, sizeof(libxl_sched_params)); for (i = 0; i < num_vcpus; i++) { scinfo->vcpus[i].period = vcpus[i].u.rtds.period; scinfo->vcpus[i].budget = vcpus[i].u.rtds.budget; scinfo->vcpus[i].vcpuid = vcpus[i].vcpuid; } rc = 0; out: return rc; } /* Set the RTDS scheduling parameters of vcpu(s) */ static int sched_rtds_vcpu_set(libxl__gc *gc, uint32_t domid, const libxl_vcpu_sched_params *scinfo) { int r, rc; int i; uint16_t max_vcpuid; xc_dominfo_t info; struct xen_domctl_schedparam_vcpu *vcpus; r = xc_domain_getinfo(CTX->xch, domid, 1, &info); if (r < 0) { LOGED(ERROR, domid, "Getting domain info"); rc = ERROR_FAIL; goto out; } max_vcpuid = info.max_vcpu_id; if (scinfo->num_vcpus <= 0) { rc = ERROR_INVAL; goto out; } for (i = 0; i < scinfo->num_vcpus; i++) { if (scinfo->vcpus[i].vcpuid < 0 || scinfo->vcpus[i].vcpuid > max_vcpuid) { LOGD(ERROR, domid, "Invalid VCPU %d: valid range is [0, %d]", scinfo->vcpus[i].vcpuid, max_vcpuid); rc = ERROR_INVAL; goto out; } rc = sched_rtds_validate_params(gc, scinfo->vcpus[i].period, scinfo->vcpus[i].budget); if (rc) { rc = ERROR_INVAL; goto out; } } GCNEW_ARRAY(vcpus, scinfo->num_vcpus); for (i = 0; i < scinfo->num_vcpus; i++) { vcpus[i].vcpuid = scinfo->vcpus[i].vcpuid; vcpus[i].u.rtds.period = scinfo->vcpus[i].period; vcpus[i].u.rtds.budget = scinfo->vcpus[i].budget; } r = xc_sched_rtds_vcpu_set(CTX->xch, domid, vcpus, scinfo->num_vcpus); if (r != 0) { LOGED(ERROR, domid, "Setting vcpu sched rtds"); rc = ERROR_FAIL; goto out; } rc = 0; out: return rc; } /* Set the RTDS scheduling parameters of all vcpus of a domain */ static int sched_rtds_vcpu_set_all(libxl__gc *gc, uint32_t domid, const libxl_vcpu_sched_params *scinfo) { int r, rc; int i; uint16_t max_vcpuid; xc_dominfo_t info; struct xen_domctl_schedparam_vcpu *vcpus; uint32_t num_vcpus; r = xc_domain_getinfo(CTX->xch, domid, 1, &info); if (r < 0) { LOGED(ERROR, domid, "Getting domain info"); rc = ERROR_FAIL; goto out; } max_vcpuid = info.max_vcpu_id; if (scinfo->num_vcpus != 1) { rc = ERROR_INVAL; goto out; } if (sched_rtds_validate_params(gc, scinfo->vcpus[0].period, scinfo->vcpus[0].budget)) { rc = ERROR_INVAL; goto out; } num_vcpus = max_vcpuid + 1; GCNEW_ARRAY(vcpus, num_vcpus); for (i = 0; i < num_vcpus; i++) { vcpus[i].vcpuid = i; vcpus[i].u.rtds.period = scinfo->vcpus[0].period; vcpus[i].u.rtds.budget = scinfo->vcpus[0].budget; } r = xc_sched_rtds_vcpu_set(CTX->xch, domid, vcpus, num_vcpus); if (r != 0) { LOGED(ERROR, domid, "Setting vcpu sched rtds"); rc = ERROR_FAIL; goto out; } rc = 0; out: return rc; } static int sched_rtds_domain_get(libxl__gc *gc, uint32_t domid, libxl_domain_sched_params *scinfo) { struct xen_domctl_sched_rtds sdom; int rc; rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom); if (rc != 0) { LOGED(ERROR, domid, "Getting domain sched rtds"); return ERROR_FAIL; } libxl_domain_sched_params_init(scinfo); scinfo->sched = LIBXL_SCHEDULER_RTDS; scinfo->period = sdom.period; scinfo->budget = sdom.budget; return 0; } static int sched_rtds_domain_set(libxl__gc *gc, uint32_t domid, const libxl_domain_sched_params *scinfo) { struct xen_domctl_sched_rtds sdom; int rc; rc = xc_sched_rtds_domain_get(CTX->xch, domid, &sdom); if (rc != 0) { LOGED(ERROR, domid, "Getting domain sched rtds"); return ERROR_FAIL; } if (scinfo->period != LIBXL_DOMAIN_SCHED_PARAM_PERIOD_DEFAULT) sdom.period = scinfo->period; if (scinfo->budget != LIBXL_DOMAIN_SCHED_PARAM_BUDGET_DEFAULT) sdom.budget = scinfo->budget; if (sched_rtds_validate_params(gc, sdom.period, sdom.budget)) return ERROR_INVAL; rc = xc_sched_rtds_domain_set(CTX->xch, domid, &sdom); if (rc < 0) { LOGED(ERROR, domid, "Setting domain sched rtds"); return ERROR_FAIL; } return 0; } int libxl_domain_sched_params_set(libxl_ctx *ctx, uint32_t domid, const libxl_domain_sched_params *scinfo) { GC_INIT(ctx); libxl_scheduler sched = scinfo->sched; int ret; if (sched == LIBXL_SCHEDULER_UNKNOWN) sched = libxl__domain_scheduler(gc, domid); switch (sched) { case LIBXL_SCHEDULER_SEDF: LOGD(ERROR, domid, "SEDF scheduler no longer available"); ret=ERROR_FEATURE_REMOVED; break; case LIBXL_SCHEDULER_CREDIT: ret=sched_credit_domain_set(gc, domid, scinfo); break; case LIBXL_SCHEDULER_CREDIT2: ret=sched_credit2_domain_set(gc, domid, scinfo); break; case LIBXL_SCHEDULER_ARINC653: ret=sched_arinc653_domain_set(gc, domid, scinfo); break; case LIBXL_SCHEDULER_RTDS: ret=sched_rtds_domain_set(gc, domid, scinfo); break; case LIBXL_SCHEDULER_NULL: ret=sched_null_domain_set(gc, domid, scinfo); break; default: LOGD(ERROR, domid, "Unknown scheduler"); ret=ERROR_INVAL; break; } GC_FREE; return ret; } int libxl_vcpu_sched_params_set(libxl_ctx *ctx, uint32_t domid, const libxl_vcpu_sched_params *scinfo) { GC_INIT(ctx); libxl_scheduler sched = scinfo->sched; int rc; if (sched == LIBXL_SCHEDULER_UNKNOWN) sched = libxl__domain_scheduler(gc, domid); switch (sched) { case LIBXL_SCHEDULER_SEDF: LOGD(ERROR, domid, "SEDF scheduler no longer available"); rc = ERROR_FEATURE_REMOVED; break; case LIBXL_SCHEDULER_CREDIT: case LIBXL_SCHEDULER_CREDIT2: case LIBXL_SCHEDULER_ARINC653: case LIBXL_SCHEDULER_NULL: LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler"); rc = ERROR_INVAL; break; case LIBXL_SCHEDULER_RTDS: rc = sched_rtds_vcpu_set(gc, domid, scinfo); break; default: LOGD(ERROR, domid, "Unknown scheduler"); rc = ERROR_INVAL; break; } GC_FREE; return rc; } int libxl_vcpu_sched_params_set_all(libxl_ctx *ctx, uint32_t domid, const libxl_vcpu_sched_params *scinfo) { GC_INIT(ctx); libxl_scheduler sched = scinfo->sched; int rc; if (sched == LIBXL_SCHEDULER_UNKNOWN) sched = libxl__domain_scheduler(gc, domid); switch (sched) { case LIBXL_SCHEDULER_SEDF: LOGD(ERROR, domid, "SEDF scheduler no longer available"); rc = ERROR_FEATURE_REMOVED; break; case LIBXL_SCHEDULER_CREDIT: case LIBXL_SCHEDULER_CREDIT2: case LIBXL_SCHEDULER_ARINC653: case LIBXL_SCHEDULER_NULL: LOGD(ERROR, domid, "per-VCPU parameter setting not supported for this scheduler"); rc = ERROR_INVAL; break; case LIBXL_SCHEDULER_RTDS: rc = sched_rtds_vcpu_set_all(gc, domid, scinfo); break; default: LOGD(ERROR, domid, "Unknown scheduler"); rc = ERROR_INVAL; break; } GC_FREE; return rc; } int libxl_domain_sched_params_get(libxl_ctx *ctx, uint32_t domid, libxl_domain_sched_params *scinfo) { GC_INIT(ctx); int ret; libxl_domain_sched_params_init(scinfo); scinfo->sched = libxl__domain_scheduler(gc, domid); switch (scinfo->sched) { case LIBXL_SCHEDULER_SEDF: LOGD(ERROR, domid, "SEDF scheduler no longer available"); ret=ERROR_FEATURE_REMOVED; break; case LIBXL_SCHEDULER_CREDIT: ret=sched_credit_domain_get(gc, domid, scinfo); break; case LIBXL_SCHEDULER_CREDIT2: ret=sched_credit2_domain_get(gc, domid, scinfo); break; case LIBXL_SCHEDULER_RTDS: ret=sched_rtds_domain_get(gc, domid, scinfo); break; case LIBXL_SCHEDULER_NULL: ret=sched_null_domain_get(gc, domid, scinfo); break; default: LOGD(ERROR, domid, "Unknown scheduler"); ret=ERROR_INVAL; break; } GC_FREE; return ret; } int libxl_vcpu_sched_params_get(libxl_ctx *ctx, uint32_t domid, libxl_vcpu_sched_params *scinfo) { GC_INIT(ctx); int rc; scinfo->sched = libxl__domain_scheduler(gc, domid); switch (scinfo->sched) { case LIBXL_SCHEDULER_SEDF: LOGD(ERROR, domid, "SEDF scheduler is no longer available"); rc = ERROR_FEATURE_REMOVED; break; case LIBXL_SCHEDULER_CREDIT: case LIBXL_SCHEDULER_CREDIT2: case LIBXL_SCHEDULER_ARINC653: case LIBXL_SCHEDULER_NULL: LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler"); rc = ERROR_INVAL; break; case LIBXL_SCHEDULER_RTDS: rc = sched_rtds_vcpu_get(gc, domid, scinfo); break; default: LOGD(ERROR, domid, "Unknown scheduler"); rc = ERROR_INVAL; break; } GC_FREE; return rc; } int libxl_vcpu_sched_params_get_all(libxl_ctx *ctx, uint32_t domid, libxl_vcpu_sched_params *scinfo) { GC_INIT(ctx); int rc; scinfo->sched = libxl__domain_scheduler(gc, domid); switch (scinfo->sched) { case LIBXL_SCHEDULER_SEDF: LOGD(ERROR, domid, "SEDF scheduler is no longer available"); rc = ERROR_FEATURE_REMOVED; break; case LIBXL_SCHEDULER_CREDIT: case LIBXL_SCHEDULER_CREDIT2: case LIBXL_SCHEDULER_ARINC653: case LIBXL_SCHEDULER_NULL: LOGD(ERROR, domid, "per-VCPU parameter getting not supported for this scheduler"); rc = ERROR_INVAL; break; case LIBXL_SCHEDULER_RTDS: rc = sched_rtds_vcpu_get_all(gc, domid, scinfo); break; default: LOGD(ERROR, domid, "Unknown scheduler"); rc = ERROR_INVAL; break; } GC_FREE; return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_arm.h0000664000175000017500000000267513256712137015063 0ustar smbsmb/* * Copyright (C) 2016 Linaro Ltd. * * Author: Shannon Zhao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_internal.h" #include "libxl_arch.h" #include _hidden int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info, struct xc_dom_image *dom); _hidden int libxl__get_acpi_size(libxl__gc *gc, const libxl_domain_build_info *info, uint64_t *out); static inline uint64_t libxl__compute_mpdir(unsigned int cpuid) { /* * According to ARM CPUs bindings, the reg field should match * the MPIDR's affinity bits. We will use AFF0 and AFF1 when * constructing the reg value of the guest at the moment, for it * is enough for the current max vcpu number. */ return (cpuid & 0x0f) | (((cpuid >> 4) & 0xff) << 8); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_x86_acpi.h0000664000175000017500000000170313256712137015714 0ustar smbsmb/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. */ #ifndef LIBXL_X86_ACPI_H #define LIBXL_X86_ACPI_H #include "libxl_internal.h" #define ASSERT(x) assert(x) static inline int test_bit(unsigned int b, const void *p) { return !!(((const uint8_t *)p)[b>>3] & (1u<<(b&7))); } #endif /* LIBXL_X_86_ACPI_H */ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_nic.c0000664000175000017500000005417413256712137015051 0ustar smbsmb/* * Copyright (C) 2016 SUSE Linux GmbH * Author Juergen Gross * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid, const char *mac, libxl_device_nic *nic) { libxl_device_nic *nics; int nb, rc, i; libxl_mac mac_n; rc = libxl__parse_mac(mac, mac_n); if (rc) return rc; nics = libxl_device_nic_list(ctx, domid, &nb); if (!nics) return ERROR_FAIL; memset(nic, 0, sizeof (libxl_device_nic)); rc = ERROR_INVAL; for (i = 0; i < nb; ++i) { if (!libxl__compare_macs(&mac_n, &nics[i].mac)) { *nic = nics[i]; rc = 0; i++; /* Do not dispose this NIC on exit path */ break; } libxl_device_nic_dispose(&nics[i]); } for (; imtu) nic->mtu = 1492; if (!nic->model) { nic->model = strdup("rtl8139"); if (!nic->model) return ERROR_NOMEM; } if (libxl__mac_is_default(&nic->mac)) { const uint8_t *r; libxl_uuid uuid; libxl_uuid_generate(&uuid); r = libxl_uuid_bytearray(&uuid); nic->mac[0] = 0x00; nic->mac[1] = 0x16; nic->mac[2] = 0x3e; nic->mac[3] = r[0] & 0x7f; nic->mac[4] = r[1]; nic->mac[5] = r[2]; } if (!nic->bridge) { nic->bridge = strdup("xenbr0"); if (!nic->bridge) return ERROR_NOMEM; } if ( !nic->script && asprintf(&nic->script, "%s/vif-bridge", libxl__xen_script_dir_path()) < 0 ) return ERROR_FAIL; rc = libxl__resolve_domid(gc, nic->backend_domname, &nic->backend_domid); if (rc < 0) return rc; switch (libxl__domain_type(gc, domid)) { case LIBXL_DOMAIN_TYPE_HVM: if (!nic->nictype) { if (hotplug || (libxl__device_model_version_running(gc, domid) == LIBXL_DEVICE_MODEL_VERSION_NONE)) nic->nictype = LIBXL_NIC_TYPE_VIF; else nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU; } break; case LIBXL_DOMAIN_TYPE_PV: if (nic->nictype == LIBXL_NIC_TYPE_VIF_IOEMU) { LOGD(ERROR, domid, "trying to create PV guest with an emulated interface"); return ERROR_INVAL; } nic->nictype = LIBXL_NIC_TYPE_VIF; break; case LIBXL_DOMAIN_TYPE_INVALID: return ERROR_FAIL; default: abort(); } return rc; } static int libxl__device_from_nic(libxl__gc *gc, uint32_t domid, libxl_device_nic *nic, libxl__device *device) { device->backend_devid = nic->devid; device->backend_domid = nic->backend_domid; device->backend_kind = LIBXL__DEVICE_KIND_VIF; device->devid = nic->devid; device->domid = domid; device->kind = LIBXL__DEVICE_KIND_VIF; return 0; } static void libxl__update_config_nic(libxl__gc *gc, libxl_device_nic *dst, const libxl_device_nic *src) { dst->devid = src->devid; dst->nictype = src->nictype; libxl_mac_copy(CTX, &dst->mac, &src->mac); } static void libxl__device_nic_add(libxl__egc *egc, uint32_t domid, libxl_device_nic *nic, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); flexarray_t *front; flexarray_t *back; libxl__device *device; int rc; xs_transaction_t t = XBT_NULL; libxl_domain_config d_config; libxl_device_nic nic_saved; libxl__domain_userdata_lock *lock = NULL; libxl_domain_config_init(&d_config); libxl_device_nic_init(&nic_saved); libxl_device_nic_copy(CTX, &nic_saved, nic); rc = libxl__device_nic_setdefault(gc, nic, domid, aodev->update_json); if (rc) goto out; front = flexarray_make(gc, 16, 1); back = flexarray_make(gc, 18, 1); if (nic->devid == -1) { if ((nic->devid = libxl__device_nextid(gc, domid, "vif")) < 0) { rc = ERROR_FAIL; goto out; } } libxl__update_config_nic(gc, &nic_saved, nic); GCNEW(device); rc = libxl__device_from_nic(gc, domid, nic, device); if ( rc != 0 ) goto out; flexarray_append(back, "frontend-id"); flexarray_append(back, GCSPRINTF("%d", domid)); flexarray_append(back, "online"); flexarray_append(back, "1"); flexarray_append(back, "state"); flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising)); if (nic->script) flexarray_append_pair(back, "script", libxl__abs_path(gc, nic->script, libxl__xen_script_dir_path())); if (nic->ifname) { flexarray_append(back, "vifname"); flexarray_append(back, nic->ifname); } if (nic->coloft_forwarddev) { flexarray_append(back, "forwarddev"); flexarray_append(back, nic->coloft_forwarddev); } #define MAYBE_ADD_COLO_ARGS(arg) ({ \ if (nic->colo_##arg) { \ flexarray_append(back, "colo_"#arg); \ flexarray_append(back, nic->colo_##arg); \ } \ }) MAYBE_ADD_COLO_ARGS(sock_mirror_id); MAYBE_ADD_COLO_ARGS(sock_mirror_ip); MAYBE_ADD_COLO_ARGS(sock_mirror_port); MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_id); MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_ip); MAYBE_ADD_COLO_ARGS(sock_compare_pri_in_port); MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_id); MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_ip); MAYBE_ADD_COLO_ARGS(sock_compare_sec_in_port); MAYBE_ADD_COLO_ARGS(sock_compare_notify_id); MAYBE_ADD_COLO_ARGS(sock_compare_notify_ip); MAYBE_ADD_COLO_ARGS(sock_compare_notify_port); MAYBE_ADD_COLO_ARGS(sock_redirector0_id); MAYBE_ADD_COLO_ARGS(sock_redirector0_ip); MAYBE_ADD_COLO_ARGS(sock_redirector0_port); MAYBE_ADD_COLO_ARGS(sock_redirector1_id); MAYBE_ADD_COLO_ARGS(sock_redirector1_ip); MAYBE_ADD_COLO_ARGS(sock_redirector1_port); MAYBE_ADD_COLO_ARGS(sock_redirector2_id); MAYBE_ADD_COLO_ARGS(sock_redirector2_ip); MAYBE_ADD_COLO_ARGS(sock_redirector2_port); MAYBE_ADD_COLO_ARGS(filter_mirror_queue); MAYBE_ADD_COLO_ARGS(filter_mirror_outdev); MAYBE_ADD_COLO_ARGS(filter_redirector0_queue); MAYBE_ADD_COLO_ARGS(filter_redirector0_indev); MAYBE_ADD_COLO_ARGS(filter_redirector0_outdev); MAYBE_ADD_COLO_ARGS(filter_redirector1_queue); MAYBE_ADD_COLO_ARGS(filter_redirector1_indev); MAYBE_ADD_COLO_ARGS(filter_redirector1_outdev); MAYBE_ADD_COLO_ARGS(compare_pri_in); MAYBE_ADD_COLO_ARGS(compare_sec_in); MAYBE_ADD_COLO_ARGS(compare_out); MAYBE_ADD_COLO_ARGS(compare_notify_dev); MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_id); MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_ip); MAYBE_ADD_COLO_ARGS(sock_sec_redirector0_port); MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_id); MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_ip); MAYBE_ADD_COLO_ARGS(sock_sec_redirector1_port); MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_queue); MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_indev); MAYBE_ADD_COLO_ARGS(filter_sec_redirector0_outdev); MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_queue); MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_indev); MAYBE_ADD_COLO_ARGS(filter_sec_redirector1_outdev); MAYBE_ADD_COLO_ARGS(filter_sec_rewriter0_queue); MAYBE_ADD_COLO_ARGS(checkpoint_host); MAYBE_ADD_COLO_ARGS(checkpoint_port); #undef MAYBE_ADD_COLO_ARGS flexarray_append(back, "mac"); flexarray_append(back,GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac))); if (nic->ip) { flexarray_append(back, "ip"); flexarray_append(back, libxl__strdup(gc, nic->ip)); } if (nic->gatewaydev) { flexarray_append(back, "gatewaydev"); flexarray_append(back, libxl__strdup(gc, nic->gatewaydev)); } if (nic->rate_interval_usecs > 0) { flexarray_append(back, "rate"); flexarray_append(back, GCSPRINTF("%"PRIu64",%"PRIu32"", nic->rate_bytes_per_interval, nic->rate_interval_usecs)); } flexarray_append(back, "bridge"); flexarray_append(back, libxl__strdup(gc, nic->bridge)); flexarray_append(back, "handle"); flexarray_append(back, GCSPRINTF("%d", nic->devid)); flexarray_append(back, "type"); flexarray_append(back, libxl__strdup(gc, libxl_nic_type_to_string(nic->nictype))); flexarray_append(front, "backend-id"); flexarray_append(front, GCSPRINTF("%d", nic->backend_domid)); flexarray_append(front, "state"); flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append(front, "handle"); flexarray_append(front, GCSPRINTF("%d", nic->devid)); flexarray_append(front, "mac"); flexarray_append(front, GCSPRINTF( LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac))); if (aodev->update_json) { lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc) goto out; DEVICE_ADD(nic, nics, domid, &nic_saved, COMPARE_DEVID, &d_config); rc = libxl__dm_check_start(gc, &d_config, domid); if (rc) goto out; } for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; rc = libxl__device_exists(gc, t, device); if (rc < 0) goto out; if (rc == 1) { /* already exists in xenstore */ LOGD(ERROR, domid, "device already exists in xenstore"); aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */ rc = ERROR_DEVICE_EXISTS; goto out; } if (aodev->update_json) { rc = libxl__set_domain_configuration(gc, domid, &d_config); if (rc) goto out; } libxl__device_generic_add(gc, t, device, libxl__xs_kvs_of_flexarray(gc, back), libxl__xs_kvs_of_flexarray(gc, front), NULL); rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } aodev->dev = device; aodev->action = LIBXL__DEVICE_ACTION_ADD; libxl__wait_device_connection(egc, aodev); rc = 0; out: libxl__xs_transaction_abort(gc, &t); if (lock) libxl__unlock_domain_userdata(lock); libxl_device_nic_dispose(&nic_saved); libxl_domain_config_dispose(&d_config); aodev->rc = rc; if (rc) aodev->callback(egc, aodev); return; } static int libxl__device_nic_from_xenstore(libxl__gc *gc, const char *libxl_path, libxl_device_nic *nic) { const char *tmp; int rc; libxl_device_nic_init(nic); rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/handle", libxl_path), &tmp); if (rc) goto out; if (tmp) nic->devid = atoi(tmp); else nic->devid = 0; rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/backend", libxl_path), &tmp); if (rc) goto out; if (!tmp) { LOG(ERROR, "nic %s does not exist (no backend path)", libxl_path); rc = ERROR_FAIL; goto out; } rc = libxl__backendpath_parse_domid(gc, tmp, &nic->backend_domid); if (rc) goto out; /* nic->mtu = */ rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/mac", libxl_path), &tmp); if (rc) goto out; if (tmp) { rc = libxl__parse_mac(tmp, nic->mac); if (rc) goto out; } else { memset(nic->mac, 0, sizeof(nic->mac)); } rc = libxl__xs_read_checked(NOGC, XBT_NULL, GCSPRINTF("%s/ip", libxl_path), (const char **)(&nic->ip)); if (rc) goto out; rc = libxl__xs_read_checked(NOGC, XBT_NULL, GCSPRINTF("%s/bridge", libxl_path), (const char **)(&nic->bridge)); if (rc) goto out; rc = libxl__xs_read_checked(NOGC, XBT_NULL, GCSPRINTF("%s/script", libxl_path), (const char **)(&nic->script)); if (rc) goto out; rc = libxl__xs_read_checked(NOGC, XBT_NULL, GCSPRINTF("%s/forwarddev", libxl_path), (const char **)(&nic->coloft_forwarddev)); if (rc) goto out; #define CHECK_COLO_ARGS(arg) ({ \ rc = libxl__xs_read_checked(NOGC, XBT_NULL, \ GCSPRINTF("%s/colo_"#arg, libxl_path), \ (const char **)(&nic->colo_##arg)); \ if (rc) goto out; \ }) CHECK_COLO_ARGS(sock_mirror_id); CHECK_COLO_ARGS(sock_mirror_ip); CHECK_COLO_ARGS(sock_mirror_port); CHECK_COLO_ARGS(sock_compare_pri_in_id); CHECK_COLO_ARGS(sock_compare_pri_in_ip); CHECK_COLO_ARGS(sock_compare_pri_in_port); CHECK_COLO_ARGS(sock_compare_sec_in_id); CHECK_COLO_ARGS(sock_compare_sec_in_ip); CHECK_COLO_ARGS(sock_compare_sec_in_port); CHECK_COLO_ARGS(sock_compare_notify_id); CHECK_COLO_ARGS(sock_compare_notify_ip); CHECK_COLO_ARGS(sock_compare_notify_port); CHECK_COLO_ARGS(sock_redirector0_id); CHECK_COLO_ARGS(sock_redirector0_ip); CHECK_COLO_ARGS(sock_redirector0_port); CHECK_COLO_ARGS(sock_redirector1_id); CHECK_COLO_ARGS(sock_redirector1_ip); CHECK_COLO_ARGS(sock_redirector1_port); CHECK_COLO_ARGS(sock_redirector2_id); CHECK_COLO_ARGS(sock_redirector2_ip); CHECK_COLO_ARGS(sock_redirector2_port); CHECK_COLO_ARGS(filter_mirror_queue); CHECK_COLO_ARGS(filter_mirror_outdev); CHECK_COLO_ARGS(filter_redirector0_queue); CHECK_COLO_ARGS(filter_redirector0_indev); CHECK_COLO_ARGS(filter_redirector0_outdev); CHECK_COLO_ARGS(filter_redirector1_queue); CHECK_COLO_ARGS(filter_redirector1_indev); CHECK_COLO_ARGS(filter_redirector1_outdev); CHECK_COLO_ARGS(compare_pri_in); CHECK_COLO_ARGS(compare_sec_in); CHECK_COLO_ARGS(compare_out); CHECK_COLO_ARGS(compare_notify_dev); CHECK_COLO_ARGS(sock_sec_redirector0_id); CHECK_COLO_ARGS(sock_sec_redirector0_ip); CHECK_COLO_ARGS(sock_sec_redirector0_port); CHECK_COLO_ARGS(sock_sec_redirector1_id); CHECK_COLO_ARGS(sock_sec_redirector1_ip); CHECK_COLO_ARGS(sock_sec_redirector1_port); CHECK_COLO_ARGS(filter_sec_redirector0_queue); CHECK_COLO_ARGS(filter_sec_redirector0_indev); CHECK_COLO_ARGS(filter_sec_redirector0_outdev); CHECK_COLO_ARGS(filter_sec_redirector1_queue); CHECK_COLO_ARGS(filter_sec_redirector1_indev); CHECK_COLO_ARGS(filter_sec_redirector1_outdev); CHECK_COLO_ARGS(filter_sec_rewriter0_queue); CHECK_COLO_ARGS(checkpoint_host); CHECK_COLO_ARGS(checkpoint_port); #undef CHECK_COLO_ARGS /* vif_ioemu nics use the same xenstore entries as vif interfaces */ rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/type", libxl_path), &tmp); if (rc) goto out; if (tmp) { rc = libxl_nic_type_from_string(tmp, &nic->nictype); if (rc) goto out; } else { nic->nictype = LIBXL_NIC_TYPE_VIF; } nic->model = NULL; /* XXX Only for TYPE_IOEMU */ nic->ifname = NULL; /* XXX Only for TYPE_IOEMU */ rc = 0; out: return rc; } int libxl_devid_to_device_nic(libxl_ctx *ctx, uint32_t domid, int devid, libxl_device_nic *nic) { GC_INIT(ctx); char *libxl_dom_path, *libxl_path; int rc = ERROR_FAIL; libxl_device_nic_init(nic); libxl_dom_path = libxl__xs_libxl_path(gc, domid); if (!libxl_dom_path) goto out; libxl_path = GCSPRINTF("%s/device/vif/%d", libxl_dom_path, devid); rc = libxl__device_nic_from_xenstore(gc, libxl_path, nic); if (rc) goto out; rc = 0; out: GC_FREE; return rc; } static int libxl__append_nic_list(libxl__gc *gc, uint32_t domid, libxl_device_nic **nics, int *nnics) { char *libxl_dir_path = NULL; char **dir = NULL; unsigned int n = 0; libxl_device_nic *pnic = NULL, *pnic_end = NULL; int rc; libxl_dir_path = GCSPRINTF("%s/device/vif", libxl__xs_libxl_path(gc, domid)); dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n); if (dir && n) { libxl_device_nic *tmp; tmp = realloc(*nics, sizeof (libxl_device_nic) * (*nnics + n)); if (tmp == NULL) return ERROR_NOMEM; *nics = tmp; pnic = *nics + *nnics; pnic_end = *nics + *nnics + n; for (; pnic < pnic_end; pnic++, dir++) { const char *p; p = GCSPRINTF("%s/%s", libxl_dir_path, *dir); rc = libxl__device_nic_from_xenstore(gc, p, pnic); if (rc) goto out; } *nnics += n; } return 0; out: return rc; } libxl_device_nic *libxl_device_nic_list(libxl_ctx *ctx, uint32_t domid, int *num) { GC_INIT(ctx); libxl_device_nic *nics = NULL; int rc; *num = 0; rc = libxl__append_nic_list(gc, domid, &nics, num); if (rc) goto out_err; GC_FREE; return nics; out_err: LOGD(ERROR, domid, "Unable to list nics"); while (*num) { (*num)--; libxl_device_nic_dispose(&nics[*num]); } free(nics); return NULL; } int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic, libxl_nicinfo *nicinfo) { GC_INIT(ctx); char *dompath, *nicpath, *libxl_path; char *val; int rc; dompath = libxl__xs_get_dompath(gc, domid); nicinfo->devid = nic->devid; nicpath = GCSPRINTF("%s/device/vif/%d", dompath, nicinfo->devid); libxl_path = GCSPRINTF("%s/device/vif/%d", libxl__xs_libxl_path(gc, domid), nicinfo->devid); nicinfo->backend = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/backend", libxl_path), NULL); if (!nicinfo->backend) { GC_FREE; return ERROR_FAIL; } rc = libxl__backendpath_parse_domid(gc, nicinfo->backend, &nicinfo->backend_id); if (rc) goto out; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", nicpath)); nicinfo->state = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", nicpath)); nicinfo->evtch = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tx-ring-ref", nicpath)); nicinfo->rref_tx = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/rx-ring-ref", nicpath)); nicinfo->rref_rx = val ? strtoul(val, NULL, 10) : -1; nicinfo->frontend = libxl__strdup(NOGC, nicpath); nicinfo->frontend_id = domid; rc = 0; out: GC_FREE; return rc; } const char *libxl__device_nic_devname(libxl__gc *gc, uint32_t domid, uint32_t devid, libxl_nic_type type) { switch (type) { case LIBXL_NIC_TYPE_VIF: return GCSPRINTF(NETBACK_NIC_NAME, domid, devid); case LIBXL_NIC_TYPE_VIF_IOEMU: return GCSPRINTF(NETBACK_NIC_NAME TAP_DEVICE_SUFFIX, domid, devid); default: abort(); } } static int libxl_device_nic_compare(libxl_device_nic *d1, libxl_device_nic *d2) { return COMPARE_DEVID(d1, d2); } static void libxl_device_nic_update_config(libxl__gc *gc, void *d, void *s) { libxl__update_config_nic(gc, d, s); } int libxl__device_nic_set_devids(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid) { int ret = 0; int i; size_t last_devid = -1; for (i = 0; i < d_config->num_nics; i++) { /* We have to init the nic here, because we still haven't * called libxl_device_nic_add when domcreate_launch_dm gets called, * but qemu needs the nic information to be complete. */ ret = libxl__device_nic_setdefault(gc, &d_config->nics[i], domid, false); if (ret) { LOGD(ERROR, domid, "Unable to set nic defaults for nic %d", i); goto out; } if (d_config->nics[i].devid > last_devid) last_devid = d_config->nics[i].devid; } for (i = 0; i < d_config->num_nics; i++) { if (d_config->nics[i].devid < 0) d_config->nics[i].devid = ++last_devid; } out: return ret; } LIBXL_DEFINE_DEVICE_ADD(nic) LIBXL_DEFINE_DEVICES_ADD(nic) LIBXL_DEFINE_DEVICE_REMOVE(nic) DEFINE_DEVICE_TYPE_STRUCT(nic, .update_config = libxl_device_nic_update_config ); /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_disk.c0000664000175000017500000011745613256712137015235 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" #define BACKEND_STRING_SIZE 5 static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w, const char *wpath, const char *epath) { EGC_GC; libxl_evgen_disk_eject *evg = (void*)w; const char *backend; char *value; char backend_type[BACKEND_STRING_SIZE+1]; int rc; value = libxl__xs_read(gc, XBT_NULL, wpath); if (!value || strcmp(value, "eject")) return; if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) { LIBXL__EVENT_DISASTER(egc, "xs_write failed acknowledging eject", errno, LIBXL_EVENT_TYPE_DISK_EJECT); return; } libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user); libxl_device_disk *disk = &ev->u.disk_eject.disk; rc = libxl__xs_read_checked(gc, XBT_NULL, evg->be_ptr_path, &backend); if (rc) { LIBXL__EVENT_DISASTER(egc, "xs_read failed reading be_ptr_path", errno, LIBXL_EVENT_TYPE_DISK_EJECT); return; } if (!backend) { /* device has been removed, not simply ejected */ return; } sscanf(backend, "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE) "[a-z]/%*d/%*d", &disk->backend_domid, backend_type); if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) { disk->backend = LIBXL_DISK_BACKEND_TAP; } else if (!strcmp(backend_type, "qdisk")) { disk->backend = LIBXL_DISK_BACKEND_QDISK; } else { disk->backend = LIBXL_DISK_BACKEND_UNKNOWN; } disk->pdev_path = strdup(""); /* xxx fixme malloc failure */ disk->format = LIBXL_DISK_FORMAT_EMPTY; /* this value is returned to the user: do not free right away */ disk->vdev = libxl__strdup(NOGC, evg->vdev); disk->removable = 1; disk->readwrite = 0; disk->is_cdrom = 1; libxl__event_occurred(egc, ev); } int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid, const char *vdev, libxl_ev_user user, libxl_evgen_disk_eject **evgen_out) { GC_INIT(ctx); CTX_LOCK; int rc; char *path; libxl_evgen_disk_eject *evg = NULL; evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; } memset(evg, 0, sizeof(*evg)); evg->user = user; evg->domid = guest_domid; LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry); uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid); if (!domid) domid = guest_domid; int devid = libxl__device_disk_dev_number(vdev, NULL, NULL); path = GCSPRINTF("%s/device/vbd/%d/eject", libxl__xs_get_dompath(gc, domid), devid); if (!path) { rc = ERROR_NOMEM; goto out; } const char *libxl_path = GCSPRINTF("%s/device/vbd/%d", libxl__xs_libxl_path(gc, domid), devid); evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path); const char *configured_vdev; rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/dev", libxl_path), &configured_vdev); if (rc) goto out; evg->vdev = libxl__strdup(NOGC, configured_vdev); rc = libxl__ev_xswatch_register(gc, &evg->watch, disk_eject_xswatch_callback, path); if (rc) goto out; *evgen_out = evg; CTX_UNLOCK; GC_FREE; return 0; out: if (evg) libxl__evdisable_disk_eject(gc, evg); CTX_UNLOCK; GC_FREE; return rc; } void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) { CTX_LOCK; LIBXL_LIST_REMOVE(evg, entry); if (libxl__ev_xswatch_isregistered(&evg->watch)) libxl__ev_xswatch_deregister(gc, &evg->watch); free(evg->vdev); free(evg->be_ptr_path); free(evg); CTX_UNLOCK; } void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) { GC_INIT(ctx); libxl__evdisable_disk_eject(gc, evg); GC_FREE; } int libxl__device_disk_setdefault(libxl__gc *gc, libxl_device_disk *disk, uint32_t domid) { int rc; libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite); libxl_defbool_setdefault(&disk->colo_enable, false); libxl_defbool_setdefault(&disk->colo_restore_enable, false); rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid); if (rc < 0) return rc; /* Force Qdisk backend for CDROM devices of guests with a device model. */ if (disk->is_cdrom != 0 && libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM && libxl__device_model_version_running(gc, domid) != LIBXL_DEVICE_MODEL_VERSION_NONE) { if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK || disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) { LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk"); return ERROR_FAIL; } disk->backend = LIBXL_DISK_BACKEND_QDISK; } rc = libxl__device_disk_set_backend(gc, disk); return rc; } int libxl__device_from_disk(libxl__gc *gc, uint32_t domid, const libxl_device_disk *disk, libxl__device *device) { int devid; devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL); if (devid==-1) { LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s", disk->vdev); return ERROR_INVAL; } device->backend_domid = disk->backend_domid; device->backend_devid = devid; switch (disk->backend) { case LIBXL_DISK_BACKEND_PHY: device->backend_kind = LIBXL__DEVICE_KIND_VBD; break; case LIBXL_DISK_BACKEND_TAP: device->backend_kind = LIBXL__DEVICE_KIND_VBD; break; case LIBXL_DISK_BACKEND_QDISK: device->backend_kind = LIBXL__DEVICE_KIND_QDISK; break; default: LOGD(ERROR, domid, "Unrecognized disk backend type: %d", disk->backend); return ERROR_INVAL; } device->domid = domid; device->devid = devid; device->kind = LIBXL__DEVICE_KIND_VBD; return 0; } /* Specific function called directly only by local disk attach, * all other users should instead use the regular * libxl__device_disk_add wrapper * * The (optionally) passed function get_vdev will be used to * set the vdev the disk should be attached to. When it is set the caller * must also pass get_vdev_user, which will be passed to get_vdev. * * The passed get_vdev function is also in charge of printing * the corresponding error message when appropiate. */ static void device_disk_add(libxl__egc *egc, uint32_t domid, libxl_device_disk *disk, libxl__ao_device *aodev, char *get_vdev(libxl__gc *, void *, xs_transaction_t), void *get_vdev_user) { STATE_AO_GC(aodev->ao); flexarray_t *front = NULL; flexarray_t *back = NULL; char *dev = NULL, *script; libxl__device *device; int rc; libxl_ctx *ctx = gc->owner; xs_transaction_t t = XBT_NULL; libxl_domain_config d_config; libxl_device_disk disk_saved; libxl__domain_userdata_lock *lock = NULL; libxl_domain_config_init(&d_config); libxl_device_disk_init(&disk_saved); libxl_device_disk_copy(ctx, &disk_saved, disk); libxl_domain_type type = libxl__domain_type(gc, domid); if (type == LIBXL_DOMAIN_TYPE_INVALID) { rc = ERROR_FAIL; goto out; } /* * get_vdev != NULL -> local attach * get_vdev == NULL -> block attach * * We don't care about local attach state because it's only * intermediate state. */ if (!get_vdev && aodev->update_json) { lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc) goto out; DEVICE_ADD(disk, disks, domid, &disk_saved, COMPARE_DISK, &d_config); rc = libxl__dm_check_start(gc, &d_config, domid); if (rc) goto out; } for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; if (get_vdev) { assert(get_vdev_user); disk->vdev = get_vdev(gc, get_vdev_user, t); if (disk->vdev == NULL) { rc = ERROR_FAIL; goto out; } } rc = libxl__device_disk_setdefault(gc, disk, domid); if (rc) goto out; front = flexarray_make(gc, 16, 1); back = flexarray_make(gc, 16, 1); GCNEW(device); rc = libxl__device_from_disk(gc, domid, disk, device); if (rc != 0) { LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s", disk->vdev); goto out; } rc = libxl__device_exists(gc, t, device); if (rc < 0) goto out; if (rc == 1) { /* already exists in xenstore */ LOGD(ERROR, domid, "device already exists in xenstore"); aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */ rc = ERROR_DEVICE_EXISTS; goto out; } switch (disk->backend) { case LIBXL_DISK_BACKEND_PHY: dev = disk->pdev_path; do_backend_phy: flexarray_append(back, "params"); flexarray_append(back, dev); script = libxl__abs_path(gc, disk->script?: "block", libxl__xen_script_dir_path()); flexarray_append_pair(back, "script", script); assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD); break; case LIBXL_DISK_BACKEND_TAP: if (dev == NULL) { dev = libxl__blktap_devpath(gc, disk->pdev_path, disk->format); if (!dev) { LOGD(ERROR, domid, "Failed to get blktap devpath for %p", disk->pdev_path); rc = ERROR_FAIL; goto out; } } flexarray_append(back, "tapdisk-params"); flexarray_append(back, GCSPRINTF("%s:%s", libxl__device_disk_string_of_format(disk->format), disk->pdev_path)); /* tap backends with scripts are rejected by * libxl__device_disk_set_backend */ assert(!disk->script); /* now create a phy device to export the device to the guest */ goto do_backend_phy; case LIBXL_DISK_BACKEND_QDISK: flexarray_append(back, "params"); flexarray_append(back, GCSPRINTF("%s:%s", libxl__device_disk_string_of_format(disk->format), disk->pdev_path ? : "")); if (libxl_defbool_val(disk->colo_enable)) { flexarray_append(back, "colo-host"); flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host)); flexarray_append(back, "colo-port"); flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port)); flexarray_append(back, "colo-export"); flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export)); flexarray_append(back, "active-disk"); flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk)); flexarray_append(back, "hidden-disk"); flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk)); } assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK); break; default: LOGD(ERROR, domid, "Unrecognized disk backend type: %d", disk->backend); rc = ERROR_INVAL; goto out; } flexarray_append(back, "frontend-id"); flexarray_append(back, GCSPRINTF("%d", domid)); flexarray_append(back, "online"); flexarray_append(back, "1"); flexarray_append(back, "removable"); flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0)); flexarray_append(back, "bootable"); flexarray_append(back, GCSPRINTF("%d", 1)); flexarray_append(back, "state"); flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append(back, "dev"); flexarray_append(back, disk->vdev); flexarray_append(back, "type"); flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend)); flexarray_append(back, "mode"); flexarray_append(back, disk->readwrite ? "w" : "r"); flexarray_append(back, "device-type"); flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk"); if (disk->direct_io_safe) { flexarray_append(back, "direct-io-safe"); flexarray_append(back, "1"); } flexarray_append_pair(back, "discard-enable", libxl_defbool_val(disk->discard_enable) ? "1" : "0"); flexarray_append(front, "backend-id"); flexarray_append(front, GCSPRINTF("%d", disk->backend_domid)); flexarray_append(front, "state"); flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append(front, "virtual-device"); flexarray_append(front, GCSPRINTF("%d", device->devid)); flexarray_append(front, "device-type"); flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk"); /* * Old PV kernel disk frontends before 2.6.26 rely on tool stack to * write disk native protocol to frontend node. Xend does this, port * this behaviour to xl. * * New kernels write this node themselves. In that case it just * overwrites an existing node which is OK. */ if (type == LIBXL_DOMAIN_TYPE_PV) { const char *protocol = xc_domain_get_native_protocol(ctx->xch, domid); if (protocol) { flexarray_append(front, "protocol"); flexarray_append(front, libxl__strdup(gc, protocol)); } } if (!get_vdev && aodev->update_json) { rc = libxl__set_domain_configuration(gc, domid, &d_config); if (rc) goto out; } libxl__device_generic_add(gc, t, device, libxl__xs_kvs_of_flexarray(gc, back), libxl__xs_kvs_of_flexarray(gc, front), NULL); rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } aodev->dev = device; aodev->action = LIBXL__DEVICE_ACTION_ADD; libxl__wait_device_connection(egc, aodev); rc = 0; out: libxl__xs_transaction_abort(gc, &t); if (lock) libxl__unlock_domain_userdata(lock); libxl_device_disk_dispose(&disk_saved); libxl_domain_config_dispose(&d_config); aodev->rc = rc; if (rc) aodev->callback(egc, aodev); return; } static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid, libxl_device_disk *disk, libxl__ao_device *aodev) { device_disk_add(egc, domid, disk, aodev, NULL, NULL); } static int libxl__device_disk_from_xenstore(libxl__gc *gc, const char *libxl_path, libxl_device_disk *disk) { libxl_ctx *ctx = libxl__gc_owner(gc); unsigned int len; char *tmp; int rc; libxl_device_disk_init(disk); const char *backend_path; rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/backend", libxl_path), &backend_path); if (rc) goto out; if (!backend_path) { LOG(ERROR, "disk %s does not exist (no backend path", libxl_path); rc = ERROR_FAIL; goto out; } rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid); if (rc) { LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path); goto out; } /* * "params" may not be present; but everything else must be. * colo releated entries(colo-host, colo-port, colo-export, * active-disk and hidden-disk) are present only if colo is * enabled. */ tmp = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/params", libxl_path), &len); if (tmp && strchr(tmp, ':')) { disk->pdev_path = strdup(strchr(tmp, ':') + 1); free(tmp); } else { disk->pdev_path = tmp; } tmp = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/colo-host", libxl_path), &len); if (tmp) { libxl_defbool_set(&disk->colo_enable, true); disk->colo_host = tmp; tmp = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/colo-port", libxl_path), &len); if (!tmp) { LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path); goto cleanup; } disk->colo_port = atoi(tmp); #define XS_READ_COLO(param, item) do { \ tmp = xs_read(ctx->xsh, XBT_NULL, \ GCSPRINTF("%s/"#param"", libxl_path), &len); \ if (!tmp) { \ LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path); \ goto cleanup; \ } \ disk->item = tmp; \ } while (0) XS_READ_COLO(colo-export, colo_export); XS_READ_COLO(active-disk, active_disk); XS_READ_COLO(hidden-disk, hidden_disk); #undef XS_READ_COLO } else { libxl_defbool_set(&disk->colo_enable, false); } tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/type", libxl_path)); if (!tmp) { LOG(ERROR, "Missing xenstore node %s/type", libxl_path); goto cleanup; } libxl_string_to_backend(ctx, tmp, &(disk->backend)); disk->vdev = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/dev", libxl_path), &len); if (!disk->vdev) { LOG(ERROR, "Missing xenstore node %s/dev", libxl_path); goto cleanup; } tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf (gc, "%s/removable", libxl_path)); if (!tmp) { LOG(ERROR, "Missing xenstore node %s/removable", libxl_path); goto cleanup; } disk->removable = atoi(tmp); tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path)); if (!tmp) { LOG(ERROR, "Missing xenstore node %s/mode", libxl_path); goto cleanup; } if (!strcmp(tmp, "w")) disk->readwrite = 1; else disk->readwrite = 0; tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/device-type", libxl_path)); if (!tmp) { LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path); goto cleanup; } disk->is_cdrom = !strcmp(tmp, "cdrom"); disk->format = LIBXL_DISK_FORMAT_UNKNOWN; return 0; cleanup: rc = ERROR_FAIL; out: libxl_device_disk_dispose(disk); return rc; } int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid, const char *vdev, libxl_device_disk *disk) { GC_INIT(ctx); char *dom_xl_path, *libxl_path; int devid = libxl__device_disk_dev_number(vdev, NULL, NULL); int rc = ERROR_FAIL; if (devid < 0) return ERROR_INVAL; libxl_device_disk_init(disk); dom_xl_path = libxl__xs_libxl_path(gc, domid); if (!dom_xl_path) { goto out; } libxl_path = GCSPRINTF("%s/device/vbd/%d", dom_xl_path, devid); rc = libxl__device_disk_from_xenstore(gc, libxl_path, disk); out: GC_FREE; return rc; } static int libxl__append_disk_list(libxl__gc *gc, uint32_t domid, libxl_device_disk **disks, int *ndisks) { char *libxl_dir_path = NULL; char **dir = NULL; unsigned int n = 0; libxl_device_disk *pdisk = NULL, *pdisk_end = NULL; int rc=0; int initial_disks = *ndisks; libxl_dir_path = GCSPRINTF("%s/device/vbd", libxl__xs_libxl_path(gc, domid)); dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n); if (dir && n) { libxl_device_disk *tmp; tmp = realloc(*disks, sizeof (libxl_device_disk) * (*ndisks + n)); if (tmp == NULL) return ERROR_NOMEM; *disks = tmp; pdisk = *disks + initial_disks; pdisk_end = *disks + initial_disks + n; for (; pdisk < pdisk_end; pdisk++, dir++) { const char *p; p = GCSPRINTF("%s/%s", libxl_dir_path, *dir); if ((rc=libxl__device_disk_from_xenstore(gc, p, pdisk))) goto out; *ndisks += 1; } } out: return rc; } libxl_device_disk *libxl_device_disk_list(libxl_ctx *ctx, uint32_t domid, int *num) { GC_INIT(ctx); libxl_device_disk *disks = NULL; int rc; *num = 0; rc = libxl__append_disk_list(gc, domid, &disks, num); if (rc) goto out_err; GC_FREE; return disks; out_err: LOG(ERROR, "Unable to list disks"); while (disks && *num) { (*num)--; libxl_device_disk_dispose(&disks[*num]); } free(disks); return NULL; } int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, libxl_diskinfo *diskinfo) { GC_INIT(ctx); char *dompath, *fe_path, *libxl_path; char *val; int rc; diskinfo->backend = NULL; dompath = libxl__xs_get_dompath(gc, domid); diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL); /* tap devices entries in xenstore are written as vbd devices. */ fe_path = GCSPRINTF("%s/device/vbd/%d", dompath, diskinfo->devid); libxl_path = GCSPRINTF("%s/device/vbd/%d", libxl__xs_libxl_path(gc, domid), diskinfo->devid); diskinfo->backend = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/backend", libxl_path), NULL); if (!diskinfo->backend) { GC_FREE; return ERROR_FAIL; } rc = libxl__backendpath_parse_domid(gc, diskinfo->backend, &diskinfo->backend_id); if (rc) goto out; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path)); diskinfo->state = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path)); diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path)); diskinfo->rref = val ? strtoul(val, NULL, 10) : -1; diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/frontend", libxl_path), NULL); diskinfo->frontend_id = domid; GC_FREE; return 0; out: free(diskinfo->backend); return rc; } int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int num = 0, i; libxl_device_disk *disks = NULL, disk_saved, disk_empty; libxl_domain_config d_config; int rc, dm_ver; libxl__device device; const char *be_path, *libxl_path; char * tmp; libxl__domain_userdata_lock *lock = NULL; xs_transaction_t t = XBT_NULL; flexarray_t *insert = NULL, *empty = NULL; libxl_domain_config_init(&d_config); libxl_device_disk_init(&disk_empty); libxl_device_disk_init(&disk_saved); libxl_device_disk_copy(ctx, &disk_saved, disk); disk_empty.format = LIBXL_DISK_FORMAT_EMPTY; disk_empty.vdev = libxl__strdup(NOGC, disk->vdev); disk_empty.pdev_path = libxl__strdup(NOGC, ""); disk_empty.is_cdrom = 1; libxl__device_disk_setdefault(gc, &disk_empty, domid); libxl_domain_type type = libxl__domain_type(gc, domid); if (type == LIBXL_DOMAIN_TYPE_INVALID) { rc = ERROR_FAIL; goto out; } if (type != LIBXL_DOMAIN_TYPE_HVM) { LOGD(ERROR, domid, "cdrom-insert requires an HVM domain"); rc = ERROR_INVAL; goto out; } if (libxl_get_stubdom_id(ctx, domid) != 0) { LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains"); rc = ERROR_INVAL; goto out; } dm_ver = libxl__device_model_version_running(gc, domid); if (dm_ver == -1) { LOGD(ERROR, domid, "Cannot determine device model version"); rc = ERROR_FAIL; goto out; } if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_NONE) { LOGD(ERROR, domid, "Guests without a device model cannot use cd-insert"); rc = ERROR_FAIL; goto out; } disks = libxl_device_disk_list(ctx, domid, &num); for (i = 0; i < num; i++) { if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev)) { /* Found. Set backend type appropriately. */ disk->backend=disks[i].backend; break; } } if (i == num) { LOGD(ERROR, domid, "Virtual device not found"); rc = ERROR_FAIL; goto out; } rc = libxl__device_disk_setdefault(gc, disk, domid); if (rc) goto out; if (!disk->pdev_path) { disk->pdev_path = libxl__strdup(NOGC, ""); disk->format = LIBXL_DISK_FORMAT_EMPTY; } rc = libxl__device_from_disk(gc, domid, disk, &device); if (rc) goto out; be_path = libxl__device_backend_path(gc, &device); libxl_path = libxl__device_libxl_path(gc, &device); insert = flexarray_make(gc, 4, 1); flexarray_append_pair(insert, "type", libxl__device_disk_string_of_backend(disk->backend)); if (disk->format != LIBXL_DISK_FORMAT_EMPTY) flexarray_append_pair(insert, "params", GCSPRINTF("%s:%s", libxl__device_disk_string_of_format(disk->format), disk->pdev_path)); else flexarray_append_pair(insert, "params", ""); empty = flexarray_make(gc, 4, 1); flexarray_append_pair(empty, "type", libxl__device_disk_string_of_backend(disk->backend)); flexarray_append_pair(empty, "params", ""); /* Note: CTX lock is already held at this point so lock hierarchy * is maintained. */ lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } /* We need to eject the original image first. This is implemented * by inserting empty media. JSON is not updated. */ if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { rc = libxl__qmp_insert_cdrom(gc, domid, &disk_empty); if (rc) goto out; } for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; /* Sanity check: make sure the device exists before writing here */ tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path)); if (!tmp) { LOGD(ERROR, domid, "Internal error: %s does not exist", GCSPRINTF("%s/frontend", libxl_path)); rc = ERROR_FAIL; goto out; } char **kvs = libxl__xs_kvs_of_flexarray(gc, empty); rc = libxl__xs_writev(gc, t, be_path, kvs); if (rc) goto out; rc = libxl__xs_writev(gc, t, libxl_path, kvs); if (rc) goto out; rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc) goto out; DEVICE_ADD(disk, disks, domid, &disk_saved, COMPARE_DISK, &d_config); rc = libxl__dm_check_start(gc, &d_config, domid); if (rc) goto out; if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { rc = libxl__qmp_insert_cdrom(gc, domid, disk); if (rc) goto out; } for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; /* Sanity check: make sure the device exists before writing here */ tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path)); if (!tmp) { LOGD(ERROR, domid, "Internal error: %s does not exist", GCSPRINTF("%s/frontend", libxl_path)); rc = ERROR_FAIL; goto out; } rc = libxl__set_domain_configuration(gc, domid, &d_config); if (rc) goto out; char **kvs = libxl__xs_kvs_of_flexarray(gc, insert); rc = libxl__xs_writev(gc, t, be_path, kvs); if (rc) goto out; rc = libxl__xs_writev(gc, t, libxl_path, kvs); if (rc) goto out; rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } /* success, no actual async */ libxl__ao_complete(egc, ao, 0); rc = 0; out: libxl__xs_transaction_abort(gc, &t); for (i = 0; i < num; i++) libxl_device_disk_dispose(&disks[i]); free(disks); libxl_device_disk_dispose(&disk_empty); libxl_device_disk_dispose(&disk_saved); libxl_domain_config_dispose(&d_config); if (lock) libxl__unlock_domain_userdata(lock); if (rc) return AO_CREATE_FAIL(rc); return AO_INPROGRESS; } /* libxl__alloc_vdev only works on the local domain, that is the domain * where the toolstack is running */ static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user, xs_transaction_t t) { const char *blkdev_start = (const char *) get_vdev_user; int devid = 0, disk = 0, part = 0; char *libxl_dom_path = libxl__xs_libxl_path(gc, LIBXL_TOOLSTACK_DOMID); libxl__device_disk_dev_number(blkdev_start, &disk, &part); if (part != 0) { LOG(ERROR, "blkdev_start is invalid"); return NULL; } do { devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk), NULL, NULL); if (devid < 0) return NULL; if (libxl__xs_read(gc, t, GCSPRINTF("%s/device/vbd/%d/backend", libxl_dom_path, devid)) == NULL) { if (errno == ENOENT) return libxl__devid_to_vdev(gc, devid); else return NULL; } disk++; } while (1); return NULL; } /* Callbacks */ char *libxl__device_disk_find_local_path(libxl__gc *gc, libxl_domid guest_domid, const libxl_device_disk *disk, bool qdisk_direct) { char *path = NULL; /* No local paths for driver domains */ if (disk->backend_domname != NULL) { LOG(DEBUG, "Non-local backend, can't access locally.\n"); goto out; } /* * If this is in raw format, and we're not using a script or a * driver domain, we can access the target path directly. */ if (disk->format == LIBXL_DISK_FORMAT_RAW && disk->script == NULL) { path = libxl__strdup(gc, disk->pdev_path); LOG(DEBUG, "Directly accessing local RAW disk %s", path); goto out; } /* * If we're being called for a qemu path, we can pass the target * string directly as well */ if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) { path = libxl__strdup(gc, disk->pdev_path); LOG(DEBUG, "Directly accessing local QDISK target %s", path); goto out; } /* * If the format isn't raw and / or we're using a script, then see * if the script has written a path to the "cooked" node */ if (disk->script && guest_domid != INVALID_DOMID) { libxl__device device; char *be_path, *pdpath; int rc; LOGD(DEBUG, guest_domid, "Run from a script; checking for physical-device-path (vdev %s)", disk->vdev); rc = libxl__device_from_disk(gc, guest_domid, disk, &device); if (rc < 0) goto out; be_path = libxl__device_backend_path(gc, &device); pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path); LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath); path = libxl__xs_read(gc, XBT_NULL, pdpath); if (path) LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path); else LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally."); goto out; } out: return path; } static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev); void libxl__device_disk_local_initiate_attach(libxl__egc *egc, libxl__disk_local_state *dls) { STATE_AO_GC(dls->ao); int rc; const libxl_device_disk *in_disk = dls->in_disk; libxl_device_disk *disk = &dls->disk; const char *blkdev_start = dls->blkdev_start; assert(in_disk->pdev_path); disk->vdev = NULL; if (dls->diskpath) LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath); LOG(DEBUG, "Trying to find local path"); dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID, in_disk, false); if (dls->diskpath) { LOG(DEBUG, "Local path found, executing callback."); dls->callback(egc, dls, 0); } else { LOG(DEBUG, "Local path not found, initiating attach."); memcpy(disk, in_disk, sizeof(libxl_device_disk)); disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path); if (in_disk->script != NULL) disk->script = libxl__strdup(gc, in_disk->script); disk->vdev = NULL; rc = libxl__device_disk_setdefault(gc, disk, LIBXL_TOOLSTACK_DOMID); if (rc) goto out; libxl__prepare_ao_device(ao, &dls->aodev); dls->aodev.callback = local_device_attach_cb; device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev, libxl__alloc_vdev, (void *) blkdev_start); } return; out: assert(rc); dls->rc = rc; libxl__device_disk_local_initiate_detach(egc, dls); dls->callback(egc, dls, rc); } static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev); char *be_path = NULL; int rc; libxl__device device; libxl_device_disk *disk = &dls->disk; rc = aodev->rc; if (rc) { LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path); goto out; } rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device); if (rc < 0) goto out; be_path = libxl__device_backend_path(gc, &device); rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected)); if (rc < 0) goto out; dls->diskpath = GCSPRINTF("/dev/%s", libxl__devid_to_localdev(gc, device.devid)); LOG(DEBUG, "locally attached disk %s", dls->diskpath); dls->callback(egc, dls, 0); return; out: assert(rc); dls->rc = rc; libxl__device_disk_local_initiate_detach(egc, dls); return; } /* Callbacks for local detach */ static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev); void libxl__device_disk_local_initiate_detach(libxl__egc *egc, libxl__disk_local_state *dls) { STATE_AO_GC(dls->ao); int rc = 0; libxl_device_disk *disk = &dls->disk; libxl__device *device; libxl__ao_device *aodev = &dls->aodev; libxl__prepare_ao_device(ao, aodev); if (!dls->diskpath) goto out; if (disk->vdev != NULL) { GCNEW(device); rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, device); if (rc != 0) goto out; aodev->action = LIBXL__DEVICE_ACTION_REMOVE; aodev->dev = device; aodev->callback = local_device_detach_cb; aodev->force = 0; libxl__initiate_device_generic_remove(egc, aodev); return; } out: aodev->rc = rc; local_device_detach_cb(egc, aodev); return; } static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev); int rc; if (aodev->rc) { LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u", libxl__device_action_to_string(aodev->action), libxl__device_kind_to_string(aodev->dev->kind), aodev->dev->devid); goto out; } out: /* * If there was an error in dls->rc, it means we have been called from * a failed execution of libxl__device_disk_local_initiate_attach, * so return the original error. */ rc = dls->rc ? dls->rc : aodev->rc; dls->callback(egc, dls, rc); return; } /* The following functions are defined: * libxl_device_disk_add * libxl__add_disks * libxl_device_disk_remove * libxl_device_disk_destroy */ LIBXL_DEFINE_DEVICE_ADD(disk) LIBXL_DEFINE_DEVICES_ADD(disk) LIBXL_DEFINE_DEVICE_REMOVE(disk) static int libxl_device_disk_compare(libxl_device_disk *d1, libxl_device_disk *d2) { return COMPARE_DISK(d1, d2); } /* Take care of removable device. We maintain invariant in the * insert / remove operation so that: * 1. if xenstore is "empty" while JSON is not, the result * is "empty" * 2. if xenstore has a different media than JSON, use the * one in JSON * 3. if xenstore and JSON have the same media, well, you * know the answer :-) * * Currently there is only one removable device -- CDROM. * Look for libxl_cdrom_insert for reference. */ static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2) { GC_INIT(ctx); libxl_device_disk *src = d1; libxl_device_disk *dst = d2; if (src->removable) { if (!src->pdev_path || *src->pdev_path == '\0') { /* 1, no media in drive */ free(dst->pdev_path); dst->pdev_path = libxl__strdup(NOGC, ""); dst->format = LIBXL_DISK_FORMAT_EMPTY; } else { /* 2 and 3, use JSON, no need to touch anything */ ; } } } static int libxl_device_disk_dm_needed(void *e, unsigned domid) { libxl_device_disk *elem = e; return elem->backend == LIBXL_DISK_BACKEND_QDISK && elem->backend_domid == domid; } DEFINE_DEVICE_TYPE_STRUCT(disk, .merge = libxl_device_disk_merge, .dm_needed = libxl_device_disk_dm_needed, .skip_attach = 1 ); /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_convert_callout.c0000664000175000017500000001210413256712137017466 0ustar smbsmb/* * Copyright (C) 2014 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" /* * Infrastructure for converting a legacy migration stream into a * libxl v2 stream. * * This is done by fork()ing the python conversion script, which takes * in a legacy stream, and puts out a suitably-formatted v2 stream. */ static void helper_exited(libxl__egc *egc, libxl__ev_child *ch, pid_t pid, int status); static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc); static void helper_done(libxl__egc *egc, libxl__conversion_helper_state *chs); /*----- Entrypoints -----*/ void libxl__conversion_helper_init(libxl__conversion_helper_state *chs) { assert(chs->ao); chs->v2_carefd = NULL; chs->rc = 0; libxl__ao_abortable_init(&chs->abrt); libxl__ev_child_init(&chs->child); } int libxl__convert_legacy_stream(libxl__egc *egc, libxl__conversion_helper_state *chs) { STATE_AO_GC(chs->ao); libxl__carefd *child_in = NULL, *child_out = NULL; int rc = 0; chs->abrt.ao = chs->ao; chs->abrt.callback = helper_stop; rc = libxl__ao_abortable_register(&chs->abrt); if (rc) goto err; libxl__carefd_begin(); int fds[2]; if (libxl_pipe(CTX, fds)) { rc = ERROR_FAIL; libxl__carefd_unlock(); goto err; } child_out = libxl__carefd_record(CTX, fds[0]); child_in = libxl__carefd_record(CTX, fds[1]); libxl__carefd_unlock(); pid_t pid = libxl__ev_child_fork(gc, &chs->child, helper_exited); if (!pid) { char * const args[] = { getenv("LIBXL_CONVERT_HELPER") ?: LIBEXEC_BIN "/convert-legacy-stream", "--in", GCSPRINTF("%d", chs->legacy_fd), "--out", GCSPRINTF("%d", fds[1]), /* * The width calculation is an assumption for the common * case. The conversion script needs to know the width of * the toolstack which saved the legacy stream. * * In the overwhelming majority of cases, the width of the * saving toolstack will be the same as our current * width. To avoid extending the libxl API with a * parameter intended to disappear shortly, this option * has not been exposed to the caller. * * If more complicated conversion is required, the * conversion script can be instantiated manually, which * will bypass all of this conversion logic. */ "--width", sizeof(unsigned long) == 8 ? "64" : "32", "--guest", chs->hvm ? "hvm" : "pv", "--format", "libxl", /* "--verbose", */ NULL, }; libxl_fd_set_cloexec(CTX, chs->legacy_fd, 0); libxl_fd_set_cloexec(CTX, libxl__carefd_fd(child_in), 0); libxl__exec(gc, -1, -1, -1, args[0], args, NULL); } libxl__carefd_close(child_in); chs->v2_carefd = child_out; assert(!rc); return rc; err: libxl__ao_abortable_deregister(&chs->abrt); assert(rc); return rc; } void libxl__conversion_helper_abort(libxl__egc *egc, libxl__conversion_helper_state *chs, int rc) { STATE_AO_GC(chs->ao); assert(rc); if (libxl__conversion_helper_inuse(chs)) { if (!chs->rc) chs->rc = rc; libxl__kill(gc, chs->child.pid, SIGTERM, "conversion helper"); } } /*----- State handling -----*/ static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc) { libxl__conversion_helper_state *chs = CONTAINER_OF(abrt, *chs, abrt); STATE_AO_GC(chs->ao); libxl__conversion_helper_abort(egc, chs, rc); } static void helper_exited(libxl__egc *egc, libxl__ev_child *ch, pid_t pid, int status) { libxl__conversion_helper_state *chs = CONTAINER_OF(ch, *chs, child); STATE_AO_GC(chs->ao); if (status) { libxl_report_child_exitstatus( CTX, chs->rc ? XTL_DEBUG : XTL_ERROR, "conversion helper", pid, status); if (!chs->rc) chs->rc = ERROR_FAIL; } helper_done(egc, chs); } static void helper_done(libxl__egc *egc, libxl__conversion_helper_state *chs) { STATE_AO_GC(chs->ao); assert(!libxl__conversion_helper_inuse(chs)); libxl__ao_abortable_deregister(&chs->abrt); chs->completion_callback(egc, chs, chs->rc); } xen-4.9.2/tools/libxl/libxl_save_msgs_gen.pl0000775000175000017500000002713513256712137017311 0ustar smbsmb#!/usr/bin/perl -w use warnings; use strict; use POSIX; our $debug = 0; # produce copious debugging output at run-time? our @msgs = ( # flags: # s - applicable to save # r - applicable to restore # c - function pointer in callbacks struct rather than fixed function # x - function pointer is in struct {save,restore}_callbacks # and its null-ness needs to be passed through to the helper's xc # W - needs a return value; callback is synchronous # A - needs a return value; callback is asynchronous [ 1, 'sr', "log", [qw(uint32_t level uint32_t errnoval STRING context STRING formatted)] ], [ 2, 'sr', "progress", [qw(STRING context STRING doing_what), 'unsigned long', 'done', 'unsigned long', 'total'] ], [ 3, 'srcxA', "suspend", [] ], [ 4, 'srcxA', "postcopy", [] ], [ 5, 'srcxA', "checkpoint", [] ], [ 6, 'srcxA', "wait_checkpoint", [] ], [ 7, 'scxA', "switch_qemu_logdirty", [qw(int domid unsigned enable)] ], [ 8, 'rcx', "restore_results", ['xen_pfn_t', 'store_gfn', 'xen_pfn_t', 'console_gfn'] ], [ 9, 'srW', "complete", [qw(int retval int errnoval)] ], ); #---------------------------------------- our %cbs; our %func; our %func_ah; our @outfuncs; our %out_decls; our %out_body; our %msgnum_used; die unless @ARGV==1; die if $ARGV[0] =~ m/^-/; our ($intendedout) = @ARGV; $intendedout =~ m/([a-z]+)\.([ch])$/ or die; my ($want_ah, $ch) = ($1, $2); my $declprefix = ''; foreach my $ah (qw(callout helper)) { $out_body{$ah} .= < #include #include #include END_BOTH #include "libxl_internal.h" END_CALLOUT #include #include #include "_libxl_save_msgs_${ah}.h" END_HELPER } die $want_ah unless defined $out_body{$want_ah}; sub f_decl ($$$$) { my ($name, $ah, $c_rtype, $c_decl) = @_; $out_decls{$name} = "${declprefix}$c_rtype $name$c_decl;\n"; $func{$name} = "$c_rtype $name$c_decl\n{\n" . ($func{$name} || ''); $func_ah{$name} = $ah; } sub f_more ($$) { my ($name, $addbody) = @_; $func{$name} ||= ''; $func{$name} .= $addbody; push @outfuncs, $name; } our $libxl = "libxl__srm"; our $callback = "${libxl}_callout_callback"; our $receiveds = "${libxl}_callout_received"; our $sendreply = "${libxl}_callout_sendreply"; our $getcallbacks = "${libxl}_callout_get_callbacks"; our $enumcallbacks = "${libxl}_callout_enumcallbacks"; sub cbtype ($) { "${libxl}_".$_[0]."_autogen_callbacks"; }; f_decl($sendreply, 'callout', 'void', "(int r, void *user)"); our $helper = "helper"; our $encode = "${helper}_stub"; our $allocbuf = "${helper}_allocbuf"; our $transmit = "${helper}_transmitmsg"; our $getreply = "${helper}_getreply"; our $setcallbacks = "${helper}_setcallbacks"; f_decl($allocbuf, 'helper', 'unsigned char *', '(int len, void *user)'); f_decl($transmit, 'helper', 'void', '(unsigned char *msg_freed, int len, void *user)'); f_decl($getreply, 'helper', 'int', '(void *user)'); sub typeid ($) { my ($t) = @_; $t =~ s/\W/_/; return $t; }; $out_body{'callout'} .= <($sr); f_more("${fnamebase}_${sr}", $contents); } }; $f_more_sr->(" case $msgnum: { /* $name */\n"); if ($flags =~ m/W/) { $f_more_sr->(" int r;\n"); } my $c_rtype_helper = $flags =~ m/[WA]/ ? 'int' : 'void'; my $c_rtype_callout = $flags =~ m/W/ ? 'int' : 'void'; my $c_decl = '('; my $c_callback_args = ''; f_more("${encode}_$name", <(" const char *$arg;\n"); } elsif ($argtype eq 'BLOCK') { $c_decl .= "const uint8_t *$arg, uint32_t ${arg}_size, "; $c_args .= ", ${arg}_size"; $c_get_args .= ",&${arg}_size"; $f_more_sr->(" const uint8_t *$arg;\n". " uint32_t ${arg}_size;\n"); } else { $c_decl .= "$argtype $arg, "; $f_more_sr->(" $argtype $arg;\n"); } $c_callback_args .= "$c_args, "; $c_recv.= " if (!${typeid}_get(&msg,endmsg,$c_get_args)) return 0;\n"; f_more("${encode}_$name", " ${typeid}_put(buf, &len, $c_args);\n"); } $f_more_sr->($c_recv); $c_decl .= "void *user)"; $c_callback_args .= "user"; $f_more_sr->(" if (msg != endmsg) return 0;\n"); my $c_callback; if ($flags !~ m/c/) { $c_callback = "${callback}_$name"; } else { $f_more_sr->(sub { my ($sr) = @_; $cbs{$sr} .= " $c_rtype_callout (*${name})$c_decl;\n"; return " const ".cbtype($sr)." *const cbs =\n". " ${getcallbacks}_${sr}(user);\n"; }); $c_callback = "cbs->${name}"; } my $c_make_callback = "$c_callback($c_callback_args)"; if ($flags !~ m/W/) { $f_more_sr->(" $c_make_callback;\n"); } else { $f_more_sr->(" r = $c_make_callback;\n". " $sendreply(r, user);\n"); f_decl($sendreply, 'callout', 'void', '(int r, void *user)'); } if ($flags =~ m/x/) { my $c_v = "(1u<<$msgnum)"; my $c_cb = "cbs->$name"; $f_more_sr->(" if ($c_cb) cbflags |= $c_v;\n", $enumcallbacks); $f_more_sr->(" $c_cb = (cbflags & $c_v) ? ${encode}_${name} : 0;\n", $setcallbacks); } $f_more_sr->(" return 1;\n }\n\n"); f_decl("${callback}_$name", 'callout', $c_rtype_callout, $c_decl); f_decl("${encode}_$name", 'helper', $c_rtype_helper, $c_decl); f_more("${encode}_$name", " if (buf) break; buf = ${helper}_allocbuf(len, user); assert(buf); allocd = len; len = 0; } assert(len == allocd); ${transmit}(buf, len, user); "); if ($flags =~ m/[WA]/) { f_more("${encode}_$name", (<length); break; default: return 3; } } if (resstr == NULL) { resstr = strdup("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); } /* the family and model entry is potentially split up across * two fields in Fn0000_0001_EAX, so handle them here separately. */ if (!strncmp(str, "family", sep - str)) { if (num < 16) { memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4); memcpy(resstr + (32 - 8) - 20, "00000000", 8); } else { num -= 15; memcpy(resstr + (32 - 4) - flag->bit, "1111", 4); for (i = 0; i < 7; i++) { flags[7 - i] = "01"[num & 1]; num >>= 1; } memcpy(resstr + (32 - 8) - 20, flags, 8); } } else if (!strncmp(str, "model", sep - str)) { memcpy(resstr + (32 - 4) - 16, flags, 4); memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4); } else { memcpy(resstr + (32 - flag->length) - flag->bit, flags, flag->length); } entry->policy[flag->reg - 1] = resstr; return 0; } /* parse a single list item from the legacy Python xend syntax, where * the strings for each register were directly exposed to the user. * Used for maintaining compatibility with older config files */ int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *cpuid, const char* str) { char *endptr; unsigned long value; uint32_t leaf, subleaf = XEN_CPUID_INPUT_UNUSED; struct libxl__cpuid_policy *entry; /* parse the leaf number */ value = strtoul(str, &endptr, 0); if (str == endptr) { return 1; } leaf = value; /* check for an optional subleaf number */ if (*endptr == ',') { str = endptr + 1; value = strtoul(str, &endptr, 0); if (str == endptr) { return 2; } subleaf = value; } if (*endptr != ':') { return 3; } str = endptr + 1; entry = cpuid_find_match(cpuid, leaf, subleaf); for (str = endptr + 1; *str != 0;) { if (str[0] != 'e' || str[2] != 'x') { return 4; } value = str[1] - 'a'; endptr = strchr(str, '='); if (value > 3 || endptr == NULL) { return 4; } str = endptr + 1; endptr = strchr(str, ','); if (endptr == NULL) { endptr = strchr(str, 0); } if (endptr - str != 32) { return 5; } entry->policy[value] = calloc(32 + 1, 1); strncpy(entry->policy[value], str, 32); entry->policy[value][32] = 0; if (*endptr == 0) { break; } for (str = endptr + 1; *str == ' ' || *str == '\n'; str++); } return 0; } void libxl_cpuid_apply_policy(libxl_ctx *ctx, uint32_t domid) { xc_cpuid_apply_policy(ctx->xch, domid, NULL, 0); } void libxl_cpuid_set(libxl_ctx *ctx, uint32_t domid, libxl_cpuid_policy_list cpuid) { int i; char *cpuid_res[4]; for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) xc_cpuid_set(ctx->xch, domid, cpuid[i].input, (const char**)(cpuid[i].policy), cpuid_res); } static const char *input_names[2] = { "leaf", "subleaf" }; static const char *policy_names[4] = { "eax", "ebx", "ecx", "edx" }; /* * Aiming for: * [ * { 'leaf': 'val-eax', * 'subleaf': 'val-ecx', * 'eax': 'filter', * 'ebx': 'filter', * 'ecx': 'filter', * 'edx': 'filter' }, * { 'leaf': 'val-eax', ..., 'eax': 'filter', ... }, * ... etc ... * ] */ yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand, libxl_cpuid_policy_list *pcpuid) { libxl_cpuid_policy_list cpuid = *pcpuid; yajl_gen_status s; int i, j; s = yajl_gen_array_open(hand); if (s != yajl_gen_status_ok) goto out; if (cpuid == NULL) goto empty; for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) { s = yajl_gen_map_open(hand); if (s != yajl_gen_status_ok) goto out; for (j = 0; j < 2; j++) { if (cpuid[i].input[j] != XEN_CPUID_INPUT_UNUSED) { s = libxl__yajl_gen_asciiz(hand, input_names[j]); if (s != yajl_gen_status_ok) goto out; s = yajl_gen_integer(hand, cpuid[i].input[j]); if (s != yajl_gen_status_ok) goto out; } } for (j = 0; j < 4; j++) { if (cpuid[i].policy[j] != NULL) { s = libxl__yajl_gen_asciiz(hand, policy_names[j]); if (s != yajl_gen_status_ok) goto out; s = yajl_gen_string(hand, (const unsigned char *)cpuid[i].policy[j], 32); if (s != yajl_gen_status_ok) goto out; } } s = yajl_gen_map_close(hand); if (s != yajl_gen_status_ok) goto out; } empty: s = yajl_gen_array_close(hand); out: return s; } int libxl__cpuid_policy_list_parse_json(libxl__gc *gc, const libxl__json_object *o, libxl_cpuid_policy_list *p) { int i, size; libxl_cpuid_policy_list l; flexarray_t *array; if (!libxl__json_object_is_array(o)) return ERROR_FAIL; array = libxl__json_object_get_array(o); if (!array->count) return 0; size = array->count; /* need one extra slot as sentinel */ l = *p = libxl__calloc(NOGC, size + 1, sizeof(libxl_cpuid_policy)); l[size].input[0] = XEN_CPUID_INPUT_UNUSED; l[size].input[1] = XEN_CPUID_INPUT_UNUSED; for (i = 0; i < size; i++) { const libxl__json_object *t; int j; if (flexarray_get(array, i, (void**)&t) != 0) return ERROR_FAIL; if (!libxl__json_object_is_map(t)) return ERROR_FAIL; for (j = 0; j < ARRAY_SIZE(l[0].input); j++) { const libxl__json_object *r; r = libxl__json_map_get(input_names[j], t, JSON_INTEGER); if (!r) l[i].input[j] = XEN_CPUID_INPUT_UNUSED; else l[i].input[j] = libxl__json_object_get_integer(r); } for (j = 0; j < ARRAY_SIZE(l[0].policy); j++) { const libxl__json_object *r; r = libxl__json_map_get(policy_names[j], t, JSON_STRING); if (!r) l[i].policy[j] = NULL; else l[i].policy[j] = libxl__strdup(NOGC, libxl__json_object_get_string(r)); } } return 0; } int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *pl) { int i = 0; libxl_cpuid_policy_list l = *pl; if (l) { while (l[i].input[0] != XEN_CPUID_INPUT_UNUSED) i++; } return i; } void libxl_cpuid_policy_list_copy(libxl_ctx *ctx, libxl_cpuid_policy_list *dst, const libxl_cpuid_policy_list *src) { GC_INIT(ctx); int i, j, len; if (*src == NULL) { *dst = NULL; goto out; } len = libxl_cpuid_policy_list_length(src); /* one extra slot for sentinel */ *dst = libxl__calloc(NOGC, len + 1, sizeof(libxl_cpuid_policy)); (*dst)[len].input[0] = XEN_CPUID_INPUT_UNUSED; (*dst)[len].input[1] = XEN_CPUID_INPUT_UNUSED; for (i = 0; i < len; i++) { for (j = 0; j < 2; j++) (*dst)[i].input[j] = (*src)[i].input[j]; for (j = 0; j < 4; j++) if ((*src)[i].policy[j]) (*dst)[i].policy[j] = libxl__strdup(NOGC, (*src)[i].policy[j]); else (*dst)[i].policy[j] = NULL; } out: GC_FREE; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/flexarray.h0000664000175000017500000000336113256712137015100 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef FLEXARRAY_H #define FLEXARRAY_H struct libxl__gc; typedef struct flexarray { int size; int autogrow; unsigned int count; void **data; /* array of pointer */ struct libxl__gc *gc; } flexarray_t; /* * NOGC can be used with flexarrays, but flexarray_free will need to be called * to free the struct. The content of the flexarray will not be freed through * flexarray_free. */ _hidden flexarray_t *flexarray_make(struct libxl__gc *gc_opt, int size, int autogrow); _hidden void flexarray_free(flexarray_t *array); _hidden void flexarray_grow(flexarray_t *array, int extents); _hidden int flexarray_set(flexarray_t *array, unsigned int index, void *ptr); _hidden int flexarray_append(flexarray_t *array, void *ptr); _hidden int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2); _hidden int flexarray_vappend(flexarray_t *array, ...); _hidden int flexarray_get(flexarray_t *array, int index, void **ptr); _hidden void **flexarray_contents(flexarray_t *array); #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_vif.c0000664000175000017500000000711313256712137015240 0ustar smbsmb#include "libxl_osdeps.h" /* must come before any other headers */ #include "libxlu_internal.h" static const char *vif_bytes_per_sec_re = "^[0-9]+[GMK]?[Bb]/s$"; static const char *vif_internal_usec_re = "^[0-9]+[mu]?s?$"; static void xlu__vif_err(XLU_Config *cfg, const char *msg, const char *rate) { fprintf(cfg->report, "%s: config parsing error in vif: %s in `%s'\n", cfg->config_source, msg, rate); } static int vif_parse_rate_bytes_per_sec(XLU_Config *cfg, const char *bytes, uint64_t *bytes_per_sec) { regex_t rec; uint64_t tmp = 0; const char *p; int rc = 0; regcomp(&rec, vif_bytes_per_sec_re, REG_EXTENDED|REG_NOSUB); if (regexec(&rec, bytes, 0, NULL, 0)) { xlu__vif_err(cfg, "invalid rate", bytes); rc = EINVAL; goto out; } p = bytes; tmp = strtoull(p, (char**)&p, 0); if (tmp == 0 || tmp > UINT32_MAX || errno == ERANGE) { xlu__vif_err(cfg, "rate overflow", bytes); rc = EOVERFLOW; goto out; } if (*p == 'G') tmp *= 1000 * 1000 * 1000; else if (*p == 'M') tmp *= 1000 * 1000; else if (*p == 'K') tmp *= 1000; if (*p == 'b' || *(p+1) == 'b') tmp /= 8; *bytes_per_sec = tmp; out: regfree(&rec); return rc; } static int vif_parse_rate_interval_usecs(XLU_Config *cfg, const char *interval, uint32_t *interval_usecs) { regex_t rec; uint64_t tmp = 0; const char *p; int rc = 0; regcomp(&rec, vif_internal_usec_re, REG_EXTENDED|REG_NOSUB); if (regexec(&rec, interval, 0, NULL, 0)) { xlu__vif_err(cfg, "invalid replenishment interval", interval); rc = EINVAL; goto out; } p = interval; tmp = strtoull(p, (char**)&p, 0); if (tmp == 0 || tmp > UINT32_MAX || errno == ERANGE) { xlu__vif_err(cfg, "replenishment interval overflow", interval); rc = EOVERFLOW; goto out; } if (*p == 's' || *p == '\0') tmp *= 1000 * 1000; else if (*p == 'm') tmp *= 1000; if (tmp > UINT32_MAX) { xlu__vif_err(cfg, "replenishment interval overflow", interval); rc = EOVERFLOW; goto out; } *interval_usecs = (uint32_t) tmp; out: regfree(&rec); return rc; } int xlu_vif_parse_rate(XLU_Config *cfg, const char *rate, libxl_device_nic *nic) { uint64_t bytes_per_sec = 0; uint64_t bytes_per_interval = 0; uint32_t interval_usecs = 50000UL; /* Default to 50ms */ char *p, *tmprate; int rc = 0; tmprate = strdup(rate); if (tmprate == NULL) { rc = ENOMEM; goto out; } p = strchr(tmprate, '@'); if (p != NULL) *p++ = 0; if (!strcmp(tmprate,"")) { xlu__vif_err(cfg, "no rate specified", rate); rc = EINVAL; goto out; } rc = vif_parse_rate_bytes_per_sec(cfg, tmprate, &bytes_per_sec); if (rc) goto out; if (p != NULL) { rc = vif_parse_rate_interval_usecs(cfg, p, &interval_usecs); if (rc) goto out; } if (interval_usecs != 0 && (bytes_per_sec > (UINT64_MAX / interval_usecs))) { xlu__vif_err(cfg, "rate overflow", rate); rc = EOVERFLOW; goto out; } bytes_per_interval = (((uint64_t) bytes_per_sec * (uint64_t) interval_usecs) / 1000000UL); nic->rate_interval_usecs = interval_usecs; nic->rate_bytes_per_interval = bytes_per_interval; out: free(tmprate); return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_json.h0000664000175000017500000000604013256712137015243 0ustar smbsmb/* * Copyright (C) 2011 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef LIBXL_JSON_H #define LIBXL_JSON_H #include #include #ifdef HAVE_YAJL_YAJL_VERSION_H # include #endif yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val); yajl_gen_status libxl_defbool_gen_json(yajl_gen hand, libxl_defbool *p); yajl_gen_status libxl_uuid_gen_json(yajl_gen hand, libxl_uuid *p); yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *p); yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand, libxl_bitmap *p); yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand, libxl_cpuid_policy_list *p); yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *p); yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand, libxl_key_value_list *p); yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand, libxl_hwcap *p); yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p); #include <_libxl_types_json.h> /* YAJL version check */ #if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1) # define HAVE_YAJL_V2 1 #endif #ifdef HAVE_YAJL_V2 typedef size_t libxl_yajl_length; static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks, yajl_alloc_funcs *allocFuncs, void *ctx) { return yajl_alloc(callbacks, allocFuncs, ctx); } static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs) { yajl_gen g; g = yajl_gen_alloc(allocFuncs); if (g) yajl_gen_config(g, yajl_gen_beautify, 1); return g; } #else /* !HAVE_YAJL_V2 */ #define yajl_complete_parse yajl_parse_complete typedef unsigned int libxl_yajl_length; static inline yajl_handle libxl__yajl_alloc(const yajl_callbacks *callbacks, const yajl_alloc_funcs *allocFuncs, void *ctx) { yajl_parser_config cfg = { .allowComments = 1, .checkUTF8 = 1, }; return yajl_alloc(callbacks, &cfg, allocFuncs, ctx); } static inline yajl_gen libxl_yajl_gen_alloc(const yajl_alloc_funcs *allocFuncs) { yajl_gen_config conf = { 1, " " }; return yajl_gen_alloc(&conf, allocFuncs); } #endif /* !HAVE_YAJL_V2 */ yajl_gen_status libxl_domain_config_gen_json(yajl_gen hand, libxl_domain_config *p); #endif /* LIBXL_JSON_H */ xen-4.9.2/tools/libxl/libxlu_disk_i.h0000664000175000017500000000100013256712137015710 0ustar smbsmb#ifndef LIBXLU_DISK_I_H #define LIBXLU_DISK_I_H #include "libxlu_internal.h" typedef struct { XLU_Config *cfg; int err; void *scanner; YY_BUFFER_STATE buf; libxl_device_disk *disk; int access_set, had_depr_prefix; const char *spec; } DiskParseContext; void xlu__disk_err(DiskParseContext *dpc, const char *erroneous, const char *message); #endif /*LIBXLU_DISK_I_H*/ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl.c0000664000175000017500000004705313256712137014216 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" int libxl_ctx_alloc(libxl_ctx **pctx, int version, unsigned flags, xentoollog_logger * lg) { libxl_ctx *ctx = NULL; libxl__gc gc_buf, *gc = NULL; int rc; if (version != LIBXL_VERSION) { rc = ERROR_VERSION; goto out; } ctx = malloc(sizeof(*ctx)); if (!ctx) { xtl_log(lg, XTL_ERROR, errno, "libxl", "%s:%d:%s: Failed to allocate context\n", __FILE__, __LINE__, __func__); rc = ERROR_NOMEM; goto out; } memset(ctx, 0, sizeof(libxl_ctx)); ctx->lg = lg; /* First initialise pointers etc. (cannot fail) */ ctx->nogc_gc.alloc_maxsize = -1; ctx->nogc_gc.owner = ctx; LIBXL_TAILQ_INIT(&ctx->occurred); ctx->osevent_hooks = 0; ctx->poller_app = 0; LIBXL_LIST_INIT(&ctx->pollers_event); LIBXL_LIST_INIT(&ctx->pollers_idle); LIBXL_LIST_INIT(&ctx->pollers_fds_changed); LIBXL_LIST_INIT(&ctx->efds); LIBXL_TAILQ_INIT(&ctx->etimes); ctx->watch_slots = 0; LIBXL_SLIST_INIT(&ctx->watch_freeslots); libxl__ev_fd_init(&ctx->watch_efd); ctx->xce = 0; LIBXL_LIST_INIT(&ctx->evtchns_waiting); libxl__ev_fd_init(&ctx->evtchn_efd); LIBXL_LIST_INIT(&ctx->aos_inprogress); LIBXL_TAILQ_INIT(&ctx->death_list); libxl__ev_xswatch_init(&ctx->death_watch); ctx->childproc_hooks = &libxl__childproc_default_hooks; ctx->childproc_user = 0; ctx->sigchld_selfpipe[0] = -1; ctx->sigchld_selfpipe[1] = -1; libxl__ev_fd_init(&ctx->sigchld_selfpipe_efd); /* The mutex is special because we can't idempotently destroy it */ if (libxl__init_recursive_mutex(ctx, &ctx->lock) < 0) { LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Failed to initialize mutex"); free(ctx); ctx = 0; rc = ERROR_FAIL; goto out; } /* Now ctx is safe for ctx_free; failures simply set rc and "goto out" */ LIBXL_INIT_GC(gc_buf,ctx); gc = &gc_buf; /* Now gc is useable */ rc = libxl__atfork_init(ctx); if (rc) goto out; ctx->poller_app = libxl__poller_get(gc); if (!ctx->poller_app) { rc = ERROR_FAIL; goto out; } ctx->xch = xc_interface_open(lg,lg,0); if (!ctx->xch) { LOGEV(ERROR, errno, "cannot open libxc handle"); rc = ERROR_FAIL; goto out; } ctx->xsh = xs_daemon_open(); if (!ctx->xsh) ctx->xsh = xs_domain_open(); if (!ctx->xsh) { LOGEV(ERROR, errno, "cannot connect to xenstore"); rc = ERROR_FAIL; goto out; } *pctx = ctx; return 0; out: if (gc) libxl__free_all(gc); libxl_ctx_free(ctx); *pctx = NULL; return rc; } static void free_disable_deaths(libxl__gc *gc, struct libxl__evgen_domain_death_list *l) { libxl_evgen_domain_death *death; while ((death = LIBXL_TAILQ_FIRST(l))) libxl__evdisable_domain_death(gc, death); } static void discard_events(struct libxl__event_list *l) { /* doesn't bother unlinking from the list, so l is corrupt on return */ libxl_event *ev, *next; LIBXL_TAILQ_FOREACH_SAFE(ev, l, link, next) libxl_event_free(0, ev); } int libxl_ctx_free(libxl_ctx *ctx) { if (!ctx) return 0; int i; GC_INIT(ctx); CTX_LOCK; assert(!ctx->osevent_in_hook); CTX->osevent_in_hook += 1000; /* make violations easier to debug */ /* Deregister all libxl__ev_KINDs: */ free_disable_deaths(gc, &CTX->death_list); free_disable_deaths(gc, &CTX->death_reported); libxl_evgen_disk_eject *eject; while ((eject = LIBXL_LIST_FIRST(&CTX->disk_eject_evgens))) libxl__evdisable_disk_eject(gc, eject); libxl_childproc_setmode(CTX,0,0); for (i = 0; i < ctx->watch_nslots; i++) assert(!libxl__watch_slot_contents(gc, i)); assert(!libxl__ev_fd_isregistered(&ctx->watch_efd)); assert(!libxl__ev_fd_isregistered(&ctx->evtchn_efd)); assert(!libxl__ev_fd_isregistered(&ctx->sigchld_selfpipe_efd)); /* Now there should be no more events requested from the application: */ assert(LIBXL_LIST_EMPTY(&ctx->efds)); assert(LIBXL_TAILQ_EMPTY(&ctx->etimes)); assert(LIBXL_LIST_EMPTY(&ctx->evtchns_waiting)); assert(LIBXL_LIST_EMPTY(&ctx->aos_inprogress)); if (ctx->xch) xc_interface_close(ctx->xch); libxl_version_info_dispose(&ctx->version_info); if (ctx->xsh) xs_daemon_close(ctx->xsh); if (ctx->xce) xenevtchn_close(ctx->xce); libxl__poller_put(ctx, ctx->poller_app); ctx->poller_app = NULL; assert(LIBXL_LIST_EMPTY(&ctx->pollers_event)); assert(LIBXL_LIST_EMPTY(&ctx->pollers_fds_changed)); libxl__poller *poller, *poller_tmp; LIBXL_LIST_FOREACH_SAFE(poller, &ctx->pollers_idle, entry, poller_tmp) { libxl__poller_dispose(poller); free(poller); } free(ctx->watch_slots); discard_events(&ctx->occurred); /* If we have outstanding children, then the application inherits * them; we wish the application good luck with understanding * this if and when it reaps them. */ libxl__sigchld_notneeded(gc); libxl__pipe_close(ctx->sigchld_selfpipe); CTX_UNLOCK; pthread_mutex_destroy(&ctx->lock); GC_FREE; free(ctx); return 0; } void libxl_string_list_dispose(libxl_string_list *psl) { int i; libxl_string_list sl = *psl; if (!sl) return; for (i = 0; sl[i] != NULL; i++) { free(sl[i]); sl[i] = NULL; } free(sl); *psl = NULL; } void libxl_string_list_copy(libxl_ctx *ctx, libxl_string_list *dst, const libxl_string_list *src) { GC_INIT(ctx); int i, len; if (!*src) { *dst = NULL; goto out; } len = libxl_string_list_length(src); /* one extra slot for sentinel */ *dst = libxl__calloc(NOGC, len + 1, sizeof(char *)); for (i = 0; i < len; i++) (*dst)[i] = libxl__strdup(NOGC, (*src)[i]); out: GC_FREE; } int libxl_string_list_length(const libxl_string_list *psl) { int i = 0; if (*psl) while ((*psl)[i]) i++; return i; } int libxl_key_value_list_length(const libxl_key_value_list *pkvl) { int i = 0; libxl_key_value_list kvl = *pkvl; if (kvl) { while (kvl[2 * i]) /* Only checks keys */ i++; } return i; } void libxl_key_value_list_dispose(libxl_key_value_list *pkvl) { int i; libxl_key_value_list kvl = *pkvl; if (!kvl) return; for (i = 0; kvl[i] != NULL; i += 2) { free(kvl[i]); kvl[i] = NULL; if (kvl[i + 1]) { free(kvl[i + 1]); kvl[i+1] = NULL; } } free(kvl); *pkvl = NULL; } void libxl_key_value_list_copy(libxl_ctx *ctx, libxl_key_value_list *dst, const libxl_key_value_list *src) { GC_INIT(ctx); int i, len; if (*src == NULL) { *dst = NULL; goto out; } len = libxl_key_value_list_length(src); /* one extra slot for sentinel */ *dst = libxl__calloc(NOGC, len * 2 + 1, sizeof(char *)); for (i = 0; i < len * 2; i += 2) { (*dst)[i] = libxl__strdup(NOGC, (*src)[i]); if ((*src)[i+1]) (*dst)[i+1] = libxl__strdup(NOGC, (*src)[i+1]); else (*dst)[i+1] = NULL; } out: GC_FREE; } void libxl_defbool_set(libxl_defbool *db, bool b) { db->val = b ? LIBXL__DEFBOOL_TRUE : LIBXL__DEFBOOL_FALSE; } void libxl_defbool_unset(libxl_defbool *db) { db->val = LIBXL__DEFBOOL_DEFAULT; } bool libxl_defbool_is_default(libxl_defbool db) { return !db.val; } void libxl_defbool_setdefault(libxl_defbool *db, bool b) { if (libxl_defbool_is_default(*db)) libxl_defbool_set(db, b); } bool libxl_defbool_val(libxl_defbool db) { assert(!libxl_defbool_is_default(db)); return db.val > 0; } const char *libxl_defbool_to_string(libxl_defbool b) { if (b.val < 0) return LIBXL__DEFBOOL_STR_FALSE; else if (b.val > 0) return LIBXL__DEFBOOL_STR_TRUE; else return LIBXL__DEFBOOL_STR_DEFAULT; } /******************************************************************************/ int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo) { xc_physinfo_t xcphysinfo = { 0 }; int rc; long l; GC_INIT(ctx); rc = xc_physinfo(ctx->xch, &xcphysinfo); if (rc != 0) { LOGE(ERROR, "getting physinfo"); GC_FREE; return ERROR_FAIL; } physinfo->threads_per_core = xcphysinfo.threads_per_core; physinfo->cores_per_socket = xcphysinfo.cores_per_socket; physinfo->max_cpu_id = xcphysinfo.max_cpu_id; physinfo->nr_cpus = xcphysinfo.nr_cpus; physinfo->cpu_khz = xcphysinfo.cpu_khz; physinfo->total_pages = xcphysinfo.total_pages; physinfo->free_pages = xcphysinfo.free_pages; physinfo->scrub_pages = xcphysinfo.scrub_pages; physinfo->outstanding_pages = xcphysinfo.outstanding_pages; l = xc_sharing_freed_pages(ctx->xch); if (l < 0 && errno == ENOSYS) { l = 0; } else if (l < 0) { LOGEV(ERROR, l, "getting sharing freed pages"); GC_FREE; return ERROR_FAIL; } physinfo->sharing_freed_pages = l; l = xc_sharing_used_frames(ctx->xch); if (l < 0 && errno == ENOSYS) { l = 0; } else if (l < 0) { LOGEV(ERROR, l, "getting sharing used frames"); GC_FREE; return ERROR_FAIL; } physinfo->sharing_used_frames = l; physinfo->nr_nodes = xcphysinfo.nr_nodes; memcpy(physinfo->hw_cap,xcphysinfo.hw_cap, sizeof(physinfo->hw_cap)); physinfo->cap_hvm = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hvm); physinfo->cap_hvm_directio = !!(xcphysinfo.capabilities & XEN_SYSCTL_PHYSCAP_hvm_directio); GC_FREE; return 0; } libxl_cputopology *libxl_get_cpu_topology(libxl_ctx *ctx, int *nb_cpu_out) { GC_INIT(ctx); xc_cputopo_t *cputopo; libxl_cputopology *ret = NULL; int i; unsigned num_cpus = 0; /* Setting buffer to NULL makes the call return number of CPUs */ if (xc_cputopoinfo(ctx->xch, &num_cpus, NULL)) { LOGE(ERROR, "Unable to determine number of CPUS"); goto out; } cputopo = libxl__zalloc(gc, sizeof(*cputopo) * num_cpus); if (xc_cputopoinfo(ctx->xch, &num_cpus, cputopo)) { LOGE(ERROR, "CPU topology info hypercall failed"); goto out; } ret = libxl__zalloc(NOGC, sizeof(libxl_cputopology) * num_cpus); for (i = 0; i < num_cpus; i++) { #define V(map, i, invalid) ( cputopo[i].map == invalid) ? \ LIBXL_CPUTOPOLOGY_INVALID_ENTRY : cputopo[i].map ret[i].core = V(core, i, XEN_INVALID_CORE_ID); ret[i].socket = V(socket, i, XEN_INVALID_SOCKET_ID); ret[i].node = V(node, i, XEN_INVALID_NODE_ID); #undef V } *nb_cpu_out = num_cpus; out: GC_FREE; return ret; } libxl_pcitopology *libxl_get_pci_topology(libxl_ctx *ctx, int *num_devs) { GC_INIT(ctx); physdev_pci_device_t *devs; uint32_t *nodes; libxl_pcitopology *ret = NULL; int i, rc; *num_devs = libxl__pci_numdevs(gc); if (*num_devs < 0) { LOG(ERROR, "Unable to determine number of PCI devices, rc %d", *num_devs); goto out; } devs = libxl__zalloc(gc, sizeof(*devs) * *num_devs); nodes = libxl__zalloc(gc, sizeof(*nodes) * *num_devs); rc = libxl__pci_topology_init(gc, devs, *num_devs); if (rc) { LOG(ERROR, "Cannot initialize PCI hypercall structure, rc %d", rc); goto out; } if (xc_pcitopoinfo(ctx->xch, *num_devs, devs, nodes) != 0) { LOGE(ERROR, "PCI topology info hypercall failed"); goto out; } ret = libxl__zalloc(NOGC, sizeof(libxl_pcitopology) * *num_devs); for (i = 0; i < *num_devs; i++) { ret[i].seg = devs[i].seg; ret[i].bus = devs[i].bus; ret[i].devfn = devs[i].devfn; ret[i].node = ((nodes[i] == XEN_INVALID_NODE_ID) || (nodes[i] == XEN_INVALID_DEV)) ? LIBXL_PCITOPOLOGY_INVALID_ENTRY : nodes[i]; } out: GC_FREE; return ret; } libxl_numainfo *libxl_get_numainfo(libxl_ctx *ctx, int *nr) { GC_INIT(ctx); xc_meminfo_t *meminfo; uint32_t *distance; libxl_numainfo *ret = NULL; int i, j; unsigned num_nodes = 0; if (xc_numainfo(ctx->xch, &num_nodes, NULL, NULL)) { LOGE(ERROR, "Unable to determine number of nodes"); goto out; } meminfo = libxl__zalloc(gc, sizeof(*meminfo) * num_nodes); distance = libxl__zalloc(gc, sizeof(*distance) * num_nodes * num_nodes); if (xc_numainfo(ctx->xch, &num_nodes, meminfo, distance)) { LOGE(ERROR, "getting numainfo"); goto out; } *nr = num_nodes; ret = libxl__zalloc(NOGC, sizeof(libxl_numainfo) * num_nodes); for (i = 0; i < num_nodes; i++) ret[i].dists = libxl__calloc(NOGC, num_nodes, sizeof(*distance)); for (i = 0; i < num_nodes; i++) { #define V(val, invalid) (val == invalid) ? \ LIBXL_NUMAINFO_INVALID_ENTRY : val ret[i].size = V(meminfo[i].memsize, XEN_INVALID_MEM_SZ); ret[i].free = V(meminfo[i].memfree, XEN_INVALID_MEM_SZ); ret[i].num_dists = num_nodes; for (j = 0; j < ret[i].num_dists; j++) { unsigned idx = i * num_nodes + j; ret[i].dists[j] = V(distance[idx], XEN_INVALID_NODE_DIST); } #undef V } out: GC_FREE; return ret; } static int libxl__xc_version_wrap(libxl__gc *gc, libxl_version_info *info, xen_build_id_t *build) { int r; r = xc_version(CTX->xch, XENVER_build_id, build); switch (r) { case -EPERM: case -ENODATA: case 0: info->build_id = libxl__strdup(NOGC, ""); break; case -ENOBUFS: break; default: if (r > 0) { unsigned int i; info->build_id = libxl__zalloc(NOGC, (r * 2) + 1); for (i = 0; i < r ; i++) snprintf(&info->build_id[i * 2], 3, "%02hhx", build->buf[i]); r = 0; } break; } return r; } const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx) { GC_INIT(ctx); union { xen_extraversion_t xen_extra; xen_compile_info_t xen_cc; xen_changeset_info_t xen_chgset; xen_capabilities_info_t xen_caps; xen_platform_parameters_t p_parms; xen_commandline_t xen_commandline; xen_build_id_t build_id; } u; long xen_version; int r; libxl_version_info *info = &ctx->version_info; if (info->xen_version_extra != NULL) goto out; xen_version = xc_version(ctx->xch, XENVER_version, NULL); info->xen_version_major = xen_version >> 16; info->xen_version_minor = xen_version & 0xFF; xc_version(ctx->xch, XENVER_extraversion, &u.xen_extra); info->xen_version_extra = libxl__strdup(NOGC, u.xen_extra); xc_version(ctx->xch, XENVER_compile_info, &u.xen_cc); info->compiler = libxl__strdup(NOGC, u.xen_cc.compiler); info->compile_by = libxl__strdup(NOGC, u.xen_cc.compile_by); info->compile_domain = libxl__strdup(NOGC, u.xen_cc.compile_domain); info->compile_date = libxl__strdup(NOGC, u.xen_cc.compile_date); xc_version(ctx->xch, XENVER_capabilities, &u.xen_caps); info->capabilities = libxl__strdup(NOGC, u.xen_caps); xc_version(ctx->xch, XENVER_changeset, &u.xen_chgset); info->changeset = libxl__strdup(NOGC, u.xen_chgset); xc_version(ctx->xch, XENVER_platform_parameters, &u.p_parms); info->virt_start = u.p_parms.virt_start; info->pagesize = xc_version(ctx->xch, XENVER_pagesize, NULL); xc_version(ctx->xch, XENVER_commandline, &u.xen_commandline); info->commandline = libxl__strdup(NOGC, u.xen_commandline); u.build_id.len = sizeof(u) - sizeof(u.build_id); r = libxl__xc_version_wrap(gc, info, &u.build_id); if (r == -ENOBUFS) { xen_build_id_t *build_id; build_id = libxl__zalloc(gc, info->pagesize); build_id->len = info->pagesize - sizeof(*build_id); r = libxl__xc_version_wrap(gc, info, build_id); if (r) LOGEV(ERROR, r, "getting build_id"); } out: GC_FREE; return info; } int libxl_send_sysrq(libxl_ctx *ctx, uint32_t domid, char sysrq) { GC_INIT(ctx); char *dompath = libxl__xs_get_dompath(gc, domid); libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/control/sysrq", dompath), "%c", sysrq); GC_FREE; return 0; } int libxl_send_debug_keys(libxl_ctx *ctx, char *keys) { int ret; GC_INIT(ctx); ret = xc_send_debug_keys(ctx->xch, keys); if ( ret < 0 ) { LOGE(ERROR, "sending debug keys"); GC_FREE; return ERROR_FAIL; } GC_FREE; return 0; } static int fd_set_flags(libxl_ctx *ctx, int fd, int fcntlgetop, int fcntlsetop, const char *fl, int flagmask, int set_p) { int flags, r; GC_INIT(ctx); flags = fcntl(fd, fcntlgetop); if (flags == -1) { LOGE(ERROR, "fcntl(,F_GET%s) failed", fl); GC_FREE; return ERROR_FAIL; } if (set_p) flags |= flagmask; else flags &= ~flagmask; r = fcntl(fd, fcntlsetop, flags); if (r == -1) { LOGE(ERROR, "fcntl(,F_SET%s) failed", fl); GC_FREE; return ERROR_FAIL; } GC_FREE; return 0; } int libxl_fd_set_cloexec(libxl_ctx *ctx, int fd, int cloexec) { return fd_set_flags(ctx,fd, F_GETFD,F_SETFD,"FD", FD_CLOEXEC, cloexec); } int libxl_fd_set_nonblock(libxl_ctx *ctx, int fd, int nonblock) { return fd_set_flags(ctx,fd, F_GETFL,F_SETFL,"FL", O_NONBLOCK, nonblock); } int libxl__fd_flags_modify_save(libxl__gc *gc, int fd, int mask, int val, int *r_oldflags) { int rc, ret, fdfl; fdfl = fcntl(fd, F_GETFL); if (fdfl < 0) { LOGE(ERROR, "failed to fcntl.F_GETFL for fd %d", fd); rc = ERROR_FAIL; goto out_err; } LOG(DEBUG, "fnctl F_GETFL flags for fd %d are 0x%x", fd, fdfl); if (r_oldflags) *r_oldflags = fdfl; fdfl &= mask; fdfl |= val; LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl); ret = fcntl(fd, F_SETFL, fdfl); if (ret < 0) { LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd); rc = ERROR_FAIL; goto out_err; } rc = 0; out_err: return rc; } int libxl__fd_flags_restore(libxl__gc *gc, int fd, int fdfl) { int ret, rc; LOG(DEBUG, "fnctl F_SETFL of fd %d to 0x%x", fd, fdfl); ret = fcntl(fd, F_SETFL, fdfl); if (ret < 0) { LOGE(ERROR, "failed to fcntl.F_SETFL for fd %d", fd); rc = ERROR_FAIL; goto out_err; } rc = 0; out_err: return rc; } void libxl_hwcap_copy(libxl_ctx *ctx,libxl_hwcap *dst, const libxl_hwcap *src) { int i; for (i = 0; i < 8; i++) (*dst)[i] = (*src)[i]; } void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src) { int i; for (i = 0; i < 6; i++) (*dst)[i] = (*src)[i]; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/check-xl-vif-parse0000775000175000017500000000744113256712137016253 0ustar smbsmb#!/bin/bash set -e if [ -x ./xl ] ; then export LD_LIBRARY_PATH=. XL=./xl else XL=xl fi fprefix=tmp.check-xl-vif-parse expected () { cat >$fprefix.expected } failures=0 one () { expected_rc=$1; shift printf "test case %s...\n" "$*" set +e ${XL} -N network-attach 0 "$@" $fprefix.actual 2>/dev/null actual_rc=$? diff -u $fprefix.expected $fprefix.actual diff_rc=$? set -e if [ $actual_rc != $expected_rc ] || [ $diff_rc != 0 ]; then echo >&2 "test case \`$*' failed ($actual_rc $diff_rc)" failures=$(( $failures + 1 )) fi } complete () { if [ "$failures" = 0 ]; then echo all ok.; exit 0 else echo "$failures tests failed."; exit 1 fi } e=255 #---------- test data ---------- # test invalid vif config expected * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef LIBXL_UTILS_H #define LIBXL_UTILS_H #include "libxl.h" #ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE const #endif char *libxl_basename(const char *name); /* returns string from strdup */ unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus); int libxl_name_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid); int libxl_domain_qualifier_to_domid(libxl_ctx *ctx, const char *name, uint32_t *domid); char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid); int libxl_cpupool_qualifier_to_cpupoolid(libxl_ctx *ctx, const char *p, uint32_t *poolid_r, int *was_name_r); int libxl_name_to_cpupoolid(libxl_ctx *ctx, const char *name, uint32_t *poolid); char *libxl_cpupoolid_to_name(libxl_ctx *ctx, uint32_t poolid); int libxl_cpupoolid_is_valid(libxl_ctx *ctx, uint32_t poolid); int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid); int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid); int libxl_create_logfile(libxl_ctx *ctx, const char *name, char **full_name); int libxl_string_to_backend(libxl_ctx *ctx, char *s, libxl_disk_backend *backend); int libxl_read_file_contents(libxl_ctx *ctx, const char *filename, void **data_r, int *datalen_r); /* Reads the contents of the plain file filename into a mallocd * buffer. Returns 0 or errno. Any errors other than ENOENT are logged. * If the file is empty, *data_r and *datalen_r are set to 0. * On error, *data_r and *datalen_r are unchanged. * data_r and/or datalen_r may be 0. */ int libxl_read_exactly(libxl_ctx *ctx, int fd, void *data, ssize_t sz, const char *filename, const char *what); int libxl_write_exactly(libxl_ctx *ctx, int fd, const void *data, ssize_t sz, const char *filename, const char *what); /* Returns 0 or errno. If file is truncated on reading, returns * EPROTO and you have no way to tell how much was read. Errors are * logged using filename (which is only used for logging) and what * (which may be 0). */ int libxl_pipe(libxl_ctx *ctx, int pipes[2]); /* Just like pipe(2), but log errors. */ void libxl_report_child_exitstatus(libxl_ctx *ctx, xentoollog_level, const char *what, pid_t pid, int status); /* treats all exit statuses as errors; if that's not what you want, * check status yourself first */ int libxl_mac_to_device_nic(libxl_ctx *ctx, uint32_t domid, const char *mac, libxl_device_nic *nic); int libxl_devid_to_device_nic(libxl_ctx *ctx, uint32_t domid, int devid, libxl_device_nic *nic); int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid, const char *vdev, libxl_device_disk *disk); int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid, libxl_uuid *uuid, libxl_device_vtpm *vtpm); int libxl_devid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid, int devid, libxl_device_vtpm *vtpm); int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, uint32_t domid, int devid, libxl_device_usbctrl *usbctrl); int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, uint32_t domid, int ctrl, int port, libxl_device_usbdev *usbdev); int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits); /* Allocated bimap is from malloc, libxl_bitmap_dispose() to be * called by the application when done. */ void libxl_bitmap_copy_alloc(libxl_ctx *ctx, libxl_bitmap *dptr, const libxl_bitmap *sptr); void libxl_bitmap_copy(libxl_ctx *ctx, libxl_bitmap *dptr, const libxl_bitmap *sptr); int libxl_bitmap_is_full(const libxl_bitmap *bitmap); int libxl_bitmap_is_empty(const libxl_bitmap *bitmap); int libxl_bitmap_test(const libxl_bitmap *bitmap, int bit); void libxl_bitmap_set(libxl_bitmap *bitmap, int bit); void libxl_bitmap_reset(libxl_bitmap *bitmap, int bit); int libxl_bitmap_count_set(const libxl_bitmap *bitmap); int libxl_bitmap_or(libxl_ctx *ctx, libxl_bitmap *or_map, const libxl_bitmap *map1, const libxl_bitmap *map2); int libxl_bitmap_and(libxl_ctx *ctx, libxl_bitmap *and_map, const libxl_bitmap *map1, const libxl_bitmap *map2); char *libxl_bitmap_to_hex_string(libxl_ctx *ctx, const libxl_bitmap *bitmap); static inline void libxl_bitmap_set_any(libxl_bitmap *bitmap) { memset(bitmap->map, -1, bitmap->size); } static inline void libxl_bitmap_set_none(libxl_bitmap *bitmap) { memset(bitmap->map, 0, bitmap->size); } static inline int libxl_bitmap_cpu_valid(libxl_bitmap *bitmap, int bit) { return bit >= 0 && bit < (bitmap->size * 8); } #define libxl_for_each_bit(var, map) for (var = 0; var < (map).size * 8; var++) #define libxl_for_each_set_bit(v, m) for (v = 0; v < (m).size * 8; v++) \ if (libxl_bitmap_test(&(m), v)) /* * Compares two bitmaps bit by bit, up to nr_bits or, if nr_bits is 0, up * to the size of the largest bitmap. If sizes does not match, bits past the * of a bitmap are considered as being 0, which matches with the semantic and * implementation of libxl_bitmap_test I think(). * * So, basically, [0,1,0] and [0,1] are considered equal, while [0,1,1] and * [0,1] are different. */ static inline int libxl_bitmap_equal(const libxl_bitmap *ba, const libxl_bitmap *bb, int nr_bits) { int i; if (nr_bits == 0) nr_bits = ba->size > bb->size ? ba->size * 8 : bb->size * 8; for (i = 0; i < nr_bits; i++) { if (libxl_bitmap_test(ba, i) != libxl_bitmap_test(bb, i)) return 0; } return 1; } int libxl_cpu_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *cpumap, int max_cpus); int libxl_node_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *nodemap, int max_nodes); int libxl_socket_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *socketmap, int max_sockets); /* Fill socketmap with the CPU topology information on the system. */ int libxl_get_online_socketmap(libxl_ctx *ctx, libxl_bitmap *socketmap); /* Populate cpumap with the cpus spanned by the nodes in nodemap */ int libxl_nodemap_to_cpumap(libxl_ctx *ctx, const libxl_bitmap *nodemap, libxl_bitmap *cpumap); /* Populate cpumap with the cpus spanned by node */ int libxl_node_to_cpumap(libxl_ctx *ctx, int node, libxl_bitmap *cpumap); /* Populate nodemap with the nodes of the cpus in cpumap */ int libxl_cpumap_to_nodemap(libxl_ctx *ctx, const libxl_bitmap *cpumap, libxl_bitmap *nodemap); static inline uint32_t libxl__sizekb_to_mb(uint32_t s) { return (s + 1023) / 1024; } void libxl_string_copy(libxl_ctx *ctx, char **dst, char * const*src); #define LIBXL_FILLZERO(object) (memset(&(object), 0, sizeof((object)))) #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_arm_acpi.c0000664000175000017500000003150413256712137016043 0ustar smbsmb/* * ARM DomU ACPI generation * * Copyright (C) 2016 Linaro Ltd. * * Author: Shannon Zhao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_arm.h" #include /* Below typedefs are useful for the headers under acpi/ */ typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef int64_t s64; #include #include #ifndef BITS_PER_LONG #ifdef _LP64 #define BITS_PER_LONG 64 #else #define BITS_PER_LONG 32 #endif #endif #define ACPI_MACHINE_WIDTH BITS_PER_LONG #define COMPILER_DEPENDENT_INT64 int64_t #define COMPILER_DEPENDENT_UINT64 uint64_t #include _hidden extern const unsigned char dsdt_anycpu_arm[]; _hidden extern const int dsdt_anycpu_arm_len; #define ACPI_OEM_ID "Xen" #define ACPI_OEM_TABLE_ID "ARM" #define ACPI_ASL_COMPILER_ID "XL" enum { RSDP, XSDT, GTDT, MADT, FADT, DSDT, MAX_TABLE_NUMS, }; struct acpitable { uint64_t addr; size_t size; }; static int libxl__estimate_madt_size(libxl__gc *gc, const libxl_domain_build_info *info, size_t *size) { int rc = 0; switch (info->arch_arm.gic_version) { case LIBXL_GIC_VERSION_V2: *size = sizeof(struct acpi_table_madt) + ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus + sizeof(struct acpi_madt_generic_distributor); break; case LIBXL_GIC_VERSION_V3: *size = sizeof(struct acpi_table_madt) + ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus + sizeof(struct acpi_madt_generic_distributor) + sizeof(struct acpi_madt_generic_redistributor); break; default: LOG(ERROR, "Unknown GIC version"); *size = 0; rc = ERROR_FAIL; break; } return rc; } int libxl__get_acpi_size(libxl__gc *gc, const libxl_domain_build_info *info, uint64_t *out) { uint64_t size; int rc = 0; rc = libxl__estimate_madt_size(gc, info, &size); if (rc < 0) goto out; *out = ROUNDUP(size, 3) + ROUNDUP(sizeof(struct acpi_table_rsdp), 3) + ROUNDUP(sizeof(struct acpi_table_xsdt), 3) + ROUNDUP(sizeof(struct acpi_table_gtdt), 3) + ROUNDUP(sizeof(struct acpi_table_fadt), 3) + ROUNDUP(sizeof(dsdt_anycpu_arm_len), 3); out: return rc; } static int libxl__allocate_acpi_tables(libxl__gc *gc, libxl_domain_build_info *info, struct xc_dom_image *dom, struct acpitable acpitables[]) { int rc; size_t size; acpitables[RSDP].addr = GUEST_ACPI_BASE; acpitables[RSDP].size = sizeof(struct acpi_table_rsdp); dom->acpi_modules[0].length += ROUNDUP(acpitables[RSDP].size, 3); acpitables[XSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length; /* * Currently only 3 tables(GTDT, FADT, MADT) are pointed by XSDT. Alloc * entries for them. */ acpitables[XSDT].size = sizeof(struct acpi_table_xsdt) + sizeof(uint64_t) * 2; dom->acpi_modules[0].length += ROUNDUP(acpitables[XSDT].size, 3); acpitables[GTDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length; acpitables[GTDT].size = sizeof(struct acpi_table_gtdt); dom->acpi_modules[0].length += ROUNDUP(acpitables[GTDT].size, 3); acpitables[MADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length; rc = libxl__estimate_madt_size(gc, info, &size); if (rc < 0) goto out; acpitables[MADT].size = size; dom->acpi_modules[0].length += ROUNDUP(acpitables[MADT].size, 3); acpitables[FADT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length; acpitables[FADT].size = sizeof(struct acpi_table_fadt); dom->acpi_modules[0].length += ROUNDUP(acpitables[FADT].size, 3); acpitables[DSDT].addr = GUEST_ACPI_BASE + dom->acpi_modules[0].length; acpitables[DSDT].size = dsdt_anycpu_arm_len; dom->acpi_modules[0].length += ROUNDUP(acpitables[DSDT].size, 3); assert(dom->acpi_modules[0].length <= GUEST_ACPI_SIZE); dom->acpi_modules[0].data = libxl__zalloc(gc, dom->acpi_modules[0].length); rc = 0; out: return rc; } static void calculate_checksum(void *table, uint32_t checksum_offset, uint32_t length) { uint8_t *p, sum = 0; p = table; p[checksum_offset] = 0; while (length--) sum = sum + *p++; p = table; p[checksum_offset] = -sum; } static void make_acpi_rsdp(libxl__gc *gc, struct xc_dom_image *dom, struct acpitable acpitables[]) { uint64_t offset = acpitables[RSDP].addr - GUEST_ACPI_BASE; struct acpi_table_rsdp *rsdp = (void *)dom->acpi_modules[0].data + offset; memcpy(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature)); memcpy(rsdp->oem_id, ACPI_OEM_ID, sizeof(rsdp->oem_id)); rsdp->length = acpitables[RSDP].size; rsdp->revision = 0x02; rsdp->xsdt_physical_address = acpitables[XSDT].addr; calculate_checksum(rsdp, offsetof(struct acpi_table_rsdp, extended_checksum), acpitables[RSDP].size); } static void make_acpi_header(struct acpi_table_header *h, const char *sig, size_t len, uint8_t rev) { memcpy(h->signature, sig, 4); h->length = len; h->revision = rev; memcpy(h->oem_id, ACPI_OEM_ID, sizeof(h->oem_id)); memcpy(h->oem_table_id, ACPI_OEM_TABLE_ID, sizeof(h->oem_table_id)); h->oem_revision = 0; memcpy(h->asl_compiler_id, ACPI_ASL_COMPILER_ID, sizeof(h->asl_compiler_id)); h->asl_compiler_revision = 0; h->checksum = 0; } static void make_acpi_xsdt(libxl__gc *gc, struct xc_dom_image *dom, struct acpitable acpitables[]) { uint64_t offset = acpitables[XSDT].addr - GUEST_ACPI_BASE; struct acpi_table_xsdt *xsdt = (void *)dom->acpi_modules[0].data + offset; xsdt->table_offset_entry[0] = acpitables[MADT].addr; xsdt->table_offset_entry[1] = acpitables[GTDT].addr; xsdt->table_offset_entry[2] = acpitables[FADT].addr; make_acpi_header(&xsdt->header, "XSDT", acpitables[XSDT].size, 1); calculate_checksum(xsdt, offsetof(struct acpi_table_header, checksum), acpitables[XSDT].size); } static void make_acpi_gtdt(libxl__gc *gc, struct xc_dom_image *dom, struct acpitable acpitables[]) { uint64_t offset = acpitables[GTDT].addr - GUEST_ACPI_BASE; struct acpi_table_gtdt *gtdt = (void *)dom->acpi_modules[0].data + offset; gtdt->non_secure_el1_interrupt = GUEST_TIMER_PHYS_NS_PPI; gtdt->non_secure_el1_flags = (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE) |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY); gtdt->virtual_timer_interrupt = GUEST_TIMER_VIRT_PPI; gtdt->virtual_timer_flags = (ACPI_LEVEL_SENSITIVE << ACPI_GTDT_INTERRUPT_MODE) |(ACPI_ACTIVE_LOW << ACPI_GTDT_INTERRUPT_POLARITY); gtdt->counter_block_addresss = ~((uint64_t)0); gtdt->counter_read_block_address = ~((uint64_t)0); make_acpi_header(>dt->header, "GTDT", acpitables[GTDT].size, 2); calculate_checksum(gtdt, offsetof(struct acpi_table_header, checksum), acpitables[GTDT].size); } static void make_acpi_madt_gicc(void *table, int nr_cpus, uint64_t gicc_base) { int i; struct acpi_madt_generic_interrupt *gicc = table; for (i = 0; i < nr_cpus; i++) { gicc->header.type = ACPI_MADT_TYPE_GENERIC_INTERRUPT; gicc->header.length = ACPI_MADT_GICC_SIZE_v5; gicc->base_address = gicc_base; gicc->cpu_interface_number = i; gicc->arm_mpidr = libxl__compute_mpdir(i); gicc->uid = i; gicc->flags = ACPI_MADT_ENABLED; gicc = table + ACPI_MADT_GICC_SIZE_v5; } } static void make_acpi_madt_gicd(void *table, uint64_t gicd_base, uint8_t gic_version) { struct acpi_madt_generic_distributor *gicd = table; gicd->header.type = ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR; gicd->header.length = sizeof(*gicd); gicd->base_address = gicd_base; /* This version field has no meaning before ACPI 5.1 errata. */ gicd->version = gic_version; } static void make_acpi_madt_gicr(void *table, uint64_t gicr_base, uint64_t gicr_size) { struct acpi_madt_generic_redistributor *gicr = table; gicr->header.type = ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR; gicr->header.length = sizeof(*gicr); gicr->base_address = gicr_base; gicr->length = gicr_size; } static int make_acpi_madt(libxl__gc *gc, struct xc_dom_image *dom, libxl_domain_build_info *info, struct acpitable acpitables[]) { uint64_t offset = acpitables[MADT].addr - GUEST_ACPI_BASE; void *table = dom->acpi_modules[0].data + offset; struct acpi_table_madt *madt = table; int rc = 0; switch (info->arch_arm.gic_version) { case LIBXL_GIC_VERSION_V2: table += sizeof(struct acpi_table_madt); make_acpi_madt_gicc(table, info->max_vcpus, GUEST_GICC_BASE); table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus; make_acpi_madt_gicd(table, GUEST_GICD_BASE, ACPI_MADT_GIC_VERSION_V2); break; case LIBXL_GIC_VERSION_V3: table += sizeof(struct acpi_table_madt); make_acpi_madt_gicc(table, info->max_vcpus, 0); table += ACPI_MADT_GICC_SIZE_v5 * info->max_vcpus; make_acpi_madt_gicd(table, GUEST_GICV3_GICD_BASE, ACPI_MADT_GIC_VERSION_V3); table += sizeof(struct acpi_madt_generic_distributor); make_acpi_madt_gicr(table, GUEST_GICV3_GICR0_BASE, GUEST_GICV3_GICR0_SIZE); break; default: LOG(ERROR, "Unknown GIC version"); rc = ERROR_FAIL; goto out; } make_acpi_header(&madt->header, "APIC", acpitables[MADT].size, 3); calculate_checksum(madt, offsetof(struct acpi_table_header, checksum), acpitables[MADT].size); out: return rc; } static void make_acpi_fadt(libxl__gc *gc, struct xc_dom_image *dom, struct acpitable acpitables[]) { uint64_t offset = acpitables[FADT].addr - GUEST_ACPI_BASE; struct acpi_table_fadt *fadt = (void *)dom->acpi_modules[0].data + offset; /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */ fadt->flags = ACPI_FADT_HW_REDUCED; fadt->arm_boot_flags = ACPI_FADT_PSCI_COMPLIANT | ACPI_FADT_PSCI_USE_HVC; /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */ fadt->minor_revision = 0x1; fadt->dsdt = acpitables[DSDT].addr; make_acpi_header(&fadt->header, "FACP", acpitables[FADT].size, 5); calculate_checksum(fadt, offsetof(struct acpi_table_header, checksum), acpitables[FADT].size); } static void make_acpi_dsdt(libxl__gc *gc, struct xc_dom_image *dom, struct acpitable acpitables[]) { uint64_t offset = acpitables[DSDT].addr - GUEST_ACPI_BASE; void *dsdt = dom->acpi_modules[0].data + offset; memcpy(dsdt, dsdt_anycpu_arm, dsdt_anycpu_arm_len); } int libxl__prepare_acpi(libxl__gc *gc, libxl_domain_build_info *info, struct xc_dom_image *dom) { const libxl_version_info *vers; int rc = 0; struct acpitable acpitables[MAX_TABLE_NUMS]; vers = libxl_get_version_info(CTX); if (vers == NULL) { rc = ERROR_FAIL; goto out; } LOG(DEBUG, "constructing ACPI tables for Xen version %d.%d guest", vers->xen_version_major, vers->xen_version_minor); dom->acpi_modules[0].data = NULL; dom->acpi_modules[0].length = 0; dom->acpi_modules[0].guest_addr_out = GUEST_ACPI_BASE; rc = libxl__allocate_acpi_tables(gc, info, dom, acpitables); if (rc) goto out; make_acpi_rsdp(gc, dom, acpitables); make_acpi_xsdt(gc, dom, acpitables); make_acpi_gtdt(gc, dom, acpitables); rc = make_acpi_madt(gc, dom, info, acpitables); if (rc) goto out; make_acpi_fadt(gc, dom, acpitables); make_acpi_dsdt(gc, dom, acpitables); out: return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxlu_disk_l.h0000664000175000017500000002150013256712137015722 0ustar smbsmb#ifndef xlu__disk_yyHEADER_H #define xlu__disk_yyHEADER_H 1 #define xlu__disk_yyIN_HEADER 1 #line 6 "libxlu_disk_l.h" #line 31 "libxlu_disk_l.l" #include "libxl_osdeps.h" /* must come before any other headers */ #line 12 "libxlu_disk_l.h" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 39 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ void xlu__disk_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void xlu__disk_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__disk_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void xlu__disk_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void xlu__disk_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void xlu__disk_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void xlu__disk_yypop_buffer_state (yyscan_t yyscanner ); YY_BUFFER_STATE xlu__disk_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__disk_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__disk_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); void *xlu__disk_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *xlu__disk_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void xlu__disk_yyfree (void * ,yyscan_t yyscanner ); #define xlu__disk_yywrap(yyscanner) 1 #define YY_SKIP_YYWRAP #define yytext_ptr yytext_r #ifdef YY_HEADER_EXPORT_START_CONDITIONS #define INITIAL 0 #define LEXERR 1 #endif #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif int xlu__disk_yylex_init (yyscan_t* scanner); int xlu__disk_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int xlu__disk_yylex_destroy (yyscan_t yyscanner ); int xlu__disk_yyget_debug (yyscan_t yyscanner ); void xlu__disk_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE xlu__disk_yyget_extra (yyscan_t yyscanner ); void xlu__disk_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *xlu__disk_yyget_in (yyscan_t yyscanner ); void xlu__disk_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *xlu__disk_yyget_out (yyscan_t yyscanner ); void xlu__disk_yyset_out (FILE * out_str ,yyscan_t yyscanner ); yy_size_t xlu__disk_yyget_leng (yyscan_t yyscanner ); char *xlu__disk_yyget_text (yyscan_t yyscanner ); int xlu__disk_yyget_lineno (yyscan_t yyscanner ); void xlu__disk_yyset_lineno (int line_number ,yyscan_t yyscanner ); int xlu__disk_yyget_column (yyscan_t yyscanner ); void xlu__disk_yyset_column (int column_no ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int xlu__disk_yywrap (yyscan_t yyscanner ); #else extern int xlu__disk_yywrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int xlu__disk_yylex (yyscan_t yyscanner); #define YY_DECL int xlu__disk_yylex (yyscan_t yyscanner) #endif /* !YY_DECL */ /* yy_get_previous_state - get the state just before the EOB char was reached */ #undef YY_NEW_FILE #undef YY_FLUSH_BUFFER #undef yy_set_bol #undef yy_new_buffer #undef yy_set_interactive #undef YY_DO_BEFORE_ACTION #ifdef YY_DECL_IS_OURS #undef YY_DECL_IS_OURS #undef YY_DECL #endif #line 278 "libxlu_disk_l.l" #line 354 "libxlu_disk_l.h" #undef xlu__disk_yyIN_HEADER #endif /* xlu__disk_yyHEADER_H */ xen-4.9.2/tools/libxl/libxlu_cfg_y.c0000664000175000017500000014647713256712137015564 0ustar smbsmb/* A Bison parser, made by GNU Bison 3.0.2. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "3.0.2" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Substitute the variable and function names. */ #define yyparse xlu__cfg_yyparse #define yylex xlu__cfg_yylex #define yyerror xlu__cfg_yyerror #define yydebug xlu__cfg_yydebug #define yynerrs xlu__cfg_yynerrs /* Copy the first part of user declarations. */ #line 19 "libxlu_cfg_y.y" /* yacc.c:339 */ #define ctx_scanner ctx->scanner #include "libxlu_cfg_i.h" #include "libxlu_cfg_l.h" #line 78 "libxlu_cfg_y.c" /* yacc.c:339 */ # ifndef YY_NULLPTR # if defined __cplusplus && 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 1 #endif /* In a future release of Bison, this section will be replaced by #include "libxlu_cfg_y.h". */ #ifndef YY_XLU_CFG_YY_LIBXLU_CFG_Y_H_INCLUDED # define YY_XLU_CFG_YY_LIBXLU_CFG_Y_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int xlu__cfg_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { IDENT = 258, STRING = 259, NUMBER = 260, NEWLINE = 261 }; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE YYSTYPE; union YYSTYPE { #line 25 "libxlu_cfg_y.y" /* yacc.c:355 */ char *string; XLU_ConfigValue *value; #line 130 "libxlu_cfg_y.c" /* yacc.c:355 */ }; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif /* Location type. */ #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED typedef struct YYLTYPE YYLTYPE; struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; }; # define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_TRIVIAL 1 #endif int xlu__cfg_yyparse (CfgParseContext *ctx); #endif /* !YY_XLU_CFG_YY_LIBXLU_CFG_Y_H_INCLUDED */ /* Copy the second part of user declarations. */ #line 158 "libxlu_cfg_y.c" /* yacc.c:358 */ #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif #ifndef YY_ATTRIBUTE # if (defined __GNUC__ \ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C # define YY_ATTRIBUTE(Spec) __attribute__(Spec) # else # define YY_ATTRIBUTE(Spec) /* empty */ # endif #endif #ifndef YY_ATTRIBUTE_PURE # define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) #endif #ifndef YY_ATTRIBUTE_UNUSED # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) #endif #if !defined _Noreturn \ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) # if defined _MSC_VER && 1200 <= _MSC_VER # define _Noreturn __declspec (noreturn) # else # define _Noreturn YY_ATTRIBUTE ((__noreturn__)) # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(E) ((void) (E)) #else # define YYUSE(E) /* empty */ #endif #if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; YYLTYPE yyls_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + 2 * YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 3 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 25 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 12 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 11 /* YYNRULES -- Number of rules. */ #define YYNRULES 22 /* YYNSTATES -- Number of states. */ #define YYNSTATES 30 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 261 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex, without out-of-bounds checking. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 2, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 2, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint8 yyrline[] = { 0, 47, 47, 48, 50, 51, 53, 54, 55, 57, 59, 60, 62, 63, 65, 66, 68, 69, 70, 72, 73, 75, 77 }; #endif #if YYDEBUG || YYERROR_VERBOSE || 1 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "IDENT", "STRING", "NUMBER", "NEWLINE", "'='", "';'", "'['", "']'", "','", "$accept", "file", "stmts", "stmt", "assignment", "endstmt", "value", "atom", "valuelist", "values", "nlok", YY_NULLPTR }; #endif # ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 61, 59, 91, 93, 44 }; # endif #define YYPACT_NINF -18 #define yypact_value_is_default(Yystate) \ (!!((Yystate) == (-18))) #define YYTABLE_NINF -3 #define yytable_value_is_error(Yytable_value) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int8 yypact[] = { -18, 4, 0, -18, -1, 6, -18, -18, -18, 3, -18, -18, 14, -18, -18, -18, -18, -18, -18, 11, -18, -18, 12, 10, 18, -18, -18, 11, -18, 18 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 4, 0, 0, 1, 0, 0, 10, 11, 5, 3, 7, 8, 0, 6, 14, 15, 21, 9, 12, 16, 22, 21, 0, 17, 19, 13, 21, 18, 21, 20 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -18, -18, -18, -18, -18, 16, -17, -18, -18, -18, -14 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { -1, 1, 2, 8, 9, 10, 17, 18, 22, 23, 19 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int8 yytable[] = { -2, 4, 21, 5, 3, 11, 6, 24, 7, 6, 28, 7, 27, 12, 29, 14, 15, 20, 14, 15, 16, 26, 25, 16, 20, 13 }; static const yytype_uint8 yycheck[] = { 0, 1, 19, 3, 0, 6, 6, 21, 8, 6, 27, 8, 26, 7, 28, 4, 5, 6, 4, 5, 9, 11, 10, 9, 6, 9 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 13, 14, 0, 1, 3, 6, 8, 15, 16, 17, 6, 7, 17, 4, 5, 9, 18, 19, 22, 6, 18, 20, 21, 22, 10, 11, 22, 18, 22 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 12, 13, 13, 14, 14, 15, 15, 15, 16, 17, 17, 18, 18, 19, 19, 20, 20, 20, 21, 21, 22, 22 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 2, 0, 2, 2, 1, 2, 3, 1, 1, 1, 4, 1, 1, 0, 1, 3, 2, 5, 0, 2 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (&yylloc, ctx, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (N) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (0) #endif #define YYRHSLOC(Rhs, K) ((Rhs)[K]) /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL /* Print *YYLOCP on YYO. Private, do not rely on its existence. */ YY_ATTRIBUTE_UNUSED static unsigned yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) { unsigned res = 0; int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; if (0 <= yylocp->first_line) { res += YYFPRINTF (yyo, "%d", yylocp->first_line); if (0 <= yylocp->first_column) res += YYFPRINTF (yyo, ".%d", yylocp->first_column); } if (0 <= yylocp->last_line) { if (yylocp->first_line < yylocp->last_line) { res += YYFPRINTF (yyo, "-%d", yylocp->last_line); if (0 <= end_col) res += YYFPRINTF (yyo, ".%d", end_col); } else if (0 <= end_col && yylocp->first_column < end_col) res += YYFPRINTF (yyo, "-%d", end_col); } return res; } # define YY_LOCATION_PRINT(File, Loc) \ yy_location_print_ (File, &(Loc)) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, Location, ctx); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*----------------------------------------. | Print this symbol's value on YYOUTPUT. | `----------------------------------------*/ static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, CfgParseContext *ctx) { FILE *yyo = yyoutput; YYUSE (yyo); YYUSE (yylocationp); YYUSE (ctx); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # endif YYUSE (yytype); } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, CfgParseContext *ctx) { YYFPRINTF (yyoutput, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); YY_LOCATION_PRINT (yyoutput, *yylocationp); YYFPRINTF (yyoutput, ": "); yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, ctx); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, CfgParseContext *ctx) { unsigned long int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yystos[yyssp[yyi + 1 - yynrhs]], &(yyvsp[(yyi + 1) - (yynrhs)]) , &(yylsp[(yyi + 1) - (yynrhs)]) , ctx); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, yylsp, Rule, ctx); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ static YYSIZE_T yystrlen (const char *yystr) { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error (yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } } } } switch (yycount) { # define YYCASE_(N, S) \ case N: \ yyformat = S; \ break YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); # undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen (yyformat); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, CfgParseContext *ctx) { YYUSE (yyvaluep); YYUSE (yylocationp); YYUSE (ctx); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yytype) { case 3: /* IDENT */ #line 40 "libxlu_cfg_y.y" /* yacc.c:1257 */ { free(((*yyvaluep).string)); } #line 1064 "libxlu_cfg_y.c" /* yacc.c:1257 */ break; case 4: /* STRING */ #line 40 "libxlu_cfg_y.y" /* yacc.c:1257 */ { free(((*yyvaluep).string)); } #line 1070 "libxlu_cfg_y.c" /* yacc.c:1257 */ break; case 5: /* NUMBER */ #line 40 "libxlu_cfg_y.y" /* yacc.c:1257 */ { free(((*yyvaluep).string)); } #line 1076 "libxlu_cfg_y.c" /* yacc.c:1257 */ break; case 18: /* value */ #line 43 "libxlu_cfg_y.y" /* yacc.c:1257 */ { xlu__cfg_value_free(((*yyvaluep).value)); } #line 1082 "libxlu_cfg_y.c" /* yacc.c:1257 */ break; case 19: /* atom */ #line 40 "libxlu_cfg_y.y" /* yacc.c:1257 */ { free(((*yyvaluep).string)); } #line 1088 "libxlu_cfg_y.c" /* yacc.c:1257 */ break; case 20: /* valuelist */ #line 43 "libxlu_cfg_y.y" /* yacc.c:1257 */ { xlu__cfg_value_free(((*yyvaluep).value)); } #line 1094 "libxlu_cfg_y.c" /* yacc.c:1257 */ break; case 21: /* values */ #line 43 "libxlu_cfg_y.y" /* yacc.c:1257 */ { xlu__cfg_value_free(((*yyvaluep).value)); } #line 1100 "libxlu_cfg_y.c" /* yacc.c:1257 */ break; default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } /*----------. | yyparse. | `----------*/ int yyparse (CfgParseContext *ctx) { /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ /* Default value used for initialization, for pacifying older GCCs or non-GCC compilers. */ YY_INITIAL_VALUE (static YYSTYPE yyval_default;) YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); /* Location data for the lookahead symbol. */ static YYLTYPE yyloc_default # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL = { 1, 1, 1, 1 } # endif ; YYLTYPE yylloc = yyloc_default; /* Number of syntax errors so far. */ int yynerrs; int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: 'yyss': related to states. 'yyvs': related to semantic values. 'yyls': related to locations. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; /* The location stack. */ YYLTYPE yylsa[YYINITDEPTH]; YYLTYPE *yyls; YYLTYPE *yylsp; /* The locations where the error started and ended. */ YYLTYPE yyerror_range[3]; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yylsp = yyls = yylsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ yylsp[0] = yylloc; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; YYLTYPE *yyls1 = yyls; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yyls1, yysize * sizeof (*yylsp), &yystacksize); yyls = yyls1; yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); YYSTACK_RELOCATE (yyls_alloc, yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; yylsp = yyls + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = yylex (&yylval, &yylloc, ctx_scanner); } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END *++yylsp = yylloc; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; /* Default location. */ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); YY_REDUCE_PRINT (yyn); switch (yyn) { case 9: #line 57 "libxlu_cfg_y.y" /* yacc.c:1646 */ { xlu__cfg_set_store(ctx,(yyvsp[-2].string),(yyvsp[0].value),(yylsp[0]).first_line); } #line 1394 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; case 12: #line 62 "libxlu_cfg_y.y" /* yacc.c:1646 */ { (yyval.value)= xlu__cfg_string_mk(ctx,(yyvsp[0].string),&(yylsp[0])); } #line 1400 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; case 13: #line 63 "libxlu_cfg_y.y" /* yacc.c:1646 */ { (yyval.value)= (yyvsp[-1].value); } #line 1406 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; case 14: #line 65 "libxlu_cfg_y.y" /* yacc.c:1646 */ { (yyval.string)= (yyvsp[0].string); } #line 1412 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; case 15: #line 66 "libxlu_cfg_y.y" /* yacc.c:1646 */ { (yyval.string)= (yyvsp[0].string); } #line 1418 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; case 16: #line 68 "libxlu_cfg_y.y" /* yacc.c:1646 */ { (yyval.value)= xlu__cfg_list_mk(ctx,NULL,&yylloc); } #line 1424 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; case 17: #line 69 "libxlu_cfg_y.y" /* yacc.c:1646 */ { (yyval.value)= (yyvsp[0].value); } #line 1430 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; case 18: #line 70 "libxlu_cfg_y.y" /* yacc.c:1646 */ { (yyval.value)= (yyvsp[-2].value); } #line 1436 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; case 19: #line 72 "libxlu_cfg_y.y" /* yacc.c:1646 */ { (yyval.value)= xlu__cfg_list_mk(ctx,(yyvsp[-1].value),&(yylsp[-1])); } #line 1442 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; case 20: #line 73 "libxlu_cfg_y.y" /* yacc.c:1646 */ { xlu__cfg_list_append(ctx,(yyvsp[-4].value),(yyvsp[-1].value)); (yyval.value)= (yyvsp[-4].value); } #line 1448 "libxlu_cfg_y.c" /* yacc.c:1646 */ break; #line 1452 "libxlu_cfg_y.c" /* yacc.c:1646 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; *++yylsp = yyloc; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (&yylloc, ctx, YY_("syntax error")); #else # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror (&yylloc, ctx, yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } # undef YYSYNTAX_ERROR #endif } yyerror_range[1] = yylloc; if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, &yylloc, ctx); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; yyerror_range[1] = yylsp[1-yylen]; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yyerror_range[1] = *yylsp; yydestruct ("Error: popping", yystos[yystate], yyvsp, yylsp, ctx); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END yyerror_range[2] = yylloc; /* Using YYLLOC is tempting, but would change the location of the lookahead. YYLOC is available though. */ YYLLOC_DEFAULT (yyloc, yyerror_range, 2); *++yylsp = yyloc; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (&yylloc, ctx, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, ctx); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, yylsp, ctx); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif return yyresult; } xen-4.9.2/tools/libxl/libxl_domain.c0000664000175000017500000014356713256712137015554 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" #define PAGE_TO_MEMKB(pages) ((pages) * 4) int libxl__domain_rename(libxl__gc *gc, uint32_t domid, const char *old_name, const char *new_name, xs_transaction_t trans) { libxl_ctx *ctx = libxl__gc_owner(gc); char *dom_path = 0; const char *name_path; char *got_old_name; unsigned int got_old_len; xs_transaction_t our_trans = 0; uint32_t stub_dm_domid; const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL; int rc; libxl_dominfo info; char *uuid; const char *vm_name_path; libxl_dominfo_init(&info); dom_path = libxl__xs_get_dompath(gc, domid); if (!dom_path) goto x_nomem; name_path= GCSPRINTF("%s/name", dom_path); if (!name_path) goto x_nomem; stub_dm_domid = libxl_get_stubdom_id(CTX, domid); if (stub_dm_domid) { stub_dm_old_name = libxl__stub_dm_name(gc, old_name); stub_dm_new_name = libxl__stub_dm_name(gc, new_name); } retry_transaction: if (!trans) { trans = our_trans = xs_transaction_start(ctx->xsh); if (!our_trans) { LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name"); goto x_fail; } } if (!new_name) { LOGD(ERROR, domid, "New domain name not specified"); rc = ERROR_INVAL; goto x_rc; } if (new_name[0]) { /* nonempty names must be unique */ uint32_t domid_e; rc = libxl_name_to_domid(ctx, new_name, &domid_e); if (rc == ERROR_INVAL) { /* no such domain, good */ } else if (rc != 0) { LOGD(ERROR, domid, "Unexpected error checking for existing domain"); goto x_rc; } else if (domid_e == domid) { /* domain already has this name, ok (but we do still * need the rest of the code as we may need to check * old_name, for example). */ } else { LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name); rc = ERROR_INVAL; goto x_rc; } } if (old_name) { got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len); if (!got_old_name) { LOGEVD(ERROR, errno, domid, "Check old name for domain allegedly named `%s'", old_name); goto x_fail; } if (strcmp(old_name, got_old_name)) { LOGD(ERROR, domid, "Allegedly named `%s' is actually named `%s' - racing ?", old_name, got_old_name); free(got_old_name); goto x_fail; } free(got_old_name); } if (!xs_write(ctx->xsh, trans, name_path, new_name, strlen(new_name))) { LOGD(ERROR, domid, "Failed to write new name `%s'" " for domain previously named `%s'", new_name, old_name); goto x_fail; } /* update /vm//name */ rc = libxl_domain_info(ctx, &info, domid); if (rc) goto x_rc; uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid)); vm_name_path = GCSPRINTF("/vm/%s/name", uuid); if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name)) goto x_fail; if (stub_dm_domid) { rc = libxl__domain_rename(gc, stub_dm_domid, stub_dm_old_name, stub_dm_new_name, trans); if (rc) { LOGED(ERROR, domid, "Unable to rename stub-domain"); goto x_rc; } } if (our_trans) { if (!xs_transaction_end(ctx->xsh, our_trans, 0)) { trans = our_trans = 0; if (errno != EAGAIN) { LOGD(ERROR, domid, "Failed to commit new name `%s'" " for domain previously named `%s'", new_name, old_name); goto x_fail; } LOGD(DEBUG, domid, "Need to retry rename transaction" " for domain (name_path=\"%s\", new_name=\"%s\")", name_path, new_name); goto retry_transaction; } our_trans = 0; } rc = 0; x_rc: if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1); libxl_dominfo_dispose(&info); return rc; x_fail: rc = ERROR_FAIL; goto x_rc; x_nomem: rc = ERROR_NOMEM; goto x_rc; } int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid, const char *old_name, const char *new_name) { GC_INIT(ctx); int rc; rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL); GC_FREE; return rc; } int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int rc = libxl__domain_resume(gc, domid, suspend_cancel); libxl__ao_complete(egc, ao, rc); return AO_INPROGRESS; } /* * Preserves a domain but rewrites xenstore etc to make it unique so * that the domain can be restarted. * * Does not modify info so that it may be reused. */ int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid, libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid) { GC_INIT(ctx); struct xs_permissions roperm[2]; xs_transaction_t t; char *preserved_name; char *uuid_string; char *vm_path; char *dom_path; int rc; preserved_name = GCSPRINTF("%s%s", info->name, name_suffix); if (!preserved_name) { GC_FREE; return ERROR_NOMEM; } uuid_string = libxl__uuid2string(gc, new_uuid); if (!uuid_string) { GC_FREE; return ERROR_NOMEM; } dom_path = libxl__xs_get_dompath(gc, domid); if (!dom_path) { GC_FREE; return ERROR_FAIL; } vm_path = GCSPRINTF("/vm/%s", uuid_string); if (!vm_path) { GC_FREE; return ERROR_FAIL; } roperm[0].id = 0; roperm[0].perms = XS_PERM_NONE; roperm[1].id = domid; roperm[1].perms = XS_PERM_READ; retry_transaction: t = xs_transaction_start(ctx->xsh); xs_rm(ctx->xsh, t, vm_path); xs_mkdir(ctx->xsh, t, vm_path); xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm)); xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path)); rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t); if (rc) { GC_FREE; return rc; } xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string)); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; GC_FREE; return 0; } void libxl__xcinfo2xlinfo(libxl_ctx *ctx, const xc_domaininfo_t *xcinfo, libxl_dominfo *xlinfo) { size_t size; memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t)); xlinfo->domid = xcinfo->domain; xlinfo->ssidref = xcinfo->ssidref; if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref, &xlinfo->ssid_label, &size) < 0) xlinfo->ssid_label = NULL; xlinfo->dying = !!(xcinfo->flags&XEN_DOMINF_dying); xlinfo->shutdown = !!(xcinfo->flags&XEN_DOMINF_shutdown); xlinfo->paused = !!(xcinfo->flags&XEN_DOMINF_paused); xlinfo->blocked = !!(xcinfo->flags&XEN_DOMINF_blocked); xlinfo->running = !!(xcinfo->flags&XEN_DOMINF_running); xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain); if (xlinfo->shutdown) xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask; else xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN; xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages); xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages); xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages); xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages); xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages); xlinfo->cpu_time = xcinfo->cpu_time; xlinfo->vcpu_max_id = xcinfo->max_vcpu_id; xlinfo->vcpu_online = xcinfo->nr_online_vcpus; xlinfo->cpupool = xcinfo->cpupool; xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ? LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV; } libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out) { libxl_dominfo *ptr = NULL; int i, ret; xc_domaininfo_t info[1024]; int size = 0; uint32_t domid = 0; GC_INIT(ctx); while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) { ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo)); for (i = 0; i < ret; i++) { libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]); } domid = info[ret - 1].domain + 1; size += ret; } if (ret < 0) { LOGE(ERROR, "getting domain info list"); free(ptr); GC_FREE; return NULL; } *nb_domain_out = size; GC_FREE; return ptr; } int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r, uint32_t domid) { xc_domaininfo_t xcinfo; int ret; GC_INIT(ctx); ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo); if (ret<0) { LOGED(ERROR, domid, "Getting domain info list"); GC_FREE; return ERROR_FAIL; } if (ret==0 || xcinfo.domain != domid) { GC_FREE; return ERROR_DOMAIN_NOTFOUND; } if (info_r) libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r); GC_FREE; return 0; } /* this API call only list VM running on this host. A VM can * be an aggregate of multiple domains. */ libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out) { GC_INIT(ctx); libxl_dominfo *info; libxl_vminfo *ptr = NULL; int idx, i, n_doms; info = libxl_list_domain(ctx, &n_doms); if (!info) goto out; /* * Always make sure to allocate at least one element; if we don't and we * request zero, libxl__calloc (might) think its internal call to calloc * has failed (if it returns null), if so it would kill our process. */ ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo)); for (idx = i = 0; i < n_doms; i++) { if (libxl_is_stubdom(ctx, info[i].domid, NULL)) continue; ptr[idx].uuid = info[i].uuid; ptr[idx].domid = info[i].domid; idx++; } *nb_vm_out = idx; libxl_dominfo_list_free(info, n_doms); out: GC_FREE; return ptr; } static void remus_failover_cb(libxl__egc *egc, libxl__domain_save_state *dss, int rc); int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info, uint32_t domid, int send_fd, int recv_fd, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); libxl__domain_save_state *dss; int rc; libxl_domain_type type = libxl__domain_type(gc, domid); if (type == LIBXL_DOMAIN_TYPE_INVALID) { rc = ERROR_FAIL; goto out; } /* The caller must set this defbool */ if (libxl_defbool_is_default(info->colo)) { LOGD(ERROR, domid, "Colo mode must be enabled/disabled"); rc = ERROR_FAIL; goto out; } libxl_defbool_setdefault(&info->allow_unsafe, false); libxl_defbool_setdefault(&info->blackhole, false); libxl_defbool_setdefault(&info->compression, !libxl_defbool_val(info->colo)); libxl_defbool_setdefault(&info->netbuf, true); libxl_defbool_setdefault(&info->diskbuf, true); if (libxl_defbool_val(info->colo) && libxl_defbool_val(info->compression)) { LOGD(ERROR, domid, "Cannot use memory checkpoint " "compression in COLO mode"); rc = ERROR_FAIL; goto out; } if (!libxl_defbool_val(info->allow_unsafe) && (libxl_defbool_val(info->blackhole) || !libxl_defbool_val(info->netbuf) || !libxl_defbool_val(info->diskbuf))) { LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null," "disable network buffering and disk replication"); rc = ERROR_FAIL; goto out; } GCNEW(dss); dss->ao = ao; dss->callback = remus_failover_cb; dss->domid = domid; dss->fd = send_fd; dss->recv_fd = recv_fd; dss->type = type; dss->live = 1; dss->debug = 0; dss->remus = info; if (libxl_defbool_val(info->colo)) dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO; else dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS; assert(info); /* Point of no return */ if (libxl_defbool_val(info->colo)) libxl__colo_save_setup(egc, &dss->css); else libxl__remus_setup(egc, &dss->rs); return AO_INPROGRESS; out: return AO_CREATE_FAIL(rc); } static void remus_failover_cb(libxl__egc *egc, libxl__domain_save_state *dss, int rc) { STATE_AO_GC(dss->ao); /* * With Remus, if we reach this point, it means either * backup died or some network error occurred preventing us * from sending checkpoints. */ libxl__ao_complete(egc, ao, rc); } static void domain_suspend_cb(libxl__egc *egc, libxl__domain_save_state *dss, int rc) { STATE_AO_GC(dss->ao); int flrc; flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl); /* If suspend has failed already then report that error not this one. */ if (flrc && !rc) rc = flrc; libxl__ao_complete(egc,ao,rc); } int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int rc; libxl_domain_type type = libxl__domain_type(gc, domid); if (type == LIBXL_DOMAIN_TYPE_INVALID) { rc = ERROR_FAIL; goto out_err; } libxl__domain_save_state *dss; GCNEW(dss); dss->ao = ao; dss->callback = domain_suspend_cb; dss->domid = domid; dss->fd = fd; dss->type = type; dss->live = flags & LIBXL_SUSPEND_LIVE; dss->debug = flags & LIBXL_SUSPEND_DEBUG; dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE; rc = libxl__fd_flags_modify_save(gc, dss->fd, ~(O_NONBLOCK|O_NDELAY), 0, &dss->fdfl); if (rc < 0) goto out_err; libxl__domain_save(egc, dss); return AO_INPROGRESS; out_err: return AO_CREATE_FAIL(rc); } int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid) { int ret; GC_INIT(ctx); ret = xc_domain_pause(ctx->xch, domid); if (ret<0) { LOGED(ERROR, domid, "Pausing domain"); GC_FREE; return ERROR_FAIL; } GC_FREE; return 0; } int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid, const char *filename, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); int ret, rc; ret = xc_domain_dumpcore(ctx->xch, domid, filename); if (ret<0) { LOGED(ERROR, domid, "Core dumping domain to %s", filename); rc = ERROR_FAIL; goto out; } rc = 0; out: libxl__ao_complete(egc, ao, rc); return AO_INPROGRESS; } int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid) { GC_INIT(ctx); int ret, rc = 0; libxl_domain_type type = libxl__domain_type(gc, domid); if (type == LIBXL_DOMAIN_TYPE_INVALID) { rc = ERROR_FAIL; goto out; } if (type == LIBXL_DOMAIN_TYPE_HVM) { if (libxl__device_model_version_running(gc, domid) != LIBXL_DEVICE_MODEL_VERSION_NONE) { rc = libxl__domain_resume_device_model(gc, domid); if (rc < 0) { LOGD(ERROR, domid, "Failed to unpause device model for domain:%d", rc); goto out; } } } ret = xc_domain_unpause(ctx->xch, domid); if (ret<0) { LOGED(ERROR, domid, "Unpausing domain"); rc = ERROR_FAIL; } out: GC_FREE; return rc; } int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid) { libxl_ctx *ctx = libxl__gc_owner(gc); uint64_t pvdriver = 0; int ret; libxl_domain_type domtype = libxl__domain_type(gc, domid); if (domtype == LIBXL_DOMAIN_TYPE_INVALID) return ERROR_FAIL; if (domtype == LIBXL_DOMAIN_TYPE_PV) return 1; ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver); if (ret<0) { LOGED(ERROR, domid, "Getting HVM callback IRQ"); return ERROR_FAIL; } return !!pvdriver; } const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid) { const char *dom_path; dom_path = libxl__xs_get_dompath(gc, domid); if (!dom_path) return NULL; return GCSPRINTF("%s/control/shutdown", dom_path); } char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t, uint32_t domid) { const char *shutdown_path; shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid); if (!shutdown_path) return NULL; return libxl__xs_read(gc, t, shutdown_path); } int libxl__domain_pvcontrol_write(libxl__gc *gc, xs_transaction_t t, uint32_t domid, const char *cmd) { const char *shutdown_path; shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid); if (!shutdown_path) return ERROR_FAIL; return libxl__xs_printf(gc, t, shutdown_path, "%s", cmd); } static int libxl__domain_pvcontrol(libxl__gc *gc, uint32_t domid, const char *cmd) { int ret; ret = libxl__domain_pvcontrol_available(gc, domid); if (ret < 0) return ret; if (!ret) return ERROR_NOPARAVIRT; return libxl__domain_pvcontrol_write(gc, XBT_NULL, domid, cmd); } int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid) { GC_INIT(ctx); int ret; ret = libxl__domain_pvcontrol(gc, domid, "poweroff"); GC_FREE; return ret; } int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid) { GC_INIT(ctx); int ret; ret = libxl__domain_pvcontrol(gc, domid, "reboot"); GC_FREE; return ret; } static void domain_death_occurred(libxl__egc *egc, libxl_evgen_domain_death **evg_upd, const char *why) { /* Removes **evg_upd from death_list and puts it on death_reported * and advances *evg_upd to the next entry. * Call sites in domain_death_xswatch_callback must use "continue". */ EGC_GC; libxl_evgen_domain_death *const evg = *evg_upd; LOGD(DEBUG, evg->domid, "%s", why); libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry); *evg_upd = evg_next; libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user); libxl__event_occurred(egc, ev); evg->death_reported = 1; LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry); LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry); } static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w, const char *wpath, const char *epath) { EGC_GC; libxl_evgen_domain_death *evg; int rc; CTX_LOCK; evg = LIBXL_TAILQ_FIRST(&CTX->death_list); for (;;) { if (!evg) goto out; int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1; xc_domaininfo_t domaininfos[nentries]; const xc_domaininfo_t *got = domaininfos, *gotend; rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos); if (rc == -1) { LIBXL__EVENT_DISASTER(egc, "xc_domain_getinfolist failed while" " processing @releaseDomain watch event", errno, 0); goto out; } gotend = &domaininfos[rc]; LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld", evg, nentries, rc, rc>0 ? (long)domaininfos[0].domain : 0, rc>0 ? (long)domaininfos[rc-1].domain : 0); for (;;) { if (!evg) { LOG(DEBUG, "[evg=0] all reported"); goto all_reported; } LOGD(DEBUG, evg->domid, "[evg=%p]" " got=domaininfos[%d] got->domain=%ld", evg, (int)(got - domaininfos), got < gotend ? (long)got->domain : -1L); if (!rc) { domain_death_occurred(egc, &evg, "empty list"); continue; } if (got == gotend) { LOG(DEBUG, " got==gotend"); break; } if (got->domain > evg->domid) { /* ie, the list doesn't contain evg->domid any more so * the domain has been destroyed */ domain_death_occurred(egc, &evg, "missing from list"); continue; } if (got->domain < evg->domid) { got++; continue; } assert(evg->domid == got->domain); LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x", evg->shutdown_reported, got->flags); if (got->flags & XEN_DOMINF_dying) { domain_death_occurred(egc, &evg, "dying"); continue; } if (!evg->shutdown_reported && (got->flags & XEN_DOMINF_shutdown)) { libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN, got->domain, evg->user); LOG(DEBUG, " shutdown reporting"); ev->u.domain_shutdown.shutdown_reason = (got->flags >> XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask; libxl__event_occurred(egc, ev); evg->shutdown_reported = 1; } evg = LIBXL_TAILQ_NEXT(evg, entry); } assert(rc); /* rc==0 results in us eating all evgs and quitting */ } all_reported: out: LOG(DEBUG, "domain death search done"); CTX_UNLOCK; } int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid, libxl_ev_user user, libxl_evgen_domain_death **evgen_out) { GC_INIT(ctx); libxl_evgen_domain_death *evg, *evg_search; int rc; CTX_LOCK; evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; } memset(evg, 0, sizeof(*evg)); evg->domid = domid; evg->user = user; LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, , evg->domid > evg_search->domid); if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) { rc = libxl__ev_xswatch_register(gc, &ctx->death_watch, domain_death_xswatch_callback, "@releaseDomain"); if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; } } *evgen_out = evg; rc = 0; out: CTX_UNLOCK; GC_FREE; return rc; }; void libxl__evdisable_domain_death(libxl__gc *gc, libxl_evgen_domain_death *evg) { CTX_LOCK; if (!evg->death_reported) LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry); else LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry); free(evg); if (!LIBXL_TAILQ_FIRST(&CTX->death_list) && libxl__ev_xswatch_isregistered(&CTX->death_watch)) libxl__ev_xswatch_deregister(gc, &CTX->death_watch); CTX_UNLOCK; } void libxl_evdisable_domain_death(libxl_ctx *ctx, libxl_evgen_domain_death *evg) { GC_INIT(ctx); libxl__evdisable_domain_death(gc, evg); GC_FREE; } /* Callbacks for libxl_domain_destroy */ static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, int rc); int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid, const libxl_asyncop_how *ao_how) { AO_CREATE(ctx, domid, ao_how); libxl__domain_destroy_state *dds; GCNEW(dds); dds->ao = ao; dds->domid = domid; dds->callback = domain_destroy_cb; libxl__domain_destroy(egc, dds); return AO_INPROGRESS; } static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds, int rc) { STATE_AO_GC(dds->ao); if (rc) LOGD(ERROR, dds->domid, "Destruction of domain failed"); libxl__ao_complete(egc, ao, rc); } /* Callbacks for libxl__domain_destroy */ static void stubdom_destroy_callback(libxl__egc *egc, libxl__destroy_domid_state *dis, int rc); static void domain_destroy_callback(libxl__egc *egc, libxl__destroy_domid_state *dis, int rc); static void destroy_finish_check(libxl__egc *egc, libxl__domain_destroy_state *dds); void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds) { STATE_AO_GC(dds->ao); uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid); if (stubdomid) { dds->stubdom.ao = ao; dds->stubdom.domid = stubdomid; dds->stubdom.callback = stubdom_destroy_callback; dds->stubdom.soft_reset = false; libxl__destroy_domid(egc, &dds->stubdom); } else { dds->stubdom_finished = 1; } dds->domain.ao = ao; dds->domain.domid = dds->domid; dds->domain.callback = domain_destroy_callback; dds->domain.soft_reset = dds->soft_reset; libxl__destroy_domid(egc, &dds->domain); } static void stubdom_destroy_callback(libxl__egc *egc, libxl__destroy_domid_state *dis, int rc) { STATE_AO_GC(dis->ao); libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom); const char *savefile; if (rc) { LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u", dis->domid); dds->rc = rc; } dds->stubdom_finished = 1; savefile = libxl__device_model_savefile(gc, dis->domid); rc = libxl__remove_file(gc, savefile); if (rc) { LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s", savefile); } destroy_finish_check(egc, dds); } static void domain_destroy_callback(libxl__egc *egc, libxl__destroy_domid_state *dis, int rc) { STATE_AO_GC(dis->ao); libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain); if (rc) { LOGD(ERROR, dis->domid, "Unable to destroy guest"); dds->rc = rc; } dds->domain_finished = 1; destroy_finish_check(egc, dds); } static void destroy_finish_check(libxl__egc *egc, libxl__domain_destroy_state *dds) { if (!(dds->domain_finished && dds->stubdom_finished)) return; dds->callback(egc, dds, dds->rc); } /* Callbacks for libxl__destroy_domid */ static void devices_destroy_cb(libxl__egc *egc, libxl__devices_remove_state *drs, int rc); static void domain_destroy_domid_cb(libxl__egc *egc, libxl__ev_child *destroyer, pid_t pid, int status); void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis) { STATE_AO_GC(dis->ao); libxl_ctx *ctx = CTX; uint32_t domid = dis->domid; char *dom_path; int rc, dm_present; libxl__ev_child_init(&dis->destroyer); rc = libxl_domain_info(ctx, NULL, domid); switch(rc) { case 0: break; case ERROR_DOMAIN_NOTFOUND: LOGD(ERROR, domid, "Non-existant domain"); default: goto out; } switch (libxl__domain_type(gc, domid)) { case LIBXL_DOMAIN_TYPE_HVM: if (libxl_get_stubdom_id(CTX, domid)) { dm_present = 0; break; } /* fall through */ case LIBXL_DOMAIN_TYPE_PV: dm_present = libxl__dm_active(gc, domid); break; case LIBXL_DOMAIN_TYPE_INVALID: rc = ERROR_FAIL; goto out; default: abort(); } dom_path = libxl__xs_get_dompath(gc, domid); if (!dom_path) { rc = ERROR_FAIL; goto out; } if (libxl__device_pci_destroy_all(gc, domid) < 0) LOGD(ERROR, domid, "Pci shutdown failed"); rc = xc_domain_pause(ctx->xch, domid); if (rc < 0) { LOGEVD(ERROR, rc, domid, "xc_domain_pause failed"); } if (dm_present) { if (libxl__destroy_device_model(gc, domid) < 0) LOGD(ERROR, domid, "libxl__destroy_device_model failed"); libxl__qmp_cleanup(gc, domid); } dis->drs.ao = ao; dis->drs.domid = domid; dis->drs.callback = devices_destroy_cb; dis->drs.force = 1; libxl__devices_destroy(egc, &dis->drs); return; out: assert(rc); dis->callback(egc, dis, rc); return; } static void devices_destroy_cb(libxl__egc *egc, libxl__devices_remove_state *drs, int rc) { STATE_AO_GC(drs->ao); libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs); libxl_ctx *ctx = CTX; uint32_t domid = dis->domid; char *dom_path; char *vm_path; libxl__domain_userdata_lock *lock; dom_path = libxl__xs_get_dompath(gc, domid); if (!dom_path) { rc = ERROR_FAIL; goto out; } if (rc < 0) LOGD(ERROR, domid, "libxl__devices_destroy failed"); vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path)); if (vm_path) if (!xs_rm(ctx->xsh, XBT_NULL, vm_path)) LOGED(ERROR, domid, "xs_rm failed for %s", vm_path); if (!xs_rm(ctx->xsh, XBT_NULL, dom_path)) LOGED(ERROR, domid, "xs_rm failed for %s", dom_path); xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid)); xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid)); /* This is async operation, we already hold CTX lock */ lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } libxl__userdata_destroyall(gc, domid); libxl__unlock_domain_userdata(lock); /* Clean up qemu-save and qemu-resume files. They are * intermediate files created by libxc. Unfortunately they * don't fit in existing userdata scheme very well. In soft reset * case we need to keep the file. */ if (!dis->soft_reset) { rc = libxl__remove_file(gc, libxl__device_model_savefile(gc, domid)); if (rc < 0) goto out; } rc = libxl__remove_file(gc, GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid)); if (rc < 0) goto out; rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb); if (rc < 0) goto out; if (!rc) { /* child */ ctx->xch = xc_interface_open(ctx->lg,0,0); if (!ctx->xch) goto badchild; if (!dis->soft_reset) { rc = xc_domain_destroy(ctx->xch, domid); } else { rc = xc_domain_pause(ctx->xch, domid); if (rc < 0) goto badchild; rc = xc_domain_soft_reset(ctx->xch, domid); if (rc < 0) goto badchild; rc = xc_domain_unpause(ctx->xch, domid); } if (rc < 0) goto badchild; _exit(0); badchild: if (errno > 0 && errno < 126) { _exit(errno); } else { LOGED(ERROR, domid, "xc_domain_destroy failed (with difficult errno value %d)", errno); _exit(-1); } } LOGD(DEBUG, domid, "Forked pid %ld for destroy of domain", (long)rc); return; out: dis->callback(egc, dis, rc); return; } static void domain_destroy_domid_cb(libxl__egc *egc, libxl__ev_child *destroyer, pid_t pid, int status) { libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer); STATE_AO_GC(dis->ao); int rc; if (status) { if (WIFEXITED(status) && WEXITSTATUS(status)<126) { LOGEVD(ERROR, WEXITSTATUS(status), dis->domid, "xc_domain_destroy failed"); } else { libxl_report_child_exitstatus(CTX, XTL_ERROR, "async domain destroy", pid, status); } rc = ERROR_FAIL; goto out; } rc = 0; out: dis->callback(egc, dis, rc); } int libxl__get_domid(libxl__gc *gc, uint32_t *domid) { int rc; const char *xs_domid; rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid); if (rc) goto out; if (!xs_domid) { LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH); rc = ERROR_FAIL; goto out; } *domid = atoi(xs_domid); out: return rc; } int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid) { if (!name) return 0; return libxl_domain_qualifier_to_domid(CTX, name, domid); } libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid, int *nr_vcpus_out, int *nr_cpus_out) { GC_INIT(ctx); libxl_vcpuinfo *ptr, *ret; xc_domaininfo_t domaininfo; xc_vcpuinfo_t vcpuinfo; if (xc_domain_getinfolist(ctx->xch, domid, 1, &domaininfo) != 1) { LOGED(ERROR, domid, "Getting infolist"); GC_FREE; return NULL; } if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) { GC_FREE; return NULL; } *nr_cpus_out = libxl_get_max_cpus(ctx); ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1, sizeof(libxl_vcpuinfo)); for (*nr_vcpus_out = 0; *nr_vcpus_out <= domaininfo.max_vcpu_id; ++*nr_vcpus_out, ++ptr) { libxl_bitmap_init(&ptr->cpumap); if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0)) goto err; libxl_bitmap_init(&ptr->cpumap_soft); if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0)) goto err; if (xc_vcpu_getinfo(ctx->xch, domid, *nr_vcpus_out, &vcpuinfo) == -1) { LOGED(ERROR, domid, "Getting vcpu info"); goto err; } if (xc_vcpu_getaffinity(ctx->xch, domid, *nr_vcpus_out, ptr->cpumap.map, ptr->cpumap_soft.map, XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) { LOGED(ERROR, domid, "Getting vcpu affinity"); goto err; } ptr->vcpuid = *nr_vcpus_out; ptr->cpu = vcpuinfo.cpu; ptr->online = !!vcpuinfo.online; ptr->blocked = !!vcpuinfo.blocked; ptr->running = !!vcpuinfo.running; ptr->vcpu_time = vcpuinfo.cpu_time; } GC_FREE; return ret; err: libxl_bitmap_dispose(&ptr->cpumap); libxl_bitmap_dispose(&ptr->cpumap_soft); free(ret); GC_FREE; return NULL; } static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid, libxl_bitmap *cpumap, const libxl_dominfo *info) { char *dompath; xs_transaction_t t; int i, rc = ERROR_FAIL; if (!(dompath = libxl__xs_get_dompath(gc, domid))) goto out; retry_transaction: t = xs_transaction_start(CTX->xsh); for (i = 0; i <= info->vcpu_max_id; i++) libxl__xs_printf(gc, t, GCSPRINTF("%s/cpu/%u/availability", dompath, i), "%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline"); if (!xs_transaction_end(CTX->xsh, t, 0)) { if (errno == EAGAIN) goto retry_transaction; } else rc = 0; out: return rc; } static int libxl__set_vcpuonline_qmp(libxl__gc *gc, uint32_t domid, libxl_bitmap *cpumap, const libxl_dominfo *info) { int i, rc; libxl_bitmap current_map, final_map; libxl_bitmap_init(¤t_map); libxl_bitmap_init(&final_map); libxl_bitmap_alloc(CTX, ¤t_map, info->vcpu_max_id + 1); libxl_bitmap_set_none(¤t_map); rc = libxl__qmp_query_cpus(gc, domid, ¤t_map); if (rc) { LOGD(ERROR, domid, "Failed to query cpus"); goto out; } libxl_bitmap_copy_alloc(CTX, &final_map, cpumap); libxl_for_each_set_bit(i, current_map) libxl_bitmap_reset(&final_map, i); libxl_for_each_set_bit(i, final_map) { rc = libxl__qmp_cpu_add(gc, domid, i); if (rc) { LOGD(ERROR, domid, "Failed to add cpu %d", i); goto out; } } rc = 0; out: libxl_bitmap_dispose(¤t_map); libxl_bitmap_dispose(&final_map); return rc; } int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid, libxl_bitmap *cpumap) { GC_INIT(ctx); int rc, maxcpus; libxl_dominfo info; libxl_dominfo_init(&info); rc = libxl_domain_info(CTX, &info, domid); if (rc < 0) { LOGED(ERROR, domid, "Getting domain info list"); goto out; } maxcpus = libxl_bitmap_count_set(cpumap); if (maxcpus > info.vcpu_max_id + 1) { LOGED(ERROR, domid, "Requested %d VCPUs, however maxcpus is %d!", maxcpus, info.vcpu_max_id + 1); rc = ERROR_FAIL; goto out; } switch (libxl__domain_type(gc, domid)) { case LIBXL_DOMAIN_TYPE_HVM: switch (libxl__device_model_version_running(gc, domid)) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: case LIBXL_DEVICE_MODEL_VERSION_NONE: break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: rc = libxl__set_vcpuonline_qmp(gc, domid, cpumap, &info); break; default: rc = ERROR_INVAL; } break; case LIBXL_DOMAIN_TYPE_PV: break; default: rc = ERROR_INVAL; } if (!rc) rc = libxl__set_vcpuonline_xenstore(gc, domid, cpumap, &info); out: libxl_dominfo_dispose(&info); GC_FREE; return rc; } static int libxl__domain_s3_resume(libxl__gc *gc, int domid) { int rc = 0; switch (libxl__domain_type(gc, domid)) { case LIBXL_DOMAIN_TYPE_HVM: switch (libxl__device_model_version_running(gc, domid)) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: rc = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0); break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: rc = libxl__qmp_system_wakeup(gc, domid); break; default: rc = ERROR_INVAL; break; } break; default: rc = ERROR_INVAL; break; } return rc; } int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid, libxl_trigger trigger, uint32_t vcpuid) { int rc; GC_INIT(ctx); switch (trigger) { case LIBXL_TRIGGER_POWER: rc = xc_domain_send_trigger(ctx->xch, domid, XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid); break; case LIBXL_TRIGGER_SLEEP: rc = xc_domain_send_trigger(ctx->xch, domid, XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid); break; case LIBXL_TRIGGER_NMI: rc = xc_domain_send_trigger(ctx->xch, domid, XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid); break; case LIBXL_TRIGGER_INIT: rc = xc_domain_send_trigger(ctx->xch, domid, XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid); break; case LIBXL_TRIGGER_RESET: rc = xc_domain_send_trigger(ctx->xch, domid, XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid); break; case LIBXL_TRIGGER_S3RESUME: rc = libxl__domain_s3_resume(gc, domid); break; default: rc = -1; errno = EINVAL; break; } if (rc != 0) { LOGED(ERROR, domid, "Send trigger '%s' failed", libxl_trigger_to_string(trigger)); rc = ERROR_FAIL; } GC_FREE; return rc; } uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid) { GC_INIT(ctx); char *dompath = libxl__xs_get_dompath(gc, domid); char *vm_path, *start_time; uint32_t ret; vm_path = libxl__xs_read( gc, XBT_NULL, GCSPRINTF("%s/vm", dompath)); start_time = libxl__xs_read( gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path)); if (start_time == NULL) { LOGEVD(ERROR, -1, domid, "Can't get start time of domain"); ret = -1; }else{ ret = strtoul(start_time, NULL, 10); } GC_FREE; return ret; } /* For QEMU upstream we always need to provide the number of cpus present to * QEMU whether they are online or not; otherwise QEMU won't accept the saved * state. See implementation of libxl__qmp_query_cpus. */ static int libxl__update_avail_vcpus_qmp(libxl__gc *gc, uint32_t domid, unsigned int max_vcpus, libxl_bitmap *map) { int rc; rc = libxl__qmp_query_cpus(gc, domid, map); if (rc) { LOGD(ERROR, domid, "Fail to get number of cpus"); goto out; } rc = 0; out: return rc; } static int libxl__update_avail_vcpus_xenstore(libxl__gc *gc, uint32_t domid, unsigned int max_vcpus, libxl_bitmap *map) { int rc; unsigned int i; const char *dompath; dompath = libxl__xs_get_dompath(gc, domid); if (!dompath) { rc = ERROR_FAIL; goto out; } for (i = 0; i < max_vcpus; i++) { const char *path = GCSPRINTF("%s/cpu/%u/availability", dompath, i); const char *content; rc = libxl__xs_read_checked(gc, XBT_NULL, path, &content); if (rc) goto out; if (content && !strcmp(content, "online")) libxl_bitmap_set(map, i); } rc = 0; out: return rc; } int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid, libxl_domain_config *d_config) { GC_INIT(ctx); int rc; libxl__domain_userdata_lock *lock = NULL; CTX_LOCK; lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__get_domain_configuration(gc, domid, d_config); if (rc) { LOGD(ERROR, domid, "Fail to get domain configuration"); rc = ERROR_FAIL; goto out; } /* Domain name */ { char *domname; domname = libxl_domid_to_name(ctx, domid); if (!domname) { LOGD(ERROR, domid, "Fail to get domain name"); goto out; } free(d_config->c_info.name); d_config->c_info.name = domname; /* steals allocation */ } /* Domain UUID */ { libxl_dominfo info; libxl_dominfo_init(&info); rc = libxl_domain_info(ctx, &info, domid); if (rc) { LOGD(ERROR, domid, "Fail to get domain info"); libxl_dominfo_dispose(&info); goto out; } libxl_uuid_copy(ctx, &d_config->c_info.uuid, &info.uuid); libxl_dominfo_dispose(&info); } /* VCPUs */ { libxl_bitmap *map = &d_config->b_info.avail_vcpus; unsigned int max_vcpus = d_config->b_info.max_vcpus; libxl_device_model_version version; libxl_bitmap_dispose(map); libxl_bitmap_init(map); libxl_bitmap_alloc(CTX, map, max_vcpus); libxl_bitmap_set_none(map); switch (d_config->b_info.type) { case LIBXL_DOMAIN_TYPE_HVM: version = libxl__device_model_version_running(gc, domid); assert(version != LIBXL_DEVICE_MODEL_VERSION_UNKNOWN); switch (version) { case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN: rc = libxl__update_avail_vcpus_qmp(gc, domid, max_vcpus, map); break; case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL: case LIBXL_DEVICE_MODEL_VERSION_NONE: rc = libxl__update_avail_vcpus_xenstore(gc, domid, max_vcpus, map); break; default: abort(); } break; case LIBXL_DOMAIN_TYPE_PV: rc = libxl__update_avail_vcpus_xenstore(gc, domid, max_vcpus, map); break; default: abort(); } if (rc) { LOGD(ERROR, domid, "Fail to update available cpu map"); goto out; } } /* Memory limits: * * Currently there are three memory limits: * 1. "target" in xenstore (originally memory= in config file) * 2. "static-max" in xenstore (originally maxmem= in config file) * 3. "max_memkb" in hypervisor * * The third one is not visible and currently managed by * toolstack. In order to rebuild a domain we only need to have * "target" and "static-max". */ { uint64_t target_memkb = 0, max_memkb = 0; /* "target" */ rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb); if (rc) { LOGD(ERROR, domid, "Fail to get memory target"); goto out; } /* libxl__get_targetmem_fudge() calculates the difference from * what is in xenstore to what we have in the domain build info. */ d_config->b_info.target_memkb = target_memkb + libxl__get_targetmem_fudge(gc, &d_config->b_info); d_config->b_info.max_memkb = max_memkb; } /* Scheduler params */ { libxl_domain_sched_params_dispose(&d_config->b_info.sched_params); rc = libxl_domain_sched_params_get(ctx, domid, &d_config->b_info.sched_params); if (rc) { LOGD(ERROR, domid, "Fail to get scheduler parameters"); goto out; } } /* Devices: disk, nic, vtpm, pcidev etc. */ /* The MERGE macro implements following logic: * 0. retrieve JSON (done by now) * 1. retrieve list of device from xenstore * 2. use xenstore entries as primary reference and compare JSON * entries with them. * a. if a device is present in xenstore and in JSON, merge the * two views. * b. if a device is not present in xenstore but in JSON, delete * it from the result. * c. it's impossible to have an entry present in xenstore but * not in JSON, because we maintain an invariant that every * entry in xenstore must have a corresponding entry in JSON. * 3. "merge" operates on "src" and "dst". "src" points to the * entry retrieved from xenstore while "dst" points to the entry * retrieve from JSON. */ { const struct libxl_device_type *dt; int idx; for (idx = 0;; idx++) { void *p = NULL; void **devs; int i, j, num; int *num_dev; dt = device_type_tbl[idx]; if (!dt) break; if (!dt->list || !dt->compare) continue; num_dev = libxl__device_type_get_num(dt, d_config); p = dt->list(CTX, domid, &num); if (p == NULL) { LOGD(DEBUG, domid, "No %s from xenstore", dt->type); } devs = libxl__device_type_get_ptr(dt, d_config); for (i = 0; i < *num_dev; i++) { void *q; q = libxl__device_type_get_elem(dt, d_config, i); for (j = 0; j < num; j++) { if (dt->compare(p + dt->dev_elem_size * j, q)) break; } if (j < num) { /* found in xenstore */ if (dt->merge) dt->merge(ctx, p + dt->dev_elem_size * j, q); } else { /* not found in xenstore */ LOGD(WARN, domid, "Device present in JSON but not in xenstore, ignored"); dt->dispose(q); for (j = i; j < *num_dev - 1; j++) memcpy(libxl__device_type_get_elem(dt, d_config, j), libxl__device_type_get_elem(dt, d_config, j+1), dt->dev_elem_size); /* rewind counters */ (*num_dev)--; i--; *devs = libxl__realloc(NOGC, *devs, dt->dev_elem_size * *num_dev); } } for (i = 0; i < num; i++) dt->dispose(p + dt->dev_elem_size * i); free(p); } } out: if (lock) libxl__unlock_domain_userdata(lock); CTX_UNLOCK; GC_FREE; return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_netbuffer.c0000664000175000017500000003426513256712137016257 0ustar smbsmb/* * Copyright (C) 2014 * Author Shriram Rajagopalan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include #include #include #include #include #include #include typedef struct libxl__remus_device_nic { int devid; const char *vif; const char *ifb; struct rtnl_qdisc *qdisc; } libxl__remus_device_nic; int libxl__netbuffer_enabled(libxl__gc *gc) { return 1; } int init_subkind_nic(libxl__checkpoint_devices_state *cds) { int rc, ret; libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds); libxl__remus_state *rs = cds->concrete_data; STATE_AO_GC(cds->ao); rs->nlsock = nl_socket_alloc(); if (!rs->nlsock) { LOGD(ERROR, dss->domid, "cannot allocate nl socket"); rc = ERROR_FAIL; goto out; } ret = nl_connect(rs->nlsock, NETLINK_ROUTE); if (ret) { LOGD(ERROR, dss->domid, "failed to open netlink socket: %s", nl_geterror(ret)); rc = ERROR_FAIL; goto out; } /* get list of all qdiscs installed on network devs. */ ret = rtnl_qdisc_alloc_cache(rs->nlsock, &rs->qdisc_cache); if (ret) { LOGD(ERROR, dss->domid, "failed to allocate qdisc cache: %s", nl_geterror(ret)); rc = ERROR_FAIL; goto out; } if (dss->remus->netbufscript) { rs->netbufscript = libxl__strdup(gc, dss->remus->netbufscript); } else { rs->netbufscript = GCSPRINTF("%s/remus-netbuf-setup", libxl__xen_script_dir_path()); } rc = 0; out: return rc; } void cleanup_subkind_nic(libxl__checkpoint_devices_state *cds) { libxl__remus_state *rs = cds->concrete_data; STATE_AO_GC(cds->ao); /* free qdisc cache */ if (rs->qdisc_cache) { nl_cache_clear(rs->qdisc_cache); nl_cache_free(rs->qdisc_cache); rs->qdisc_cache = NULL; } /* close & free nlsock */ if (rs->nlsock) { nl_close(rs->nlsock); nl_socket_free(rs->nlsock); rs->nlsock = NULL; } } /*----- setup() and teardown() -----*/ /* helper functions */ /* * If the device has a vifname, then use that instead of * the vifX.Y format. * it must ONLY be used for remus because if driver domains * were in use it would constitute a security vulnerability. */ static const char *get_vifname(libxl__checkpoint_device *dev, const libxl_device_nic *nic) { const char *vifname = NULL; const char *path; int rc; STATE_AO_GC(dev->cds->ao); /* Convenience aliases */ const uint32_t domid = dev->cds->domid; path = GCSPRINTF("%s/backend/vif/%d/%d/vifname", libxl__xs_get_dompath(gc, 0), domid, nic->devid); rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname); if (!rc && !vifname) { vifname = libxl__device_nic_devname(gc, domid, nic->devid, nic->nictype); } return vifname; } static void free_qdisc(libxl__remus_device_nic *remus_nic) { if (remus_nic->qdisc == NULL) return; nl_object_put((struct nl_object *)(remus_nic->qdisc)); remus_nic->qdisc = NULL; } static int init_qdisc(libxl__checkpoint_devices_state *cds, libxl__remus_device_nic *remus_nic) { int rc, ret, ifindex; struct rtnl_link *ifb = NULL; struct rtnl_qdisc *qdisc = NULL; libxl__remus_state *rs = cds->concrete_data; STATE_AO_GC(cds->ao); /* Now that we have brought up REMUS_IFB device with plug qdisc for * this vif, so we need to refill the qdisc cache. */ ret = nl_cache_refill(rs->nlsock, rs->qdisc_cache); if (ret) { LOGD(ERROR, cds->domid, "cannot refill qdisc cache: %s", nl_geterror(ret)); rc = ERROR_FAIL; goto out; } /* get a handle to the REMUS_IFB interface */ ret = rtnl_link_get_kernel(rs->nlsock, 0, remus_nic->ifb, &ifb); if (ret) { LOGD(ERROR, cds->domid, "cannot obtain handle for %s: %s", remus_nic->ifb, nl_geterror(ret)); rc = ERROR_FAIL; goto out; } ifindex = rtnl_link_get_ifindex(ifb); if (!ifindex) { LOGD(ERROR, cds->domid, "interface %s has no index", remus_nic->ifb); rc = ERROR_FAIL; goto out; } /* Get a reference to the root qdisc installed on the REMUS_IFB, by * querying the qdisc list we obtained earlier. The netbufscript * sets up the plug qdisc as the root qdisc, so we don't have to * search the entire qdisc tree on the REMUS_IFB dev. * There is no need to explicitly free this qdisc as its just a * reference from the qdisc cache we allocated earlier. */ qdisc = rtnl_qdisc_get_by_parent(rs->qdisc_cache, ifindex, TC_H_ROOT); if (qdisc) { const char *tc_kind = rtnl_tc_get_kind(TC_CAST(qdisc)); /* Sanity check: Ensure that the root qdisc is a plug qdisc. */ if (!tc_kind || strcmp(tc_kind, "plug")) { LOGD(ERROR, cds->domid, "plug qdisc is not installed on %s", remus_nic->ifb); rc = ERROR_FAIL; goto out; } remus_nic->qdisc = qdisc; } else { LOGD(ERROR, cds->domid, "Cannot get qdisc handle from ifb %s", remus_nic->ifb); rc = ERROR_FAIL; goto out; } rc = 0; out: if (ifb) rtnl_link_put(ifb); if (rc && qdisc) nl_object_put((struct nl_object *)qdisc); return rc; } /* callbacks */ static void netbuf_setup_script_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status); static void netbuf_teardown_script_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status); /* * the script needs the following env & args * $vifname * $XENBUS_PATH (/libxl//remus/netbuf//) * $REMUS_IFB (for teardown) * setup/teardown as command line arg. */ static void setup_async_exec(libxl__checkpoint_device *dev, char *op) { int arraysize, nr = 0; char **env = NULL, **args = NULL; libxl__remus_device_nic *remus_nic = dev->concrete_data; libxl__checkpoint_devices_state *cds = dev->cds; libxl__async_exec_state *aes = &dev->aodev.aes; libxl__remus_state *rs = cds->concrete_data; STATE_AO_GC(cds->ao); /* Convenience aliases */ char *const script = libxl__strdup(gc, rs->netbufscript); const uint32_t domid = cds->domid; const int dev_id = remus_nic->devid; const char *const vif = remus_nic->vif; const char *const ifb = remus_nic->ifb; arraysize = 7; GCNEW_ARRAY(env, arraysize); env[nr++] = "vifname"; env[nr++] = libxl__strdup(gc, vif); env[nr++] = "XENBUS_PATH"; env[nr++] = GCSPRINTF("%s/remus/netbuf/%d", libxl__xs_libxl_path(gc, domid), dev_id); if (!strcmp(op, "teardown") && ifb) { env[nr++] = "REMUS_IFB"; env[nr++] = libxl__strdup(gc, ifb); } env[nr++] = NULL; assert(nr <= arraysize); arraysize = 3; nr = 0; GCNEW_ARRAY(args, arraysize); args[nr++] = script; args[nr++] = op; args[nr++] = NULL; assert(nr == arraysize); aes->ao = dev->cds->ao; aes->what = GCSPRINTF("%s %s", args[0], args[1]); aes->env = env; aes->args = args; aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000; aes->stdfds[0] = -1; aes->stdfds[1] = -1; aes->stdfds[2] = -1; if (!strcmp(op, "teardown")) aes->callback = netbuf_teardown_script_cb; else aes->callback = netbuf_setup_script_cb; } /* setup() and teardown() */ static void nic_setup(libxl__egc *egc, libxl__checkpoint_device *dev) { int rc; libxl__remus_device_nic *remus_nic; const libxl_device_nic *nic = dev->backend_dev; STATE_AO_GC(dev->cds->ao); /* * thers's no subkind of nic devices, so nic ops is always matched * with nic devices */ dev->matched = true; GCNEW(remus_nic); dev->concrete_data = remus_nic; remus_nic->devid = nic->devid; remus_nic->vif = get_vifname(dev, nic); if (!remus_nic->vif) { rc = ERROR_FAIL; goto out; } setup_async_exec(dev, "setup"); rc = libxl__async_exec_start(&dev->aodev.aes); if (rc) goto out; return; out: dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } /* * In return, the script writes the name of REMUS_IFB device (during setup) * to be used for output buffering into XENBUS_PATH/ifb */ static void netbuf_setup_script_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status) { libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes); libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev); libxl__remus_device_nic *remus_nic = dev->concrete_data; libxl__checkpoint_devices_state *cds = dev->cds; libxl__remus_state *rs = cds->concrete_data; const char *out_path_base, *hotplug_error = NULL; STATE_AO_GC(cds->ao); /* Convenience aliases */ const uint32_t domid = cds->domid; const int devid = remus_nic->devid; const char *const vif = remus_nic->vif; const char **const ifb = &remus_nic->ifb; if (status && !rc) rc = ERROR_FAIL; if (rc) goto out; /* * we need to get ifb first because it's needed for teardown */ rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/remus/netbuf/%d/ifb", libxl__xs_libxl_path(gc, domid), devid), ifb); if (rc) goto out; if (!(*ifb)) { LOGD(ERROR, domid, "Cannot get ifb dev name for domain %u dev %s", domid, vif); rc = ERROR_FAIL; goto out; } out_path_base = GCSPRINTF("%s/remus/netbuf/%d", libxl__xs_libxl_path(gc, domid), devid); rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/hotplug-error", out_path_base), &hotplug_error); if (rc) goto out; if (hotplug_error) { LOGD(ERROR, domid, "netbuf script %s setup failed for vif %s: %s", rs->netbufscript, vif, hotplug_error); rc = ERROR_FAIL; goto out; } if (status) { rc = ERROR_FAIL; goto out; } LOGD(DEBUG, domid, "%s will buffer packets from vif %s", *ifb, vif); rc = init_qdisc(cds, remus_nic); out: aodev->rc = rc; aodev->callback(egc, aodev); } static void nic_teardown(libxl__egc *egc, libxl__checkpoint_device *dev) { int rc; STATE_AO_GC(dev->cds->ao); setup_async_exec(dev, "teardown"); rc = libxl__async_exec_start(&dev->aodev.aes); if (rc) goto out; return; out: dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } static void netbuf_teardown_script_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status) { libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes); libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev); libxl__remus_device_nic *remus_nic = dev->concrete_data; if (status && !rc) rc = ERROR_FAIL; free_qdisc(remus_nic); aodev->rc = rc; aodev->callback(egc, aodev); } /*----- checkpointing APIs -----*/ /* The value of buffer_op, not the value passed to kernel */ enum { tc_buffer_start, tc_buffer_release }; /* API implementations */ static int remus_netbuf_op(libxl__remus_device_nic *remus_nic, libxl__checkpoint_devices_state *cds, int buffer_op) { int rc, ret; libxl__remus_state *rs = cds->concrete_data; STATE_AO_GC(cds->ao); if (buffer_op == tc_buffer_start) ret = rtnl_qdisc_plug_buffer(remus_nic->qdisc); else ret = rtnl_qdisc_plug_release_one(remus_nic->qdisc); if (ret) { rc = ERROR_FAIL; goto out; } ret = rtnl_qdisc_add(rs->nlsock, remus_nic->qdisc, NLM_F_REQUEST); if (ret) { rc = ERROR_FAIL; goto out; } rc = 0; out: if (rc) LOGD(ERROR, cds-> domid, "Remus: cannot do netbuf op %s on %s:%s", ((buffer_op == tc_buffer_start) ? "start_new_epoch" : "release_prev_epoch"), remus_nic->ifb, nl_geterror(ret)); return rc; } static void nic_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev) { int rc; libxl__remus_device_nic *remus_nic = dev->concrete_data; STATE_AO_GC(dev->cds->ao); rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_start); dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } static void nic_commit(libxl__egc *egc, libxl__checkpoint_device *dev) { int rc; libxl__remus_device_nic *remus_nic = dev->concrete_data; STATE_AO_GC(dev->cds->ao); rc = remus_netbuf_op(remus_nic, dev->cds, tc_buffer_release); dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } const libxl__checkpoint_device_instance_ops remus_device_nic = { .kind = LIBXL__DEVICE_KIND_VIF, .setup = nic_setup, .teardown = nic_teardown, .postsuspend = nic_postsuspend, .commit = nic_commit, }; /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_stream_read.c0000664000175000017500000007474113256712137016570 0ustar smbsmb/* * Copyright (C) 2015 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" /* * Infrastructure for reading and acting on the contents of a libxl * migration stream. There are a lot of moving parts here. * * The logic revolves around two actions; reading another record from * the stream, and processing the records. The stream_continue() * function is responsible for choosing the next action to perform. * * The exact order of reading and processing is controlled by 'phase'. * All complete records are held in the record_queue before being * processed, and all records will be processed in queue order. * * Internal states: * running phase in_ record incoming * checkpoint _queue _record * * Undefined undef undef undef undef undef * Idle false undef false 0 0 * Active true NORMAL false 0/1 0/partial * Active true BUFFERING true any 0/partial * Active true UNBUFFERING true any 0 * * While reading data from the stream, 'dc' is active and a callback * is expected. Most actions in process_record() start a callback of * their own. Those which don't return out and stream_continue() sets * up the next action. * * PHASE_NORMAL: * This phase is used for regular migration or resume from file. * Records are read one at time and immediately processed. (The * record queue will not contain more than a single record.) * * PHASE_BUFFERING: * This phase is used in checkpointed streams, when libxc signals * the presence of a checkpoint in the stream. Records are read and * buffered until a CHECKPOINT_END record has been read. * * PHASE_UNBUFFERING: * Once a CHECKPOINT_END record has been read, all buffered records * are processed. * * Note: * Record buffers are not allocated from a GC; they are allocated * and tracked manually. This is to avoid OOM with Remus where the * AO lives for the lifetime of the process. Per-checkpoint AO's * might be an avenue to explore. * * Entry points from outside: * - libxl__stream_read_init() * - Initialises state. Must be called once before _start() * - libxl__stream_read_start() * - Starts reading records from the stream, and acting on them. * - libxl__stream_read_start_checkpoint() * - Starts buffering records at a checkpoint. Must be called on * a running stream. * * There are several chains of event: * * 1) Starting a stream follows: * - libxl__stream_read_start() * - stream_header_done() * - stream_continue() * * 2) Reading a record follows: * - stream_continue() * - record_header_done() * - record_body_done() * - stream_continue() * * 3) Processing a record had several chains to follow, depending on * the record in question. * 3a) "Simple" record: * - process_record() * - stream_continue() * 3b) LIBXC record: * - process_record() * - libxl__xc_domain_restore() * - libxl__xc_domain_restore_done() * - stream_continue() * 3c) EMULATOR record: * - process_record() * - stream_write_emulator() * - stream_write_emulator_done() * - stream_continue() * * Depending on the contents of the stream, there are likely to be several * parallel tasks being managed. check_all_finished() is used to join all * tasks in both success and error cases. * * Failover for remus * - We buffer all records until a CHECKPOINT_END record is received * - We will consume the buffered records when a CHECKPOINT_END record * is received * - If we find some internal error, then rc or retval is not 0 in * libxl__xc_domain_restore_done(). In this case, we don't resume the * guest * - If we need to do failover from primary, then rc and retval are both * 0 in libxl__xc_domain_restore_done(). In this case, the buffered * state will be dropped, because we haven't received a CHECKPOINT_END * record, and therefore the buffered state is inconsistent. In * libxl__xc_domain_restore_done(), we just complete the stream and * stream->completion_callback() will be called to resume the guest * * For back channel stream: * - libxl__stream_read_start() * - Set up the stream to running state * * - libxl__stream_read_continue() * - Set up reading the next record from a started stream. * Add some codes to process_record() to handle the record. * Then call stream->checkpoint_callback() to return. */ /* Success/error/cleanup handling. */ static void stream_complete(libxl__egc *egc, libxl__stream_read_state *stream, int rc); static void checkpoint_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc); static void stream_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc); static void conversion_done(libxl__egc *egc, libxl__conversion_helper_state *chs, int rc); static void check_all_finished(libxl__egc *egc, libxl__stream_read_state *stream, int rc); /* Event chain for first iteration, from _start(). */ static void stream_header_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); static void stream_continue(libxl__egc *egc, libxl__stream_read_state *stream); static void setup_read_record(libxl__egc *egc, libxl__stream_read_state *stream); static void record_header_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); static void record_body_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); static bool process_record(libxl__egc *egc, libxl__stream_read_state *stream); /* Event chain for processing an emulator blob. */ static void write_emulator_blob(libxl__egc *egc, libxl__stream_read_state *stream, libxl__sr_record_buf *rec); static void write_emulator_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); /* Handlers for checkpoint state mini-loop */ static void checkpoint_state_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc); /*----- Helpers -----*/ /* Helper to set up reading some data from the stream. */ static int setup_read(libxl__stream_read_state *stream, const char *what, void *ptr, size_t nr_bytes, libxl__datacopier_callback cb) { libxl__datacopier_state *dc = &stream->dc; dc->readwhat = what; dc->readbuf = ptr; dc->bytes_to_read = nr_bytes; dc->used = 0; dc->callback = cb; return libxl__datacopier_start(dc); } static void free_record(libxl__sr_record_buf *rec) { if (rec) { free(rec->body); free(rec); } } /*----- Entrypoints -----*/ void libxl__stream_read_init(libxl__stream_read_state *stream) { assert(stream->ao); stream->shs.ao = stream->ao; libxl__save_helper_init(&stream->shs); stream->chs.ao = stream->ao; libxl__conversion_helper_init(&stream->chs); stream->rc = 0; stream->running = false; stream->in_checkpoint = false; stream->sync_teardown = false; FILLZERO(stream->dc); FILLZERO(stream->hdr); LIBXL_STAILQ_INIT(&stream->record_queue); stream->phase = SRS_PHASE_NORMAL; stream->recursion_guard = false; stream->incoming_record = NULL; FILLZERO(stream->emu_dc); stream->emu_carefd = NULL; } void libxl__stream_read_start(libxl__egc *egc, libxl__stream_read_state *stream) { libxl__datacopier_state *dc = &stream->dc; STATE_AO_GC(stream->ao); int rc = 0; libxl__stream_read_init(stream); stream->running = true; stream->phase = SRS_PHASE_NORMAL; if (stream->legacy) { /* * Convert the legacy stream. * * This results in a fork()/exec() of conversion helper script. It is * passed the exiting stream->fd as an input, and returns the * transformed stream via a new pipe. The fd of this new pipe then * replaces stream->fd, to make the rest of the stream read code * agnostic to whether legacy conversion is happening or not. */ libxl__conversion_helper_state *chs = &stream->chs; chs->legacy_fd = stream->fd; chs->hvm = (stream->dcs->guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM); chs->completion_callback = conversion_done; rc = libxl__convert_legacy_stream(egc, &stream->chs); if (rc) { LOG(ERROR, "Failed to start the legacy stream conversion helper"); goto err; } /* There should be no interaction of COLO backchannels and legacy * stream conversion. */ assert(!stream->back_channel); /* Confirm *dc is still zeroed out, while we shuffle stream->fd. */ assert(dc->ao == NULL); assert(stream->chs.v2_carefd); stream->fd = libxl__carefd_fd(stream->chs.v2_carefd); stream->dcs->libxc_fd = stream->fd; } /* stream->fd is now a v2 stream. */ dc->ao = stream->ao; dc->copywhat = "restore v2 stream"; dc->readfd = stream->fd; dc->writefd = -1; if (stream->back_channel) return; /* Start reading the stream header. */ rc = setup_read(stream, "stream header", &stream->hdr, sizeof(stream->hdr), stream_header_done); if (rc) goto err; assert(!rc); return; err: assert(rc); stream_complete(egc, stream, rc); } void libxl__stream_read_start_checkpoint(libxl__egc *egc, libxl__stream_read_state *stream) { assert(stream->running); assert(!stream->in_checkpoint); stream->in_checkpoint = true; stream->phase = SRS_PHASE_BUFFERING; /* * Libxc has handed control of the fd to us. Start reading some * libxl records out of it. */ stream_continue(egc, stream); } void libxl__stream_read_abort(libxl__egc *egc, libxl__stream_read_state *stream, int rc) { assert(rc); if (stream->running) stream_complete(egc, stream, rc); } /*----- Event logic -----*/ static void stream_header_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc); libxl__sr_hdr *hdr = &stream->hdr; STATE_AO_GC(dc->ao); if (rc) goto err; hdr->ident = be64toh(hdr->ident); hdr->version = be32toh(hdr->version); hdr->options = be32toh(hdr->options); if (hdr->ident != RESTORE_STREAM_IDENT) { rc = ERROR_FAIL; LOG(ERROR, "Invalid ident: expected 0x%016"PRIx64", got 0x%016"PRIx64, RESTORE_STREAM_IDENT, hdr->ident); goto err; } if (hdr->version != RESTORE_STREAM_VERSION) { rc = ERROR_FAIL; LOG(ERROR, "Unexpected Version: expected %"PRIu32", got %"PRIu32, RESTORE_STREAM_VERSION, hdr->version); goto err; } if (hdr->options & RESTORE_OPT_BIG_ENDIAN) { rc = ERROR_FAIL; LOG(ERROR, "Unable to handle big endian streams"); goto err; } LOG(DEBUG, "Stream v%"PRIu32"%s", hdr->version, hdr->options & RESTORE_OPT_LEGACY ? " (from legacy)" : ""); stream_continue(egc, stream); return; err: assert(rc); stream_complete(egc, stream, rc); } static void stream_continue(libxl__egc *egc, libxl__stream_read_state *stream) { STATE_AO_GC(stream->ao); /* * Must not mutually recurse with process_record(). * * For records whose processing function is synchronous * (e.g. TOOLSTACK), process_record() does not start another async * operation, and a further operation should be started. * * A naive solution, which would function in general, would be for * process_record() to call stream_continue(). However, this * would allow the content of the stream to cause mutual * recursion, and possibly for us to fall off our stack. * * Instead, process_record() indicates with its return value * whether a further operation needs to start, and the * recursion_guard is in place to catch any code paths which get * this wrong. */ assert(stream->recursion_guard == false); stream->recursion_guard = true; switch (stream->phase) { case SRS_PHASE_NORMAL: /* * Normal phase (regular migration or restore from file): * * logically: * do { read_record(); process_record(); } while ( not END ); * * Alternate between reading a record from the stream, and * processing the record. There should never be two records * in the queue. */ if (LIBXL_STAILQ_EMPTY(&stream->record_queue)) setup_read_record(egc, stream); else { if (process_record(egc, stream)) setup_read_record(egc, stream); /* * process_record() had better have consumed the one and * only record in the queue. */ assert(LIBXL_STAILQ_EMPTY(&stream->record_queue)); } break; case SRS_PHASE_BUFFERING: { /* * Buffering phase (checkpointed streams only): * * logically: * do { read_record(); } while ( not CHECKPOINT_END ); * * Read and buffer all records from the stream until a * CHECKPOINT_END record is encountered. We need to peek at * the tail to spot the CHECKPOINT_END record, and switch to * the unbuffering phase. */ libxl__sr_record_buf *rec = LIBXL_STAILQ_LAST( &stream->record_queue, libxl__sr_record_buf, entry); assert(stream->in_checkpoint); if (!rec || (rec->hdr.type != REC_TYPE_CHECKPOINT_END)) { setup_read_record(egc, stream); break; } /* * There are now some number of buffered records, with a * CHECKPOINT_END at the end. Start processing them all. */ stream->phase = SRS_PHASE_UNBUFFERING; } /* FALLTHROUGH */ case SRS_PHASE_UNBUFFERING: /* * Unbuffering phase (checkpointed streams only): * * logically: * do { process_record(); } while ( not CHECKPOINT_END ); * * Process all records collected during the buffering phase. */ assert(stream->in_checkpoint); while (process_record(egc, stream)) ; /* * Nothing! process_record() helpfully tells us if no specific * futher actions have been set up, in which case we want to go * ahead and process the next record. */ break; default: abort(); } assert(stream->recursion_guard == true); stream->recursion_guard = false; } static void setup_read_record(libxl__egc *egc, libxl__stream_read_state *stream) { libxl__sr_record_buf *rec = NULL; STATE_AO_GC(stream->ao); int rc; assert(stream->incoming_record == NULL); stream->incoming_record = rec = libxl__zalloc(NOGC, sizeof(*rec)); rc = setup_read(stream, "record header", &rec->hdr, sizeof(rec->hdr), record_header_done); if (rc) goto err; return; err: assert(rc); stream_complete(egc, stream, rc); } static void record_header_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc); libxl__sr_record_buf *rec = stream->incoming_record; STATE_AO_GC(dc->ao); if (rc) goto err; /* No body? All done. */ if (rec->hdr.length == 0) { record_body_done(egc, dc, 0, 0, 0); return; } size_t bytes_to_read = ROUNDUP(rec->hdr.length, REC_ALIGN_ORDER); rec->body = libxl__malloc(NOGC, bytes_to_read); rc = setup_read(stream, "record body", rec->body, bytes_to_read, record_body_done); if (rc) goto err; return; err: assert(rc); stream_complete(egc, stream, rc); } static void record_body_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, dc); libxl__sr_record_buf *rec = stream->incoming_record; STATE_AO_GC(dc->ao); if (rc) goto err; LIBXL_STAILQ_INSERT_TAIL(&stream->record_queue, rec, entry); stream->incoming_record = NULL; stream_continue(egc, stream); return; err: assert(rc); stream_complete(egc, stream, rc); } /* * Returns a boolean indicating whether a further action should be set * up by the caller. This is needed to prevent mutual recursion with * stream_continue(). * * It is a bug for this function to ever call stream_continue() or * setup_read_record(). */ static bool process_record(libxl__egc *egc, libxl__stream_read_state *stream) { STATE_AO_GC(stream->ao); libxl__domain_create_state *dcs = stream->dcs; libxl__sr_record_buf *rec; libxl_sr_checkpoint_state *srcs; bool further_action_needed = false; int rc = 0; /* Pop a record from the head of the queue. */ assert(!LIBXL_STAILQ_EMPTY(&stream->record_queue)); rec = LIBXL_STAILQ_FIRST(&stream->record_queue); LIBXL_STAILQ_REMOVE_HEAD(&stream->record_queue, entry); LOG(DEBUG, "Record: %u, length %u", rec->hdr.type, rec->hdr.length); switch (rec->hdr.type) { case REC_TYPE_END: stream_complete(egc, stream, 0); break; case REC_TYPE_LIBXC_CONTEXT: libxl__xc_domain_restore(egc, dcs, &stream->shs, 0, 0, 0); break; case REC_TYPE_EMULATOR_XENSTORE_DATA: if (dcs->guest_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) { rc = ERROR_FAIL; LOG(ERROR, "Received a xenstore emulator record when none was expected"); goto err; } if (rec->hdr.length < sizeof(libxl__sr_emulator_hdr)) { rc = ERROR_FAIL; LOG(ERROR, "Emulator xenstore data record too short to contain header"); goto err; } rc = libxl__restore_emulator_xenstore_data(dcs, rec->body + sizeof(libxl__sr_emulator_hdr), rec->hdr.length - sizeof(libxl__sr_emulator_hdr)); if (rc) goto err; /* * libxl__restore_emulator_xenstore_data() is a synchronous function. * Request that our caller queues another action for us. */ further_action_needed = true; break; case REC_TYPE_EMULATOR_CONTEXT: if (dcs->guest_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) { rc = ERROR_FAIL; LOG(ERROR, "Received an emulator context record when none was expected"); goto err; } write_emulator_blob(egc, stream, rec); break; case REC_TYPE_CHECKPOINT_END: if (!stream->in_checkpoint) { LOG(ERROR, "Unexpected CHECKPOINT_END record in stream"); rc = ERROR_FAIL; goto err; } checkpoint_done(egc, stream, 0); break; case REC_TYPE_CHECKPOINT_STATE: if (!stream->in_checkpoint_state) { LOG(ERROR, "Unexpected CHECKPOINT_STATE record in stream"); rc = ERROR_FAIL; goto err; } srcs = rec->body; checkpoint_state_done(egc, stream, srcs->id); break; default: LOG(ERROR, "Unrecognised record 0x%08x", rec->hdr.type); rc = ERROR_FAIL; goto err; } assert(!rc); free_record(rec); return further_action_needed; err: assert(rc); free_record(rec); stream_complete(egc, stream, rc); return false; } static void write_emulator_blob(libxl__egc *egc, libxl__stream_read_state *stream, libxl__sr_record_buf *rec) { libxl__domain_create_state *dcs = stream->dcs; libxl__datacopier_state *dc = &stream->emu_dc; libxl__sr_emulator_hdr *emu_hdr; STATE_AO_GC(stream->ao); char path[256]; int rc = 0, writefd; if (rec->hdr.length < sizeof(*emu_hdr)) { rc = ERROR_FAIL; LOG(ERROR, "Emulator record too short to contain header"); goto err; } emu_hdr = rec->body; sprintf(path, LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", dcs->guest_domid); assert(stream->emu_carefd == NULL); libxl__carefd_begin(); writefd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); stream->emu_carefd = libxl__carefd_opened(CTX, writefd); if (writefd == -1) { rc = ERROR_FAIL; LOGE(ERROR, "unable to open %s", path); goto err; } FILLZERO(*dc); dc->ao = stream->ao; dc->writewhat = "qemu save file"; dc->copywhat = "restore v2 stream"; dc->writefd = writefd; dc->readfd = -1; dc->maxsz = -1; dc->callback = write_emulator_done; rc = libxl__datacopier_start(dc); if (rc) goto err; libxl__datacopier_prefixdata(egc, dc, rec->body + sizeof(*emu_hdr), rec->hdr.length - sizeof(*emu_hdr)); return; err: assert(rc); stream_complete(egc, stream, rc); } static void write_emulator_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, emu_dc); STATE_AO_GC(dc->ao); libxl__carefd_close(stream->emu_carefd); stream->emu_carefd = NULL; if (rc) goto err; stream_continue(egc, stream); return; err: assert(rc); stream_complete(egc, stream, rc); } /*----- Success/error/cleanup handling. -----*/ static void stream_complete(libxl__egc *egc, libxl__stream_read_state *stream, int rc) { assert(stream->running); if (stream->in_checkpoint) { assert(rc); /* * If an error is encountered while in a checkpoint, pass it * back to libxc. The failure will come back around to us via * libxl__xc_domain_restore_done() */ checkpoint_done(egc, stream, rc); return; } if (stream->in_checkpoint_state) { assert(rc); /* * If an error is encountered while in a checkpoint, pass it * back to libxc. The failure will come back around to us via * 1. normal stream * libxl__xc_domain_restore_done() * 2. back_channel stream * libxl__stream_read_abort() */ checkpoint_state_done(egc, stream, rc); return; } stream_done(egc, stream, rc); } static void checkpoint_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc) { int ret; assert(stream->in_checkpoint); if (rc == 0) ret = XGR_CHECKPOINT_SUCCESS; else if (stream->phase == SRS_PHASE_BUFFERING) ret = XGR_CHECKPOINT_FAILOVER; else ret = XGR_CHECKPOINT_ERROR; stream->checkpoint_callback(egc, stream, ret); stream->in_checkpoint = false; stream->phase = SRS_PHASE_NORMAL; } static void stream_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc) { libxl__sr_record_buf *rec, *trec; assert(stream->running); assert(!stream->in_checkpoint); assert(!stream->in_checkpoint_state); stream->running = false; if (stream->incoming_record) free_record(stream->incoming_record); if (stream->emu_carefd) libxl__carefd_close(stream->emu_carefd); /* If we started a conversion helper, we took ownership of its carefd. */ if (stream->chs.v2_carefd) libxl__carefd_close(stream->chs.v2_carefd); /* The record queue had better be empty if the stream believes * itself to have been successful. */ assert(LIBXL_STAILQ_EMPTY(&stream->record_queue) || stream->rc); LIBXL_STAILQ_FOREACH_SAFE(rec, &stream->record_queue, entry, trec) free_record(rec); if (!stream->back_channel) { /* * 1. In stream_done(), stream->running is set to false, so * the stream itself is not in use. * 2. Read stream is a back channel stream, this means it is * only used by primary(save side) to read records sent by * secondary(restore side), so it doesn't have restore helper. * 3. Back channel stream doesn't support legacy stream, so * there is no conversion helper. * So we don't need invoke check_all_finished here */ check_all_finished(egc, stream, rc); } } void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void, int rc, int retval, int errnoval) { libxl__domain_create_state *dcs = dcs_void; libxl__stream_read_state *stream = &dcs->srs; STATE_AO_GC(dcs->ao); /* convenience aliases */ const int checkpointed_stream = dcs->restore_params.checkpointed_stream; if (rc) goto err; if (retval) { LOGEV(ERROR, errnoval, "restoring domain"); rc = ERROR_FAIL; goto err; } err: check_all_finished(egc, stream, rc); /* * This function is the callback associated with the save helper * task, not the stream task. We do not know whether the stream is * alive, and check_all_finished() may have torn it down around us. * If the stream is not still alive, we must not continue any work. */ if (libxl__stream_read_inuse(stream)) { switch (checkpointed_stream) { case LIBXL_CHECKPOINTED_STREAM_COLO: if (stream->completion_callback) { /* * restore, just build the secondary vm, don't close * the stream */ stream->completion_callback(egc, stream, 0); } else { /* failover, just close the stream */ stream_complete(egc, stream, 0); } break; case LIBXL_CHECKPOINTED_STREAM_REMUS: /* * Failover from primary. Domain state is currently at a * consistent checkpoint, complete the stream, and call * stream->completion_callback() to resume the guest. */ stream_complete(egc, stream, 0); break; case LIBXL_CHECKPOINTED_STREAM_NONE: /* * Libxc has indicated that it is done with the stream. * Resume reading libxl records from it. */ stream_continue(egc, stream); break; } } } static void conversion_done(libxl__egc *egc, libxl__conversion_helper_state *chs, int rc) { libxl__stream_read_state *stream = CONTAINER_OF(chs, *stream, chs); check_all_finished(egc, stream, rc); } static void check_all_finished(libxl__egc *egc, libxl__stream_read_state *stream, int rc) { STATE_AO_GC(stream->ao); /* * In the case of a failure, the _abort()'s below might cancel * synchronously on top of us, or asynchronously at a later point. * * We must avoid the situation where all _abort() cancel * synchronously and the completion_callback() gets called twice; * once by the first error and once by the final stacked abort(), * both of whom will find that all of the tasks have stopped. * * To avoid this problem, any stacked re-entry into this function is * ineligible to fire the completion callback. The outermost * instance will take care of completing, once the stack has * unwound. */ if (stream->sync_teardown) return; if (!stream->rc && rc) { /* First reported failure. Tear everything down. */ stream->rc = rc; stream->sync_teardown = true; libxl__stream_read_abort(egc, stream, rc); libxl__save_helper_abort(egc, &stream->shs); libxl__conversion_helper_abort(egc, &stream->chs, rc); stream->sync_teardown = false; } /* Don't fire the callback until all our parallel tasks have stopped. */ if (libxl__stream_read_inuse(stream) || libxl__save_helper_inuse(&stream->shs) || libxl__conversion_helper_inuse(&stream->chs)) return; if (stream->completion_callback) /* back channel stream doesn't have completion_callback() */ stream->completion_callback(egc, stream, stream->rc); } /*----- Checkpoint state handlers -----*/ void libxl__stream_read_checkpoint_state(libxl__egc *egc, libxl__stream_read_state *stream) { assert(stream->running); assert(!stream->in_checkpoint); assert(!stream->in_checkpoint_state); stream->in_checkpoint_state = true; setup_read_record(egc, stream); } static void checkpoint_state_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc) { assert(stream->in_checkpoint_state); stream->in_checkpoint_state = false; stream->checkpoint_callback(egc, stream, rc); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_noblktap2.c0000664000175000017500000000206113256712137016160 0ustar smbsmb/* * Copyright (C) 2010 Advanced Micro Devices * Author Christoph Egger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" int libxl__blktap_enabled(libxl__gc *gc) { return 0; } char *libxl__blktap_devpath(libxl__gc *gc, const char *disk, libxl_disk_format format) { return NULL; } int libxl__device_destroy_tapdisk(libxl__gc *gc, const char *params) { return 0; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_x86.c0000664000175000017500000004430013256712137014713 0ustar smbsmb#include "libxl_internal.h" #include "libxl_arch.h" #include int libxl__arch_domain_prepare_config(libxl__gc *gc, libxl_domain_config *d_config, xc_domain_configuration_t *xc_config) { if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM) { if (d_config->b_info.device_model_version != LIBXL_DEVICE_MODEL_VERSION_NONE) { xc_config->emulation_flags = XEN_X86_EMU_ALL; } else if (libxl_defbool_val(d_config->b_info.u.hvm.apic)) { /* * HVM guests without device model may want * to have LAPIC emulation. */ xc_config->emulation_flags = XEN_X86_EMU_LAPIC; } } else { xc_config->emulation_flags = 0; } return 0; } int libxl__arch_domain_save_config(libxl__gc *gc, libxl_domain_config *d_config, const xc_domain_configuration_t *xc_config) { return 0; } static const char *e820_names(int type) { switch (type) { case E820_RAM: return "RAM"; case E820_RESERVED: return "Reserved"; case E820_ACPI: return "ACPI"; case E820_NVS: return "ACPI NVS"; case E820_UNUSABLE: return "Unusable"; default: break; } return "Unknown"; } static int e820_sanitize(libxl__gc *gc, struct e820entry src[], uint32_t *nr_entries, unsigned long map_limitkb, unsigned long balloon_kb) { uint64_t delta_kb = 0, start = 0, start_kb = 0, last = 0, ram_end; uint32_t i, idx = 0, nr; struct e820entry e820[E820MAX]; if (!src || !map_limitkb || !nr_entries) return ERROR_INVAL; nr = *nr_entries; if (!nr) return ERROR_INVAL; if (nr > E820MAX) return ERROR_NOMEM; /* Weed out anything under 1MB */ for (i = 0; i < nr; i++) { if (src[i].addr > 0x100000) continue; src[i].type = 0; src[i].size = 0; src[i].addr = -1ULL; } /* Find the lowest and highest entry in E820, skipping over * undesired entries. */ start = -1ULL; last = 0; for (i = 0; i < nr; i++) { if ((src[i].type == E820_RAM) || (src[i].type == E820_UNUSABLE) || (src[i].type == 0)) continue; start = src[i].addr < start ? src[i].addr : start; last = src[i].addr + src[i].size > last ? src[i].addr + src[i].size > last : last; } if (start > 1024) start_kb = start >> 10; /* Add the memory RAM region for the guest */ e820[idx].addr = 0; e820[idx].size = (uint64_t)map_limitkb << 10; e820[idx].type = E820_RAM; /* .. and trim if neccessary */ if (start_kb && map_limitkb > start_kb) { delta_kb = map_limitkb - start_kb; if (delta_kb) e820[idx].size -= (uint64_t)(delta_kb << 10); } /* Note: We don't touch balloon_kb here. Will add it at the end. */ ram_end = e820[idx].addr + e820[idx].size; idx ++; LOG(DEBUG, "Memory: %"PRIu64"kB End of RAM: " \ "0x%"PRIx64" (PFN) Delta: %"PRIu64"kB, PCI start: %"PRIu64"kB " \ "(0x%"PRIx64" PFN), Balloon %"PRIu64"kB\n", (uint64_t)map_limitkb, ram_end >> 12, delta_kb, start_kb ,start >> 12, (uint64_t)balloon_kb); /* This whole code below is to guard against if the Intel IGD is passed into * the guest. If we don't pass in IGD, this whole code can be ignored. * * The reason for this code is that Intel boxes fill their E820 with * E820_RAM amongst E820_RESERVED and we can't just ditch those E820_RAM. * That is b/c any "gaps" in the E820 is considered PCI I/O space by * Linux and it would be utilized by the Intel IGD as I/O space while * in reality it was an RAM region. * * What this means is that we have to walk the E820 and for any region * that is RAM and below 4GB and above ram_end, needs to change its type * to E820_UNUSED. We also need to move some of the E820_RAM regions if * the overlap with ram_end. */ for (i = 0; i < nr; i++) { uint64_t end = src[i].addr + src[i].size; /* We don't care about E820_UNUSABLE, but we need to * change the type to zero b/c the loop after this * sticks E820_UNUSABLE on the guest's E820 but ignores * the ones with type zero. */ if ((src[i].type == E820_UNUSABLE) || /* Any region that is within the "RAM region" can * be safely ditched. */ (end < ram_end)) { src[i].type = 0; continue; } /* Look only at RAM regions. */ if (src[i].type != E820_RAM) continue; /* We only care about RAM regions below 4GB. */ if (src[i].addr >= (1ULL<<32)) continue; /* E820_RAM overlaps with our RAM region. Move it */ if (src[i].addr < ram_end) { uint64_t delta; src[i].type = E820_UNUSABLE; delta = ram_end - src[i].addr; /* The end < ram_end should weed this out */ if (src[i].size < delta) src[i].type = 0; else { src[i].size -= delta; src[i].addr = ram_end; } if (src[i].addr + src[i].size != end) { /* We messed up somewhere */ src[i].type = 0; LOGE(ERROR, "Computed E820 wrongly. Continuing on."); } } /* Lastly, convert the RAM to UNSUABLE. Look in the Linux kernel at git commit 2f14ddc3a7146ea4cd5a3d1ecd993f85f2e4f948 "xen/setup: Inhibit resource API from using System RAM E820 gaps as PCI mem gaps" for full explanation. */ if (end > ram_end) src[i].type = E820_UNUSABLE; } /* Check if there is a region between ram_end and start. */ if (start > ram_end) { int add_unusable = 1; for (i = 0; i < nr && add_unusable; i++) { if (src[i].type != E820_UNUSABLE) continue; if (ram_end != src[i].addr) continue; if (start != src[i].addr + src[i].size) { /* there is one, adjust it */ src[i].size = start - src[i].addr; } add_unusable = 0; } /* .. and if not present, add it in. This is to guard against the Linux guest assuming that the gap between the end of RAM region and the start of the E820_[ACPI,NVS,RESERVED] is PCI I/O space. Which it certainly is _not_. */ if (add_unusable) { e820[idx].type = E820_UNUSABLE; e820[idx].addr = ram_end; e820[idx].size = start - ram_end; idx++; } } /* Almost done: copy them over, ignoring the undesireable ones */ for (i = 0; i < nr; i++) { if ((src[i].type == E820_RAM) || (src[i].type == 0)) continue; e820[idx].type = src[i].type; e820[idx].addr = src[i].addr; e820[idx].size = src[i].size; idx++; } /* At this point we have the mapped RAM + E820 entries from src. */ if (balloon_kb || delta_kb) { /* and if we truncated the RAM region, then add it to the end. */ e820[idx].type = E820_RAM; e820[idx].addr = (uint64_t)(1ULL << 32) > last ? (uint64_t)(1ULL << 32) : last; /* also add the balloon memory to the end. */ e820[idx].size = (uint64_t)(delta_kb << 10) + (uint64_t)(balloon_kb << 10); idx++; } nr = idx; for (i = 0; i < nr; i++) { LOG(DEBUG, ":\t[%"PRIx64" -> %"PRIx64"] %s", e820[i].addr >> 12, (e820[i].addr + e820[i].size) >> 12, e820_names(e820[i].type)); } /* Done: copy the sanitized version. */ *nr_entries = nr; memcpy(src, e820, nr * sizeof(struct e820entry)); return 0; } static int e820_host_sanitize(libxl__gc *gc, libxl_domain_build_info *b_info, struct e820entry map[], uint32_t *nr) { int rc; rc = xc_get_machine_memory_map(CTX->xch, map, *nr); if (rc < 0) return ERROR_FAIL; *nr = rc; rc = e820_sanitize(gc, map, nr, b_info->target_memkb, (b_info->max_memkb - b_info->target_memkb) + b_info->u.pv.slack_memkb); return rc; } static int libxl__e820_alloc(libxl__gc *gc, uint32_t domid, libxl_domain_config *d_config) { libxl_ctx *ctx = libxl__gc_owner(gc); int rc; uint32_t nr; struct e820entry map[E820MAX]; libxl_domain_build_info *b_info; if (d_config == NULL || d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM) return ERROR_INVAL; b_info = &d_config->b_info; if (!libxl_defbool_val(b_info->u.pv.e820_host)) return ERROR_INVAL; nr = E820MAX; rc = e820_host_sanitize(gc, b_info, map, &nr); if (rc) return ERROR_FAIL; rc = xc_domain_set_memory_map(ctx->xch, domid, map, nr); if (rc < 0) return ERROR_FAIL; return 0; } int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid) { int ret = 0; int tsc_mode; uint32_t rtc_timeoffset; libxl_ctx *ctx = libxl__gc_owner(gc); if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_PV) xc_domain_set_memmap_limit(ctx->xch, domid, (d_config->b_info.max_memkb + d_config->b_info.u.pv.slack_memkb)); switch (d_config->b_info.tsc_mode) { case LIBXL_TSC_MODE_DEFAULT: tsc_mode = 0; break; case LIBXL_TSC_MODE_ALWAYS_EMULATE: tsc_mode = 1; break; case LIBXL_TSC_MODE_NATIVE: tsc_mode = 2; break; case LIBXL_TSC_MODE_NATIVE_PARAVIRT: tsc_mode = 3; break; default: abort(); } xc_domain_set_tsc_info(ctx->xch, domid, tsc_mode, 0, 0, 0); if (libxl_defbool_val(d_config->b_info.disable_migrate)) xc_domain_disable_migrate(ctx->xch, domid); rtc_timeoffset = d_config->b_info.rtc_timeoffset; if (libxl_defbool_val(d_config->b_info.localtime)) { time_t t; struct tm *tm, result; t = time(NULL); tm = localtime_r(&t, &result); if (!tm) { LOGED(ERROR, domid, "Failed to call localtime_r"); ret = ERROR_FAIL; goto out; } rtc_timeoffset += tm->tm_gmtoff; } if (rtc_timeoffset) xc_domain_set_time_offset(ctx->xch, domid, rtc_timeoffset); if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) { unsigned long shadow = DIV_ROUNDUP(d_config->b_info.shadow_memkb, 1024); xc_shadow_control(ctx->xch, domid, XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION, NULL, 0, &shadow, 0, NULL); } if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV && libxl_defbool_val(d_config->b_info.u.pv.e820_host)) { ret = libxl__e820_alloc(gc, domid, d_config); if (ret) { LOGED(ERROR, domid, "Failed while collecting E820 with: %d (errno:%d)\n", ret, errno); } } out: return ret; } int libxl__arch_extra_memory(libxl__gc *gc, const libxl_domain_build_info *info, uint64_t *out) { *out = LIBXL_MAXMEM_CONSTANT; return 0; } int libxl__arch_domain_init_hw_description(libxl__gc *gc, libxl_domain_build_info *info, libxl__domain_build_state *state, struct xc_dom_image *dom) { return 0; } int libxl__arch_domain_finalise_hw_description(libxl__gc *gc, libxl_domain_build_info *info, struct xc_dom_image *dom) { int rc = 0; if ((info->type == LIBXL_DOMAIN_TYPE_HVM) && (info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE)) { rc = libxl__dom_load_acpi(gc, info, dom); if (rc != 0) LOGE(ERROR, "libxl_dom_load_acpi failed"); } return rc; } /* Return 0 on success, ERROR_* on failure. */ int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *b_info, libxl__domain_build_state *state) { int nid, nr_vmemrange, rc; uint32_t nr_e820, e820_count; struct e820entry map[E820MAX]; xen_vmemrange_t *vmemranges; unsigned int array_size; /* If e820_host is not set, call the generic function */ if (!(b_info->type == LIBXL_DOMAIN_TYPE_PV && libxl_defbool_val(b_info->u.pv.e820_host))) return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, b_info, state); assert(state->vmemranges == NULL); nr_e820 = E820MAX; rc = e820_host_sanitize(gc, b_info, map, &nr_e820); if (rc) goto out; e820_count = 0; nr_vmemrange = 0; vmemranges = NULL; array_size = 0; for (nid = 0; nid < b_info->num_vnuma_nodes; nid++) { libxl_vnode_info *p = &b_info->vnuma_nodes[nid]; uint64_t remaining_bytes = (p->memkb << 10), bytes; while (remaining_bytes > 0) { if (e820_count >= nr_e820) { rc = ERROR_NOMEM; goto out; } /* Skip non RAM region */ if (map[e820_count].type != E820_RAM) { e820_count++; continue; } if (nr_vmemrange >= array_size) { array_size += 32; GCREALLOC_ARRAY(vmemranges, array_size); } bytes = map[e820_count].size >= remaining_bytes ? remaining_bytes : map[e820_count].size; vmemranges[nr_vmemrange].start = map[e820_count].addr; vmemranges[nr_vmemrange].end = map[e820_count].addr + bytes; if (map[e820_count].size >= remaining_bytes) { map[e820_count].addr += bytes; map[e820_count].size -= bytes; } else { e820_count++; } remaining_bytes -= bytes; vmemranges[nr_vmemrange].flags = 0; vmemranges[nr_vmemrange].nid = nid; nr_vmemrange++; } } state->vmemranges = vmemranges; state->num_vmemranges = nr_vmemrange; rc = 0; out: return rc; } int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq) { int ret; ret = xc_physdev_map_pirq(CTX->xch, domid, irq, &irq); if (ret) return ret; ret = xc_domain_irq_permission(CTX->xch, domid, irq, 1); return ret; } /* * Here we're just trying to set these kinds of e820 mappings: * * #1. Low memory region * * Low RAM starts at least from 1M to make sure all standard regions * of the PC memory map, like BIOS, VGA memory-mapped I/O and vgabios, * have enough space. * Note: Those stuffs below 1M are still constructed with multiple * e820 entries by hvmloader. At this point we don't change anything. * * #2. RDM region if it exists * * #3. High memory region if it exists * * Note: these regions are not overlapping since we already check * to adjust them. Please refer to libxl__domain_device_construct_rdm(). */ #define GUEST_LOW_MEM_START_DEFAULT 0x100000 int libxl__arch_domain_construct_memmap(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid, struct xc_dom_image *dom) { int rc = 0; unsigned int nr = 0, i; /* We always own at least one lowmem entry. */ unsigned int e820_entries = 1; struct e820entry *e820 = NULL; uint64_t highmem_size = dom->highmem_end ? dom->highmem_end - (1ull << 32) : 0; uint32_t lowmem_start = dom->device_model ? GUEST_LOW_MEM_START_DEFAULT : 0; unsigned page_size = XC_DOM_PAGE_SIZE(dom); /* Add all rdm entries. */ for (i = 0; i < d_config->num_rdms; i++) if (d_config->rdms[i].policy != LIBXL_RDM_RESERVE_POLICY_INVALID) e820_entries++; /* If we should have a highmem range. */ if (highmem_size) e820_entries++; for (i = 0; i < MAX_ACPI_MODULES; i++) if (dom->acpi_modules[i].length) e820_entries++; if (e820_entries >= E820MAX) { LOGD(ERROR, domid, "Ooops! Too many entries in the memory map!"); rc = ERROR_INVAL; goto out; } e820 = libxl__malloc(gc, sizeof(struct e820entry) * e820_entries); /* Low memory */ e820[nr].addr = lowmem_start; e820[nr].size = dom->lowmem_end - lowmem_start; e820[nr].type = E820_RAM; nr++; /* RDM mapping */ for (i = 0; i < d_config->num_rdms; i++) { if (d_config->rdms[i].policy == LIBXL_RDM_RESERVE_POLICY_INVALID) continue; e820[nr].addr = d_config->rdms[i].start; e820[nr].size = d_config->rdms[i].size; e820[nr].type = E820_RESERVED; nr++; } for (i = 0; i < MAX_ACPI_MODULES; i++) { if (dom->acpi_modules[i].length) { e820[nr].addr = dom->acpi_modules[i].guest_addr_out & ~(page_size - 1); e820[nr].size = dom->acpi_modules[i].length + (dom->acpi_modules[i].guest_addr_out & (page_size - 1)); e820[nr].type = E820_ACPI; nr++; } } /* High memory */ if (highmem_size) { e820[nr].addr = ((uint64_t)1 << 32); e820[nr].size = highmem_size; e820[nr].type = E820_RAM; } if (xc_domain_set_memory_map(CTX->xch, domid, e820, e820_entries) != 0) { rc = ERROR_FAIL; goto out; } out: return rc; } void libxl__arch_domain_build_info_acpi_setdefault( libxl_domain_build_info *b_info) { libxl_defbool_setdefault(&b_info->acpi, true); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/check-xl-vcpupin-parse.data-example0000664000175000017500000000303313256712137021502 0ustar smbsmb# WARNING: some of these tests are topology based tests. # Expect failures if the topology is not detected correctly # detected topology: 16 CPUs, 2 nodes, 8 CPUs per node. # # seed used for random number generation: seed=13328. # # Format is as follows: # test-string*expected-return-code*expected-output # # Testing a wrong configuration foo*255* # Testing the 'all' syntax all*0*cpumap: all nodes:all*0*cpumap: all all,nodes:all*0*cpumap: all all,^nodes:0,all*0*cpumap: all # Testing the empty cpumap case ^0*0*cpumap: none # A few attempts of pinning to just one random cpu 0*0*cpumap: 0 9*0*cpumap: 9 6*0*cpumap: 6 0*0*cpumap: 0 # A few attempts of pinning to all but one random cpu all,^12*0*cpumap: 0-11,13-15 all,^6*0*cpumap: 0-5,7-15 all,^3*0*cpumap: 0-2,4-15 all,^7*0*cpumap: 0-6,8-15 # A few attempts of pinning to a random range of cpus 13-15*0*cpumap: 13-15 7*0*cpumap: 7 3-5*0*cpumap: 3-5 8-11*0*cpumap: 8-11 # A few attempts of pinning to just one random node nodes:1*0*cpumap: 8-15 nodes:0*0*cpumap: 0-7 nodes:0*0*cpumap: 0-7 nodes:0*0*cpumap: 0-7 # A few attempts of pinning to all but one random node all,^nodes:0*0*cpumap: 8-15 all,^nodes:1*0*cpumap: 0-7 all,^nodes:1*0*cpumap: 0-7 all,^nodes:0*0*cpumap: 8-15 # A few attempts of pinning to a random range of nodes nodes:1-1*0*cpumap: 8-15 nodes:1-1*0*cpumap: 8-15 nodes:0-1*0*cpumap: all nodes:0-0*0*cpumap: 0-7 # A few attempts of pinning to a node but excluding one random cpu nodes:1,^8*0*cpumap: 9-15 nodes:0,^6*0*cpumap: 0-5,7 nodes:1,^9*0*cpumap: 8,10-15 nodes:0,^5*0*cpumap: 0-4,6-7 xen-4.9.2/tools/libxl/test_timedereg.c0000664000175000017500000000031013256712137016071 0ustar smbsmb#include "test_common.h" #include "libxl_test_timedereg.h" int main(int argc, char **argv) { int rc; test_common_setup(XTL_DEBUG); rc = libxl_test_timedereg(ctx, 0); assert(!rc); } xen-4.9.2/tools/libxl/libxl_vtpm.c0000664000175000017500000002566413256712137015270 0ustar smbsmb/* * Copyright (C) 2016 SUSE Linux GmbH * Author Juergen Gross * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" static int libxl__device_vtpm_setdefault(libxl__gc *gc, libxl_device_vtpm *vtpm) { int rc; if (libxl_uuid_is_nil(&vtpm->uuid)) { libxl_uuid_generate(&vtpm->uuid); } rc = libxl__resolve_domid(gc, vtpm->backend_domname, &vtpm->backend_domid); return rc; } static int libxl__device_from_vtpm(libxl__gc *gc, uint32_t domid, libxl_device_vtpm *vtpm, libxl__device *device) { device->backend_devid = vtpm->devid; device->backend_domid = vtpm->backend_domid; device->backend_kind = LIBXL__DEVICE_KIND_VTPM; device->devid = vtpm->devid; device->domid = domid; device->kind = LIBXL__DEVICE_KIND_VTPM; return 0; } static void libxl__update_config_vtpm(libxl__gc *gc, libxl_device_vtpm *dst, libxl_device_vtpm *src) { dst->devid = src->devid; libxl_uuid_copy(CTX, &dst->uuid, &src->uuid); } static void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid, libxl_device_vtpm *vtpm, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); flexarray_t *front; flexarray_t *back; libxl__device *device; int rc; xs_transaction_t t = XBT_NULL; libxl_domain_config d_config; libxl_device_vtpm vtpm_saved; libxl__domain_userdata_lock *lock = NULL; libxl_domain_config_init(&d_config); libxl_device_vtpm_init(&vtpm_saved); libxl_device_vtpm_copy(CTX, &vtpm_saved, vtpm); rc = libxl__device_vtpm_setdefault(gc, vtpm); if (rc) goto out; front = flexarray_make(gc, 16, 1); back = flexarray_make(gc, 16, 1); if (vtpm->devid == -1) { if ((vtpm->devid = libxl__device_nextid(gc, domid, "vtpm")) < 0) { rc = ERROR_FAIL; goto out; } } libxl__update_config_vtpm(gc, &vtpm_saved, vtpm); GCNEW(device); rc = libxl__device_from_vtpm(gc, domid, vtpm, device); if ( rc != 0 ) goto out; flexarray_append(back, "frontend-id"); flexarray_append(back, GCSPRINTF("%d", domid)); flexarray_append(back, "online"); flexarray_append(back, "1"); flexarray_append(back, "state"); flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append(back, "handle"); flexarray_append(back, GCSPRINTF("%d", vtpm->devid)); flexarray_append(back, "uuid"); flexarray_append(back, GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(vtpm->uuid))); flexarray_append(back, "resume"); flexarray_append(back, "False"); flexarray_append(front, "backend-id"); flexarray_append(front, GCSPRINTF("%d", vtpm->backend_domid)); flexarray_append(front, "state"); flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append(front, "handle"); flexarray_append(front, GCSPRINTF("%d", vtpm->devid)); if (aodev->update_json) { lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc) goto out; DEVICE_ADD(vtpm, vtpms, domid, &vtpm_saved, COMPARE_DEVID, &d_config); rc = libxl__dm_check_start(gc, &d_config, domid); if (rc) goto out; } for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; rc = libxl__device_exists(gc, t, device); if (rc < 0) goto out; if (rc == 1) { /* already exists in xenstore */ LOGD(ERROR, domid, "device already exists in xenstore"); aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */ rc = ERROR_DEVICE_EXISTS; goto out; } if (aodev->update_json) { rc = libxl__set_domain_configuration(gc, domid, &d_config); if (rc) goto out; } libxl__device_generic_add(gc, t, device, libxl__xs_kvs_of_flexarray(gc, back), libxl__xs_kvs_of_flexarray(gc, front), NULL); rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } aodev->dev = device; aodev->action = LIBXL__DEVICE_ACTION_ADD; libxl__wait_device_connection(egc, aodev); rc = 0; out: libxl__xs_transaction_abort(gc, &t); if (lock) libxl__unlock_domain_userdata(lock); libxl_device_vtpm_dispose(&vtpm_saved); libxl_domain_config_dispose(&d_config); aodev->rc = rc; if(rc) aodev->callback(egc, aodev); return; } libxl_device_vtpm *libxl_device_vtpm_list(libxl_ctx *ctx, uint32_t domid, int *num) { GC_INIT(ctx); libxl_device_vtpm* vtpms = NULL; char *libxl_path; char** dir = NULL; unsigned int ndirs = 0; int rc; *num = 0; libxl_path = GCSPRINTF("%s/device/vtpm", libxl__xs_libxl_path(gc, domid)); dir = libxl__xs_directory(gc, XBT_NULL, libxl_path, &ndirs); if (dir && ndirs) { vtpms = malloc(sizeof(*vtpms) * ndirs); libxl_device_vtpm* vtpm; libxl_device_vtpm* end = vtpms + ndirs; for(vtpm = vtpms; vtpm < end; ++vtpm, ++dir) { char* tmp; const char* be_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s/backend", libxl_path, *dir)); libxl_device_vtpm_init(vtpm); vtpm->devid = atoi(*dir); rc = libxl__backendpath_parse_domid(gc, be_path, &vtpm->backend_domid); if (rc) return NULL; tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/uuid", libxl_path)); if (tmp) { if(libxl_uuid_from_string(&(vtpm->uuid), tmp)) { LOGD(ERROR, domid, "%s/uuid is a malformed uuid?? (%s) Probably a bug!!\n", be_path, tmp); free(vtpms); return NULL; } } } } *num = ndirs; GC_FREE; return vtpms; } int libxl_device_vtpm_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm, libxl_vtpminfo *vtpminfo) { GC_INIT(ctx); char *libxl_path, *dompath, *vtpmpath; char *val; int rc = 0; libxl_vtpminfo_init(vtpminfo); dompath = libxl__xs_get_dompath(gc, domid); vtpminfo->devid = vtpm->devid; vtpmpath = GCSPRINTF("%s/device/vtpm/%d", dompath, vtpminfo->devid); libxl_path = GCSPRINTF("%s/device/vtpm/%d", libxl__xs_libxl_path(gc, domid), vtpminfo->devid); vtpminfo->backend = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/backend", libxl_path), NULL); if (!vtpminfo->backend) { goto err; } rc = libxl__backendpath_parse_domid(gc, vtpminfo->backend, &vtpminfo->backend_id); if (rc) goto exit; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", vtpmpath)); vtpminfo->state = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", vtpmpath)); vtpminfo->evtch = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", vtpmpath)); vtpminfo->rref = val ? strtoul(val, NULL, 10) : -1; vtpminfo->frontend = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/frontend", libxl_path), NULL); vtpminfo->frontend_id = domid; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/uuid", libxl_path)); if(val == NULL) { LOGD(ERROR, domid, "%s/uuid does not exist!", vtpminfo->backend); goto err; } if(libxl_uuid_from_string(&(vtpminfo->uuid), val)) { LOGD(ERROR, domid, "%s/uuid is a malformed uuid?? (%s) Probably a bug!\n", vtpminfo->backend, val); goto err; } goto exit; err: rc = ERROR_FAIL; exit: GC_FREE; return rc; } int libxl_devid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid, int devid, libxl_device_vtpm *vtpm) { libxl_device_vtpm *vtpms; int nb, i; int rc; vtpms = libxl_device_vtpm_list(ctx, domid, &nb); if (!vtpms) return ERROR_FAIL; libxl_device_vtpm_init(vtpm); rc = 1; for (i = 0; i < nb; ++i) { if(devid == vtpms[i].devid) { vtpm->backend_domid = vtpms[i].backend_domid; vtpm->devid = vtpms[i].devid; libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid); rc = 0; break; } } libxl_device_vtpm_list_free(vtpms, nb); return rc; } static int libxl_device_vtpm_compare(libxl_device_vtpm *d1, libxl_device_vtpm *d2) { return COMPARE_DEVID(d1, d2); } int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid, libxl_uuid* uuid, libxl_device_vtpm *vtpm) { libxl_device_vtpm *vtpms; int nb, i; int rc; vtpms = libxl_device_vtpm_list(ctx, domid, &nb); if (!vtpms) return ERROR_FAIL; memset(vtpm, 0, sizeof (libxl_device_vtpm)); rc = 1; for (i = 0; i < nb; ++i) { if(!libxl_uuid_compare(uuid, &vtpms[i].uuid)) { vtpm->backend_domid = vtpms[i].backend_domid; vtpm->devid = vtpms[i].devid; libxl_uuid_copy(ctx, &vtpm->uuid, &vtpms[i].uuid); rc = 0; break; } } libxl_device_vtpm_list_free(vtpms, nb); return rc; } void libxl_vtpminfo_list_free(libxl_vtpminfo* list, int nr) { int i; for (i = 0; i < nr; i++) libxl_vtpminfo_dispose(&list[i]); free(list); } void libxl_device_vtpm_list_free(libxl_device_vtpm* list, int nr) { int i; for (i = 0; i < nr; i++) libxl_device_vtpm_dispose(&list[i]); free(list); } static void libxl_device_vtpm_update_config(libxl__gc *gc, void *d, void *s) { libxl__update_config_vtpm(gc, d, s); } LIBXL_DEFINE_DEVICE_ADD(vtpm) static LIBXL_DEFINE_DEVICES_ADD(vtpm) LIBXL_DEFINE_DEVICE_REMOVE(vtpm) DEFINE_DEVICE_TYPE_STRUCT(vtpm, .update_config = libxl_device_vtpm_update_config ); /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_9pfs.c0000664000175000017500000000535313256712137015154 0ustar smbsmb/* * Copyright (C) 2017 Aporeto * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include "libxl_internal.h" int libxl__device_p9_setdefault(libxl__gc *gc, libxl_device_p9 *p9) { int rc; rc = libxl__resolve_domid(gc, p9->backend_domname, &p9->backend_domid); return rc; } static int libxl__device_from_p9(libxl__gc *gc, uint32_t domid, libxl_device_p9 *p9, libxl__device *device) { device->backend_devid = p9->devid; device->backend_domid = p9->backend_domid; device->backend_kind = LIBXL__DEVICE_KIND_9PFS; device->devid = p9->devid; device->domid = domid; device->kind = LIBXL__DEVICE_KIND_9PFS; return 0; } int libxl__device_p9_add(libxl__gc *gc, uint32_t domid, libxl_device_p9 *p9) { flexarray_t *front; flexarray_t *back; libxl__device device; int rc; rc = libxl__device_p9_setdefault(gc, p9); if (rc) goto out; front = flexarray_make(gc, 16, 1); back = flexarray_make(gc, 16, 1); if (p9->devid == -1) { if ((p9->devid = libxl__device_nextid(gc, domid, "9pfs")) < 0) { rc = ERROR_FAIL; goto out; } } rc = libxl__device_from_p9(gc, domid, p9, &device); if (rc != 0) goto out; flexarray_append_pair(back, "frontend-id", libxl__sprintf(gc, "%d", domid)); flexarray_append_pair(back, "online", "1"); flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append_pair(front, "backend-id", libxl__sprintf(gc, "%d", p9->backend_domid)); flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append_pair(front, "tag", p9->tag); flexarray_append_pair(back, "path", p9->path); flexarray_append_pair(back, "security_model", p9->security_model); libxl__device_generic_add(gc, XBT_NULL, &device, libxl__xs_kvs_of_flexarray(gc, back), libxl__xs_kvs_of_flexarray(gc, front), NULL); rc = 0; out: return rc; } LIBXL_DEFINE_DEVICE_REMOVE(p9) xen-4.9.2/tools/libxl/libxlu_cfg_l.h0000664000175000017500000002211113256712137015526 0ustar smbsmb#ifndef xlu__cfg_yyHEADER_H #define xlu__cfg_yyHEADER_H 1 #define xlu__cfg_yyIN_HEADER 1 #line 6 "libxlu_cfg_l.h" #line 8 "libxlu_cfg_l.h" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 39 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ yy_size_t yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ void xlu__cfg_yyrestart (FILE *input_file ,yyscan_t yyscanner ); void xlu__cfg_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__cfg_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); void xlu__cfg_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void xlu__cfg_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); void xlu__cfg_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); void xlu__cfg_yypop_buffer_state (yyscan_t yyscanner ); YY_BUFFER_STATE xlu__cfg_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__cfg_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE xlu__cfg_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); void *xlu__cfg_yyalloc (yy_size_t ,yyscan_t yyscanner ); void *xlu__cfg_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void xlu__cfg_yyfree (void * ,yyscan_t yyscanner ); #define xlu__cfg_yywrap(yyscanner) 1 #define YY_SKIP_YYWRAP #define yytext_ptr yytext_r #ifdef YY_HEADER_EXPORT_START_CONDITIONS #define INITIAL 0 #define lexerr 1 #endif #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif int xlu__cfg_yylex_init (yyscan_t* scanner); int xlu__cfg_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int xlu__cfg_yylex_destroy (yyscan_t yyscanner ); int xlu__cfg_yyget_debug (yyscan_t yyscanner ); void xlu__cfg_yyset_debug (int debug_flag ,yyscan_t yyscanner ); YY_EXTRA_TYPE xlu__cfg_yyget_extra (yyscan_t yyscanner ); void xlu__cfg_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); FILE *xlu__cfg_yyget_in (yyscan_t yyscanner ); void xlu__cfg_yyset_in (FILE * in_str ,yyscan_t yyscanner ); FILE *xlu__cfg_yyget_out (yyscan_t yyscanner ); void xlu__cfg_yyset_out (FILE * out_str ,yyscan_t yyscanner ); yy_size_t xlu__cfg_yyget_leng (yyscan_t yyscanner ); char *xlu__cfg_yyget_text (yyscan_t yyscanner ); int xlu__cfg_yyget_lineno (yyscan_t yyscanner ); void xlu__cfg_yyset_lineno (int line_number ,yyscan_t yyscanner ); int xlu__cfg_yyget_column (yyscan_t yyscanner ); void xlu__cfg_yyset_column (int column_no ,yyscan_t yyscanner ); YYSTYPE * xlu__cfg_yyget_lval (yyscan_t yyscanner ); void xlu__cfg_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); YYLTYPE *xlu__cfg_yyget_lloc (yyscan_t yyscanner ); void xlu__cfg_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int xlu__cfg_yywrap (yyscan_t yyscanner ); #else extern int xlu__cfg_yywrap (yyscan_t yyscanner ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int xlu__cfg_yylex \ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); #define YY_DECL int xlu__cfg_yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* yy_get_previous_state - get the state just before the EOB char was reached */ #undef YY_NEW_FILE #undef YY_FLUSH_BUFFER #undef yy_set_bol #undef yy_new_buffer #undef yy_set_interactive #undef YY_DO_BEFORE_ACTION #ifdef YY_DECL_IS_OURS #undef YY_DECL_IS_OURS #undef YY_DECL #endif #line 103 "libxlu_cfg_l.l" #line 360 "libxlu_cfg_l.h" #undef xlu__cfg_yyIN_HEADER #endif /* xlu__cfg_yyHEADER_H */ xen-4.9.2/tools/libxl/libxl_event.c0000664000175000017500000020014613256712137015411 0ustar smbsmb/* * Copyright (C) 2011 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * Internal event machinery for use by other parts of libxl */ #include #include "libxl_internal.h" //#define DEBUG 1 #ifdef DEBUG # define LIBXL__DBG_LOG(ctx, args, ...) \ LIBXL__LOG((ctx), XTL_DEBUG, args, __VA_ARGS__) #else # define LIBXL__DBG_LOG(ctx, args, ...) ((void)0) #endif #define DBG(args, ...) LIBXL__DBG_LOG(CTX, args, __VA_ARGS__) static libxl__ao *ao_nested_root(libxl__ao *ao); static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao); /* * The counter osevent_in_hook is used to ensure that the application * honours the reentrancy restriction documented in libxl_event.h. * * The application's registration hooks should be called ONLY via * these macros, with the ctx locked. Likewise all the "occurred" * entrypoints from the application should assert(!in_hook); * * During the hook call - including while the arguments are being * evaluated - ev->nexus is guaranteed to be valid and refer to the * nexus which is being used for this event registration. The * arguments should specify ev->nexus for the for_libxl argument and * ev->nexus->for_app_reg (or a pointer to it) for for_app_reg. */ #define OSEVENT_HOOK_INTERN(retval, failedp, evkind, hookop, nexusop, ...) do { \ if (CTX->osevent_hooks) { \ CTX->osevent_in_hook++; \ libxl__osevent_hook_nexi *nexi = &CTX->hook_##evkind##_nexi_idle; \ osevent_hook_pre_##nexusop(gc, ev, nexi, &ev->nexus); \ retval CTX->osevent_hooks->evkind##_##hookop \ (CTX->osevent_user, __VA_ARGS__); \ if ((failedp)) \ osevent_hook_failed_##nexusop(gc, ev, nexi, &ev->nexus); \ CTX->osevent_in_hook--; \ } \ } while (0) #define OSEVENT_HOOK(evkind, hookop, nexusop, ...) ({ \ int osevent_hook_rc = 0; \ OSEVENT_HOOK_INTERN(osevent_hook_rc =, !!osevent_hook_rc, \ evkind, hookop, nexusop, __VA_ARGS__); \ osevent_hook_rc; \ }) #define OSEVENT_HOOK_VOID(evkind, hookop, nexusop, ...) \ OSEVENT_HOOK_INTERN(/* void */, 0, evkind, hookop, nexusop, __VA_ARGS__) /* * The application's calls to libxl_osevent_occurred_... may be * indefinitely delayed with respect to the rest of the program (since * they are not necessarily called with any lock held). So the * for_libxl value we receive may be (almost) arbitrarily old. All we * know is that it came from this ctx. * * Therefore we may not free the object referred to by any for_libxl * value until we free the whole libxl_ctx. And if we reuse it we * must be able to tell when an old use turns up, and discard the * stale event. * * Thus we cannot use the ev directly as the for_libxl value - we need * a layer of indirection. * * We do this by keeping a pool of libxl__osevent_hook_nexus structs, * and use pointers to them as for_libxl values. In fact, there are * two pools: one for fds and one for timeouts. This ensures that we * don't risk a type error when we upcast nexus->ev. In each nexus * the ev is either null or points to a valid libxl__ev_time or * libxl__ev_fd, as applicable. * * We /do/ allow ourselves to reassociate an old nexus with a new ev * as otherwise we would have to leak nexi. (This reassociation * might, of course, be an old ev being reused for a new purpose so * simply comparing the ev pointer is not sufficient.) Thus the * libxl_osevent_occurred functions need to check that the condition * allegedly signalled by this event actually exists. * * The nexi and the lists are all protected by the ctx lock. */ struct libxl__osevent_hook_nexus { void *ev; void *for_app_reg; LIBXL_SLIST_ENTRY(libxl__osevent_hook_nexus) next; }; static void *osevent_ev_from_hook_nexus(libxl_ctx *ctx, libxl__osevent_hook_nexus *nexus /* pass void *for_libxl */) { return nexus->ev; } static void osevent_release_nexus(libxl__gc *gc, libxl__osevent_hook_nexi *nexi_idle, libxl__osevent_hook_nexus *nexus) { nexus->ev = 0; LIBXL_SLIST_INSERT_HEAD(nexi_idle, nexus, next); } /*----- OSEVENT* hook functions for nexusop "alloc" -----*/ static void osevent_hook_pre_alloc(libxl__gc *gc, void *ev, libxl__osevent_hook_nexi *nexi_idle, libxl__osevent_hook_nexus **nexus_r) { libxl__osevent_hook_nexus *nexus = LIBXL_SLIST_FIRST(nexi_idle); if (nexus) { LIBXL_SLIST_REMOVE_HEAD(nexi_idle, next); } else { nexus = libxl__zalloc(NOGC, sizeof(*nexus)); } nexus->ev = ev; *nexus_r = nexus; } static void osevent_hook_failed_alloc(libxl__gc *gc, void *ev, libxl__osevent_hook_nexi *nexi_idle, libxl__osevent_hook_nexus **nexus) { osevent_release_nexus(gc, nexi_idle, *nexus); } /*----- OSEVENT* hook functions for nexusop "release" -----*/ static void osevent_hook_pre_release(libxl__gc *gc, void *ev, libxl__osevent_hook_nexi *nexi_idle, libxl__osevent_hook_nexus **nexus) { osevent_release_nexus(gc, nexi_idle, *nexus); } static void osevent_hook_failed_release(libxl__gc *gc, void *ev, libxl__osevent_hook_nexi *nexi_idle, libxl__osevent_hook_nexus **nexus) { abort(); } /*----- OSEVENT* hook functions for nexusop "noop" -----*/ static void osevent_hook_pre_noop(libxl__gc *gc, void *ev, libxl__osevent_hook_nexi *nexi_idle, libxl__osevent_hook_nexus **nexus) { } static void osevent_hook_failed_noop(libxl__gc *gc, void *ev, libxl__osevent_hook_nexi *nexi_idle, libxl__osevent_hook_nexus **nexus) { } /* * fd events */ int libxl__ev_fd_register(libxl__gc *gc, libxl__ev_fd *ev, libxl__ev_fd_callback *func, int fd, short events) { int rc; assert(fd >= 0); CTX_LOCK; DBG("ev_fd=%p register fd=%d events=%x", ev, fd, events); rc = OSEVENT_HOOK(fd,register, alloc, fd, &ev->nexus->for_app_reg, events, ev->nexus); if (rc) goto out; ev->fd = fd; ev->events = events; ev->func = func; LIBXL_LIST_INSERT_HEAD(&CTX->efds, ev, entry); rc = 0; out: CTX_UNLOCK; return rc; } int libxl__ev_fd_modify(libxl__gc *gc, libxl__ev_fd *ev, short events) { int rc; CTX_LOCK; assert(libxl__ev_fd_isregistered(ev)); DBG("ev_fd=%p modify fd=%d events=%x", ev, ev->fd, events); rc = OSEVENT_HOOK(fd,modify, noop, ev->fd, &ev->nexus->for_app_reg, events); if (rc) goto out; ev->events = events; rc = 0; out: CTX_UNLOCK; return rc; } void libxl__ev_fd_deregister(libxl__gc *gc, libxl__ev_fd *ev) { CTX_LOCK; libxl__poller *poller; if (!libxl__ev_fd_isregistered(ev)) { DBG("ev_fd=%p deregister unregistered",ev); goto out; } DBG("ev_fd=%p deregister fd=%d", ev, ev->fd); OSEVENT_HOOK_VOID(fd,deregister, release, ev->fd, ev->nexus->for_app_reg); LIBXL_LIST_REMOVE(ev, entry); ev->fd = -1; LIBXL_LIST_FOREACH(poller, &CTX->pollers_fds_changed, fds_changed_entry) poller->fds_changed = 1; out: CTX_UNLOCK; } short libxl__fd_poll_recheck(libxl__egc *egc, int fd, short events) { struct pollfd check; int r; for (;;) { check.fd = fd; check.events = events; r = poll(&check, 1, 0); DBG("poll recheck fd=%d r=%d revents=%#x", fd, r, check.revents); if (!r) break; if (r==1) break; assert(r<0); if (errno != EINTR) { LIBXL__EVENT_DISASTER(egc, "failed poll to check for fd", errno, 0); return 0; } } assert(!!r == !!check.revents); return check.revents; } /* * timeouts */ int libxl__gettimeofday(libxl__gc *gc, struct timeval *now_r) { int rc = gettimeofday(now_r, 0); if (rc) { LOGE(ERROR, "gettimeofday failed"); return ERROR_FAIL; } return 0; } static int time_rel_to_abs(libxl__gc *gc, int ms, struct timeval *abs_out) { int rc; struct timeval additional = { .tv_sec = ms / 1000, .tv_usec = (ms % 1000) * 1000 }; struct timeval now; rc = libxl__gettimeofday(gc, &now); if (rc) return rc; timeradd(&now, &additional, abs_out); return 0; } static int time_register_finite(libxl__gc *gc, libxl__ev_time *ev, struct timeval absolute) { int rc; libxl__ev_time *evsearch; rc = OSEVENT_HOOK(timeout,register, alloc, &ev->nexus->for_app_reg, absolute, ev->nexus); if (rc) return rc; ev->infinite = 0; ev->abs = absolute; LIBXL_TAILQ_INSERT_SORTED(&CTX->etimes, entry, ev, evsearch, /*empty*/, timercmp(&ev->abs, &evsearch->abs, >)); return 0; } static void time_deregister(libxl__gc *gc, libxl__ev_time *ev) { libxl__ao_abortable_deregister(&ev->abrt); if (!ev->infinite) { struct timeval right_away = { 0, 0 }; if (ev->nexus) /* only set if app provided hooks */ ev->nexus->ev = 0; OSEVENT_HOOK_VOID(timeout,modify, noop /* release nexus in _occurred_ */, &ev->nexus->for_app_reg, right_away); LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry); } } static void time_done_debug(libxl__gc *gc, const char *func, libxl__ev_time *ev, int rc) { #ifdef DEBUG libxl__log(CTX, XTL_DEBUG, -1,__FILE__,0,func, "ev_time=%p done rc=%d .func=%p infinite=%d abs=%lu.%06lu", ev, rc, ev->func, ev->infinite, (unsigned long)ev->abs.tv_sec, (unsigned long)ev->abs.tv_usec); #endif } static void time_aborted(libxl__egc *egc, libxl__ao_abortable *abrt, int rc) { libxl__ev_time *ev = CONTAINER_OF(abrt, *ev, abrt); EGC_GC; time_deregister(gc, ev); DBG("ev_time=%p aborted", ev); ev->func(egc, ev, &ev->abs, rc); } static int time_register_abortable(libxl__ao *ao, libxl__ev_time *ev) { ev->abrt.ao = ao; ev->abrt.callback = time_aborted; return libxl__ao_abortable_register(&ev->abrt); } int libxl__ev_time_register_abs(libxl__ao *ao, libxl__ev_time *ev, libxl__ev_time_callback *func, struct timeval absolute) { AO_GC; int rc; CTX_LOCK; DBG("ev_time=%p register abs=%lu.%06lu", ev, (unsigned long)absolute.tv_sec, (unsigned long)absolute.tv_usec); rc = time_register_abortable(ao, ev); if (rc) goto out; rc = time_register_finite(gc, ev, absolute); if (rc) goto out; ev->func = func; rc = 0; out: libxl__ao_abortable_deregister(&ev->abrt); time_done_debug(gc,__func__,ev,rc); CTX_UNLOCK; return rc; } int libxl__ev_time_register_rel(libxl__ao *ao, libxl__ev_time *ev, libxl__ev_time_callback *func, int milliseconds /* as for poll(2) */) { AO_GC; struct timeval absolute; int rc; CTX_LOCK; DBG("ev_time=%p register ms=%d", ev, milliseconds); rc = time_register_abortable(ao, ev); if (rc) goto out; if (milliseconds < 0) { ev->infinite = 1; } else { rc = time_rel_to_abs(gc, milliseconds, &absolute); if (rc) goto out; rc = time_register_finite(gc, ev, absolute); if (rc) goto out; } ev->func = func; rc = 0; out: if (!libxl__ev_time_isregistered(ev)) libxl__ao_abortable_deregister(&ev->abrt); time_done_debug(gc,__func__,ev,rc); CTX_UNLOCK; return rc; } void libxl__ev_time_deregister(libxl__gc *gc, libxl__ev_time *ev) { CTX_LOCK; DBG("ev_time=%p deregister", ev); if (!libxl__ev_time_isregistered(ev)) goto out; time_deregister(gc, ev); ev->func = 0; out: time_done_debug(gc,__func__,ev,0); CTX_UNLOCK; return; } static void time_occurs(libxl__egc *egc, libxl__ev_time *etime, int rc) { DBG("ev_time=%p occurs abs=%lu.%06lu", etime, (unsigned long)etime->abs.tv_sec, (unsigned long)etime->abs.tv_usec); libxl__ev_time_callback *func = etime->func; etime->func = 0; func(egc, etime, &etime->abs, rc); } /* * xenstore watches */ libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum) { libxl__ev_watch_slot *slot = &CTX->watch_slots[slotnum]; libxl__ev_watch_slot *slotcontents = LIBXL_SLIST_NEXT(slot, empty); if (slotcontents == NULL || ((uintptr_t)slotcontents >= (uintptr_t)CTX->watch_slots && (uintptr_t)slotcontents < (uintptr_t)(CTX->watch_slots + CTX->watch_nslots))) /* An empty slot has either a NULL pointer (end of the * free list), or a pointer to another entry in the array. * So we can do a bounds check to distinguish empty from * full slots. */ /* We need to do the comparisons as uintptr_t because * comparing pointers which are not in the same object is * undefined behaviour; if the compiler managed to figure * out that watch_slots[0..watch_nslots-1] is all of the * whole array object it could prove that the above bounds * check was always true if it was legal, and remove it! * * uintptr_t because even on a machine with signed * pointers, objects do not cross zero; whereas on * machines with unsigned pointers, they may cross * 0x8bazillion. */ return NULL; /* see comment near libxl__ev_watch_slot definition */ return (void*)slotcontents; } static void libxl__set_watch_slot_contents(libxl__ev_watch_slot *slot, libxl__ev_xswatch *w) { /* we look a bit behind the curtain of LIBXL_SLIST, to explicitly * assign to the pointer that's the next link. See the comment * by the definition of libxl__ev_watch_slot */ slot->empty.sle_next = (void*)w; } static void watchfd_callback(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { EGC_GC; if (revents & (POLLERR|POLLHUP)) LIBXL__EVENT_DISASTER(egc, "unexpected poll event on watch fd", 0, 0); for (;;) { char **event = xs_check_watch(CTX->xsh); if (!event) { if (errno == EAGAIN) break; if (errno == EINTR) continue; LIBXL__EVENT_DISASTER(egc, "cannot check/read watches", errno, 0); return; } const char *epath = event[0]; const char *token = event[1]; int slotnum; uint32_t counterval; int rc = sscanf(token, "%d/%"SCNx32, &slotnum, &counterval); if (rc != 2) { LOG(ERROR, "watch epath=%s token=%s: failed to parse token", epath, token); /* oh well */ goto ignore; } if (slotnum < 0 || slotnum >= CTX->watch_nslots) { /* perhaps in the future we will make the watchslots array shrink */ LIBXL__LOG(CTX, LIBXL__LOG_DEBUG, "watch epath=%s token=%s:" " slotnum %d out of range [0,%d>", epath, token, slotnum, CTX->watch_nslots); goto ignore; } libxl__ev_xswatch *w = libxl__watch_slot_contents(gc, slotnum); if (!w) { LOG(DEBUG, "watch epath=%s token=%s: empty slot", epath, token); goto ignore; } if (w->counterval != counterval) { LOG(DEBUG, "watch w=%p epath=%s token=%s: counter != %"PRIx32, w, epath, token, w->counterval); goto ignore; } /* Now it's possible, though unlikely, that this was an event * from a previous use of the same slot with the same counterval. * * In that case either: * - the event path is a child of the watch path, in * which case this watch would really have generated this * event if it had been registered soon enough and we are * OK to give this possibly-spurious event to the caller; or * - it is not, in which case we must suppress it as the * caller should not see events for unrelated paths. * * See also docs/misc/xenstore.txt. */ if (!xs_path_is_subpath(w->path, epath)) { LOG(DEBUG, "watch w=%p wpath=%s token=%s: unexpected epath=%s", w, w->path, token, epath); goto ignore; } /* At last, we have checked everything! */ LOG(DEBUG, "watch w=%p wpath=%s token=%s: event epath=%s", w, w->path, token, epath); w->callback(egc, w, w->path, epath); ignore: free(event); } } static char *watch_token(libxl__gc *gc, int slotnum, uint32_t counterval) { return GCSPRINTF("%d/%"PRIx32, slotnum, counterval); } static void watches_check_fd_deregister(libxl__gc *gc) { assert(CTX->nwatches>=0); if (!CTX->nwatches) libxl__ev_fd_deregister(gc, &CTX->watch_efd); } int libxl__ev_xswatch_register(libxl__gc *gc, libxl__ev_xswatch *w, libxl__ev_xswatch_callback *func, const char *path /* copied */) { libxl__ev_watch_slot *use = NULL; char *path_copy = NULL; int rc; CTX_LOCK; if (!libxl__ev_fd_isregistered(&CTX->watch_efd)) { rc = libxl__ev_fd_register(gc, &CTX->watch_efd, watchfd_callback, xs_fileno(CTX->xsh), POLLIN); if (rc) goto out_rc; } if (LIBXL_SLIST_EMPTY(&CTX->watch_freeslots)) { /* Free list is empty so there is not in fact a linked * free list in the array and we can safely realloc it */ int newarraysize = (CTX->watch_nslots + 1) << 2; int i; libxl__ev_watch_slot *newarray = libxl__realloc(NOGC, CTX->watch_slots, sizeof(*newarray) * newarraysize); if (!newarray) goto out_nomem; for (i = CTX->watch_nslots; i < newarraysize; i++) LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, &newarray[i], empty); CTX->watch_slots = newarray; CTX->watch_nslots = newarraysize; } use = LIBXL_SLIST_FIRST(&CTX->watch_freeslots); assert(use); LIBXL_SLIST_REMOVE_HEAD(&CTX->watch_freeslots, empty); path_copy = strdup(path); if (!path_copy) goto out_nomem; int slotnum = use - CTX->watch_slots; w->counterval = CTX->watch_counter++; const char *token = watch_token(gc, slotnum, w->counterval); LOG(DEBUG, "watch w=%p wpath=%s token=%s: register slotnum=%d", w, path, token, slotnum); if (!xs_watch(CTX->xsh, path, token)) { LOGEV(ERROR, errno, "create watch for path %s", path); rc = ERROR_FAIL; goto out_rc; } w->slotnum = slotnum; w->path = path_copy; w->callback = func; CTX->nwatches++; libxl__set_watch_slot_contents(use, w); CTX_UNLOCK; return 0; out_nomem: rc = ERROR_NOMEM; out_rc: if (use) LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, use, empty); free(path_copy); watches_check_fd_deregister(gc); CTX_UNLOCK; return rc; } void libxl__ev_xswatch_deregister(libxl__gc *gc, libxl__ev_xswatch *w) { /* it is legal to deregister from within _callback */ CTX_LOCK; if (w->slotnum >= 0) { const char *token = watch_token(gc, w->slotnum, w->counterval); LOG(DEBUG, "watch w=%p wpath=%s token=%s: deregister slotnum=%d", w, w->path, token, w->slotnum); if (!xs_unwatch(CTX->xsh, w->path, token)) /* Oh well, we will just get watch events forever more * and ignore them. But we should complain to the log. */ LOGEV(ERROR, errno, "remove watch for path %s", w->path); libxl__ev_watch_slot *slot = &CTX->watch_slots[w->slotnum]; LIBXL_SLIST_INSERT_HEAD(&CTX->watch_freeslots, slot, empty); w->slotnum = -1; CTX->nwatches--; watches_check_fd_deregister(gc); } else { LOG(DEBUG, "watch w=%p: deregister unregistered", w); } free(w->path); w->path = NULL; CTX_UNLOCK; } /* * evtchn */ static int evtchn_revents_check(libxl__egc *egc, int revents) { EGC_GC; if (revents & ~POLLIN) { LOG(ERROR, "unexpected poll event on event channel fd: %x", revents); LIBXL__EVENT_DISASTER(egc, "unexpected poll event on event channel fd", 0, 0); libxl__ev_fd_deregister(gc, &CTX->evtchn_efd); return ERROR_FAIL; } assert(revents & POLLIN); return 0; } static void evtchn_fd_callback(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { EGC_GC; libxl__ev_evtchn *evev; int rc; xenevtchn_port_or_error_t port; rc = evtchn_revents_check(egc, revents); if (rc) return; for (;;) { /* Check the fd again. The incoming revent may no longer be * true, because the libxl ctx lock has not necessarily been * held continuously since someone noticed the fd. Normally * this wouldn't be a problem but evtchn devices don't always * honour O_NONBLOCK (see xenctrl.h). */ revents = libxl__fd_poll_recheck(egc,fd,POLLIN); if (!revents) break; rc = evtchn_revents_check(egc, revents); if (rc) return; /* OK, that's that workaround done. We can actually check for * work for us to do: */ port = xenevtchn_pending(CTX->xce); if (port < 0) { if (errno == EAGAIN) break; LIBXL__EVENT_DISASTER(egc, "unexpected failure fetching occurring event port number from evtchn", errno, 0); return; } LIBXL_LIST_FOREACH(evev, &CTX->evtchns_waiting, entry) if (port == evev->port) goto found; /* not found */ DBG("ev_evtchn port=%d no-one cared", port); continue; found: DBG("ev_evtchn=%p port=%d signaled", evev, port); evev->waiting = 0; LIBXL_LIST_REMOVE(evev, entry); evev->callback(egc, evev); } } int libxl__ctx_evtchn_init(libxl__gc *gc) { xenevtchn_handle *xce; int rc, fd; if (CTX->xce) return 0; xce = xenevtchn_open(CTX->lg, 0); if (!xce) { LOGE(ERROR,"cannot open libxc evtchn handle"); rc = ERROR_FAIL; goto out; } fd = xenevtchn_fd(xce); assert(fd >= 0); rc = libxl_fd_set_nonblock(CTX, fd, 1); if (rc) goto out; CTX->xce = xce; return 0; out: xenevtchn_close(xce); return rc; } static void evtchn_check_fd_deregister(libxl__gc *gc) { if (CTX->xce && LIBXL_LIST_EMPTY(&CTX->evtchns_waiting)) libxl__ev_fd_deregister(gc, &CTX->evtchn_efd); } int libxl__ev_evtchn_wait(libxl__gc *gc, libxl__ev_evtchn *evev) { int r, rc; DBG("ev_evtchn=%p port=%d wait (was waiting=%d)", evev, evev->port, evev->waiting); rc = libxl__ctx_evtchn_init(gc); if (rc) goto out; if (!libxl__ev_fd_isregistered(&CTX->evtchn_efd)) { rc = libxl__ev_fd_register(gc, &CTX->evtchn_efd, evtchn_fd_callback, xenevtchn_fd(CTX->xce), POLLIN); if (rc) goto out; } if (evev->waiting) return 0; r = xenevtchn_unmask(CTX->xce, evev->port); if (r) { LOGE(ERROR,"cannot unmask event channel %d",evev->port); rc = ERROR_FAIL; goto out; } evev->waiting = 1; LIBXL_LIST_INSERT_HEAD(&CTX->evtchns_waiting, evev, entry); return 0; out: evtchn_check_fd_deregister(gc); return rc; } void libxl__ev_evtchn_cancel(libxl__gc *gc, libxl__ev_evtchn *evev) { DBG("ev_evtchn=%p port=%d cancel (was waiting=%d)", evev, evev->port, evev->waiting); if (!evev->waiting) return; evev->waiting = 0; LIBXL_LIST_REMOVE(evev, entry); evtchn_check_fd_deregister(gc); } /* * waiting for device state */ static void devstate_callback(libxl__egc *egc, libxl__xswait_state *xsw, int rc, const char *sstate) { EGC_GC; libxl__ev_devstate *ds = CONTAINER_OF(xsw, *ds, w); if (rc) { if (rc == ERROR_TIMEDOUT) LOG(DEBUG, "backend %s wanted state %d "" timed out", ds->w.path, ds->wanted); goto out; } if (!sstate) { LOG(DEBUG, "backend %s wanted state %d"" but it was removed", ds->w.path, ds->wanted); rc = ERROR_INVAL; goto out; } int got = atoi(sstate); if (got == ds->wanted) { LOG(DEBUG, "backend %s wanted state %d ok", ds->w.path, ds->wanted); rc = 0; } else { LOG(DEBUG, "backend %s wanted state %d"" still waiting state %d", ds->w.path, ds->wanted, got); return; } out: libxl__ev_devstate_cancel(gc, ds); ds->callback(egc, ds, rc); } int libxl__ev_devstate_wait(libxl__ao *ao, libxl__ev_devstate *ds, libxl__ev_devstate_callback cb, const char *state_path, int state, int milliseconds) { AO_GC; int rc; libxl__xswait_init(&ds->w); ds->wanted = state; ds->callback = cb; ds->w.ao = ao; ds->w.what = GCSPRINTF("backend %s (hoping for state change to %d)", state_path, state); ds->w.path = state_path; ds->w.timeout_ms = milliseconds; ds->w.callback = devstate_callback; rc = libxl__xswait_start(gc, &ds->w); if (rc) goto out; return 0; out: libxl__ev_devstate_cancel(gc, ds); return rc; } /* * domain death/destruction */ /* * We use a xenstore watch on the domain's path, rather than using an * @releaseDomain watch and asking the hypervisor. This is simpler * because turning @releaseDomain into domain-specific information is * complicated. * * It is also sufficient for our callers, which are generally trying * to do cleanup of their own execution state on domain death, for the * following reason: if the domain is destroyed then either (a) the * entries in xenstore have already been deleted, in which case the * test here works or (b) they have not in which case something has * gone very badly wrong and we are going to leak those xenstore * entries, in which case trying to avoid leaking other stuff is * futile. */ void libxl__domaindeathcheck_init(libxl__domaindeathcheck *dc) { libxl__ao_abortable_init(&dc->abrt); libxl__ev_xswatch_init(&dc->watch); } void libxl__domaindeathcheck_stop(libxl__gc *gc, libxl__domaindeathcheck *dc) { libxl__ao_abortable_deregister(&dc->abrt); libxl__ev_xswatch_deregister(gc,&dc->watch); } static void domaindeathcheck_callback(libxl__egc *egc, libxl__ev_xswatch *w, const char *watch_path, const char *event_path) { libxl__domaindeathcheck *dc = CONTAINER_OF(w, *dc, watch); EGC_GC; const char *p = libxl__xs_read(gc, XBT_NULL, watch_path); if (p) return; libxl__domaindeathcheck_stop(gc,dc); if (errno!=ENOENT) { LIBXL__EVENT_DISASTER(egc,"failed to read xenstore" " for domain detach check", errno, 0); return; } LOG(ERROR,"%s: domain %"PRIu32" removed (%s no longer in xenstore)", dc->what, dc->domid, watch_path); dc->callback(egc, dc, ERROR_DOMAIN_DESTROYED); } static void domaindeathcheck_abort(libxl__egc *egc, libxl__ao_abortable *abrt, int rc) { libxl__domaindeathcheck *dc = CONTAINER_OF(abrt, *dc, abrt); EGC_GC; libxl__domaindeathcheck_stop(gc,dc); dc->callback(egc, dc, rc); } int libxl__domaindeathcheck_start(libxl__ao *ao, libxl__domaindeathcheck *dc) { AO_GC; int rc; const char *path = GCSPRINTF("/local/domain/%"PRIu32, dc->domid); libxl__domaindeathcheck_init(dc); dc->abrt.ao = ao; dc->abrt.callback = domaindeathcheck_abort; rc = libxl__ao_abortable_register(&dc->abrt); if (rc) goto out; rc = libxl__ev_xswatch_register(gc, &dc->watch, domaindeathcheck_callback, path); if (rc) goto out; return 0; out: libxl__domaindeathcheck_stop(gc,dc); return rc; } /* * osevent poll */ static int beforepoll_internal(libxl__gc *gc, libxl__poller *poller, int *nfds_io, struct pollfd *fds, int *timeout_upd, struct timeval now) { libxl__ev_fd *efd; int rc; /* * We need to look at the fds we want twice: firstly, to count * them so we can make the rindex array big enough, and secondly * to actually fill the arrays in. * * To ensure correctness and avoid repeating the logic for * deciding which fds are relevant, we define a macro * REQUIRE_FDS( BODY ) * which calls * do{ * int req_fd; * int req_events; * BODY; * }while(0) * for each fd with a nonzero events. This is invoked twice. * * The definition of REQUIRE_FDS is simplified with the helper * macro * void REQUIRE_FD(int req_fd, int req_events, BODY); */ #define REQUIRE_FDS(BODY) do{ \ \ LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) \ REQUIRE_FD(efd->fd, efd->events, BODY); \ \ REQUIRE_FD(poller->wakeup_pipe[0], POLLIN, BODY); \ \ }while(0) #define REQUIRE_FD(req_fd_, req_events_, BODY) do{ \ int req_events = (req_events_); \ int req_fd = (req_fd_); \ if (req_events) { \ BODY; \ } \ }while(0) /* * In order to be able to efficiently find the libxl__ev_fd for a * struct poll during _afterpoll, we maintain a shadow data * structure in CTX->fd_rindices: each fd corresponds to a slot in * fd_rindices, and each element in the rindices is three indices * into the fd array (for POLLIN, POLLPRI and POLLOUT). */ if (*nfds_io) { /* * As an optimisation, we don't touch fd_rindex * if *nfds_io is zero on entry, since in that case the * caller just wanted to know how big an array to give us. * * If !*nfds_io, the unconditional parts below are guaranteed * not to mess with fd_rindex. */ int maxfd = 0; REQUIRE_FDS({ if (req_fd >= maxfd) maxfd = req_fd + 1; }); /* make sure our array is as big as *nfds_io */ if (poller->fd_rindices_allocd < maxfd) { assert(ARRAY_SIZE_OK(poller->fd_rindices, maxfd)); poller->fd_rindices = libxl__realloc(NOGC, poller->fd_rindices, maxfd * sizeof(*poller->fd_rindices)); memset(poller->fd_rindices + poller->fd_rindices_allocd, 0, (maxfd - poller->fd_rindices_allocd) * sizeof(*poller->fd_rindices)); poller->fd_rindices_allocd = maxfd; } } int used = 0; REQUIRE_FDS({ if (used < *nfds_io) { fds[used].fd = req_fd; fds[used].events = req_events; fds[used].revents = 0; assert(req_fd < poller->fd_rindices_allocd); if (req_events & POLLIN) poller->fd_rindices[req_fd][0] = used; if (req_events & POLLPRI) poller->fd_rindices[req_fd][1] = used; if (req_events & POLLOUT) poller->fd_rindices[req_fd][2] = used; } used++; }); rc = used <= *nfds_io ? 0 : ERROR_BUFFERFULL; *nfds_io = used; poller->fds_changed = 0; libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes); if (etime) { int our_timeout; struct timeval rel; static struct timeval zero; timersub(&etime->abs, &now, &rel); if (timercmp(&rel, &zero, <)) { our_timeout = 0; } else if (rel.tv_sec >= 2000000) { our_timeout = 2000000000; } else { our_timeout = rel.tv_sec * 1000 + (rel.tv_usec + 999) / 1000; } if (*timeout_upd < 0 || our_timeout < *timeout_upd) *timeout_upd = our_timeout; } return rc; } int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io, struct pollfd *fds, int *timeout_upd, struct timeval now) { EGC_INIT(ctx); CTX_LOCK; int rc = beforepoll_internal(gc, ctx->poller_app, nfds_io, fds, timeout_upd, now); CTX_UNLOCK; EGC_FREE; return rc; } static int afterpoll_check_fd(libxl__poller *poller, const struct pollfd *fds, int nfds, int fd, int events) /* Returns mask of events which were requested and occurred. Will * return nonzero only once for each (poller,fd,events) * combination, until the next beforepoll. If events from * different combinations overlap, between one such combination * and all distinct combinations will produce nonzero returns. */ { if (fd >= poller->fd_rindices_allocd) /* added after we went into poll, have to try again */ return 0; events |= POLLERR | POLLHUP; int i, revents = 0; for (i=0; i<3; i++) { int *slotp = &poller->fd_rindices[fd][i]; int slot = *slotp; if (slot >= nfds) /* stale slot entry (again, added afterwards), */ /* or slot for which we have already returned nonzero */ continue; if (fds[slot].fd != fd) /* again, stale slot entry */ continue; assert(poller->fds_changed || !(fds[slot].revents & POLLNVAL)); /* we mask in case requested events have changed */ int slot_revents = fds[slot].revents & events; if (!slot_revents) /* this slot is for a different set of events */ continue; revents |= slot_revents; *slotp = INT_MAX; /* so that next time we'll see slot >= nfds */ } return revents; } static void fd_occurs(libxl__egc *egc, libxl__ev_fd *efd, short revents_ign) { short revents_current = libxl__fd_poll_recheck(egc, efd->fd, efd->events); DBG("ev_fd=%p occurs fd=%d events=%x revents_ign=%x revents_current=%x", efd, efd->fd, efd->events, revents_ign, revents_current); if (revents_current) efd->func(egc, efd, efd->fd, efd->events, revents_current); } static void afterpoll_internal(libxl__egc *egc, libxl__poller *poller, int nfds, const struct pollfd *fds, struct timeval now) { /* May make callbacks into the application for child processes. * ctx must be locked exactly once */ EGC_GC; libxl__ev_fd *efd; /* * Warning! Reentrancy hazards! * * Many parts of this function eventually call arbitrary callback * functions which may modify the event handling data structures. * * Of the data structures used here: * * egc, poller, now * are allocated by our caller and relate to the * current thread and its call stack down into the * event machinery; it is not freed until we return. * So it is safe. * * fds is either what application passed into * libxl_osevent_afterpoll (which, although this * isn't explicitly stated, clearly must remain * valid until libxl_osevent_afterpoll returns) or * it's poller->fd_polls which is modified only by * our (non-recursive) caller eventloop_iteration. * * CTX comes from our caller, and applications are * forbidden from destroying it while we are running. * So the ctx pointer itself is safe to use; now * for its contents: * * CTX->etimes is used in a simple reentrancy-safe manner. * * CTX->efds is more complicated; see below. */ for (;;) { /* We restart our scan of fd events whenever we call a * callback function. This is necessary because such * a callback might make arbitrary changes to CTX->efds. * We invalidate the fd_rindices[] entries which were used * so that we don't call the same function again. */ int revents; LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) { if (!efd->events) continue; revents = afterpoll_check_fd(poller,fds,nfds, efd->fd,efd->events); if (revents) goto found_fd_event; } /* no ordinary fd events, then */ break; found_fd_event: fd_occurs(egc, efd, revents); } if (afterpoll_check_fd(poller,fds,nfds, poller->wakeup_pipe[0],POLLIN)) { int e = libxl__self_pipe_eatall(poller->wakeup_pipe[0]); if (e) LIBXL__EVENT_DISASTER(egc, "read wakeup", e, 0); } for (;;) { libxl__ev_time *etime = LIBXL_TAILQ_FIRST(&CTX->etimes); if (!etime) break; assert(!etime->infinite); if (timercmp(&etime->abs, &now, >)) break; time_deregister(gc, etime); time_occurs(egc, etime, ERROR_TIMEDOUT); } } void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds, struct timeval now) { EGC_INIT(ctx); CTX_LOCK; afterpoll_internal(egc, ctx->poller_app, nfds, fds, now); CTX_UNLOCK; EGC_FREE; } /* * osevent hook and callback machinery */ void libxl_osevent_register_hooks(libxl_ctx *ctx, const libxl_osevent_hooks *hooks, void *user) { GC_INIT(ctx); CTX_LOCK; assert(LIBXL_LIST_EMPTY(&ctx->efds)); assert(LIBXL_TAILQ_EMPTY(&ctx->etimes)); ctx->osevent_hooks = hooks; ctx->osevent_user = user; CTX_UNLOCK; GC_FREE; } void libxl_osevent_occurred_fd(libxl_ctx *ctx, void *for_libxl, int fd, short events_ign, short revents_ign) { EGC_INIT(ctx); CTX_LOCK; assert(!CTX->osevent_in_hook); libxl__ev_fd *ev = osevent_ev_from_hook_nexus(ctx, for_libxl); if (!ev) goto out; if (ev->fd != fd) goto out; fd_occurs(egc, ev, revents_ign); out: CTX_UNLOCK; EGC_FREE; } void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl) { EGC_INIT(ctx); CTX_LOCK; assert(!CTX->osevent_in_hook); libxl__osevent_hook_nexus *nexus = for_libxl; libxl__ev_time *ev = osevent_ev_from_hook_nexus(ctx, nexus); osevent_release_nexus(gc, &CTX->hook_timeout_nexi_idle, nexus); if (!ev) goto out; assert(!ev->infinite); LIBXL_TAILQ_REMOVE(&CTX->etimes, ev, entry); time_occurs(egc, ev, ERROR_TIMEDOUT); out: CTX_UNLOCK; EGC_FREE; } void libxl__event_disaster(libxl__egc *egc, const char *msg, int errnoval, libxl_event_type type /* may be 0 */, const char *file, int line, const char *func) { EGC_GC; libxl__log(CTX, XTL_CRITICAL, errnoval, file, line, func, INVALID_DOMID, "DISASTER in event loop: %s%s%s%s", msg, type ? " (relates to event type " : "", type ? libxl_event_type_to_string(type) : "", type ? ")" : ""); if (CTX->event_hooks && CTX->event_hooks->disaster) { CTX->event_hooks->disaster(CTX->event_hooks_user, type, msg, errnoval); return; } const char verybad[] = "DISASTER in event loop not handled by libxl application"; LIBXL__LOG(CTX, XTL_CRITICAL, verybad); fprintf(stderr, "libxl: fatal error, exiting program: %s\n", verybad); exit(-1); } static void egc_run_callbacks(libxl__egc *egc) { /* * The callbacks must happen with the ctx unlocked. See the * comment near #define EGC_GC in libxl_internal.h and those in * the definitions of libxl__egc, libxl__ao and libxl__aop. */ EGC_GC; libxl_event *ev, *ev_tmp; libxl__aop_occurred *aop, *aop_tmp; LIBXL_TAILQ_FOREACH_SAFE(ev, &egc->occurred_for_callback, link, ev_tmp) { LIBXL_TAILQ_REMOVE(&egc->occurred_for_callback, ev, link); LOG(DEBUG,"event %p callback type=%s", ev, libxl_event_type_to_string(ev->type)); CTX->event_hooks->event_occurs(CTX->event_hooks_user, ev); } LIBXL_TAILQ_FOREACH_SAFE(aop, &egc->aops_for_callback, entry, aop_tmp) { LIBXL_TAILQ_REMOVE(&egc->aops_for_callback, aop, entry); LOG(DEBUG,"ao %p: progress report: callback aop=%p", aop->ao, aop); aop->how->callback(CTX, aop->ev, aop->how->for_callback); CTX_LOCK; assert(aop->ao->magic == LIBXL__AO_MAGIC); aop->ao->progress_reports_outstanding--; libxl__ao_complete_check_progress_reports(egc, aop->ao); CTX_UNLOCK; } libxl__ao *ao, *ao_tmp; LIBXL_TAILQ_FOREACH_SAFE(ao, &egc->aos_for_callback, entry_for_callback, ao_tmp) { LIBXL_TAILQ_REMOVE(&egc->aos_for_callback, ao, entry_for_callback); LOG(DEBUG,"ao %p: completion callback", ao); ao->how.callback(CTX, ao->rc, ao->how.u.for_callback); CTX_LOCK; ao->notified = 1; ao__check_destroy(CTX, ao); CTX_UNLOCK; } } void libxl__egc_cleanup(libxl__egc *egc) { EGC_GC; egc_run_callbacks(egc); libxl__free_all(gc); } /* * Event retrieval etc. */ void libxl_event_register_callbacks(libxl_ctx *ctx, const libxl_event_hooks *hooks, void *user) { ctx->event_hooks = hooks; ctx->event_hooks_user = user; } void libxl__event_occurred(libxl__egc *egc, libxl_event *event) { EGC_GC; if (CTX->event_hooks && (CTX->event_hooks->event_occurs_mask & (1UL << event->type))) { /* libxl__egc_cleanup will call the callback, just before exit * from libxl. This helps avoid reentrancy bugs: parts of * libxl that call libxl__event_occurred do not have to worry * that libxl might be reentered at that point. */ LIBXL_TAILQ_INSERT_TAIL(&egc->occurred_for_callback, event, link); return; } else { libxl__poller *poller; LIBXL_TAILQ_INSERT_TAIL(&CTX->occurred, event, link); LIBXL_LIST_FOREACH(poller, &CTX->pollers_event, entry) libxl__poller_wakeup(egc, poller); } } void libxl_event_free(libxl_ctx *ctx, libxl_event *event) { /* Exceptionally, this function may be called from libxl, with ctx==0 */ libxl_event_dispose(event); free(event); } libxl_event *libxl__event_new(libxl__egc *egc, libxl_event_type type, uint32_t domid, libxl_ev_user for_user) { EGC_GC; libxl_event *ev; ev = libxl__zalloc(NOGC,sizeof(*ev)); libxl_event_init(ev); libxl_event_init_type(ev, type); ev->domid = domid; ev->for_user = for_user; return ev; } static int event_check_internal(libxl__egc *egc, libxl_event **event_r, unsigned long typemask, libxl_event_predicate *pred, void *pred_user) { EGC_GC; libxl_event *ev; int rc; LIBXL_TAILQ_FOREACH(ev, &CTX->occurred, link) { if (!(typemask & ((uint64_t)1 << ev->type))) continue; if (pred && !pred(ev, pred_user)) continue; /* got one! */ LIBXL_TAILQ_REMOVE(&CTX->occurred, ev, link); *event_r = ev; rc = 0; goto out; } rc = ERROR_NOT_READY; out: return rc; } int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r, uint64_t typemask, libxl_event_predicate *pred, void *pred_user) { EGC_INIT(ctx); CTX_LOCK; int rc = event_check_internal(egc, event_r, typemask, pred, pred_user); CTX_UNLOCK; EGC_FREE; return rc; } /* * Utilities for pipes (specifically, useful for self-pipes) */ void libxl__pipe_close(int fds[2]) { if (fds[0] >= 0) close(fds[0]); if (fds[1] >= 0) close(fds[1]); fds[0] = fds[1] = -1; } int libxl__pipe_nonblock(libxl_ctx *ctx, int fds[2]) { int r, rc; r = libxl_pipe(ctx, fds); if (r) { fds[0] = fds[1] = -1; rc = ERROR_FAIL; goto out; } rc = libxl_fd_set_nonblock(ctx, fds[0], 1); if (rc) goto out; rc = libxl_fd_set_nonblock(ctx, fds[1], 1); if (rc) goto out; return 0; out: libxl__pipe_close(fds); return rc; } int libxl__self_pipe_wakeup(int fd) { /* Called from signal handlers, so needs to be async-signal-safe */ static const char buf[1] = ""; for (;;) { int r = write(fd, buf, 1); if (r==1) return 0; assert(r==-1); if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return 0; if (!errno) abort(); return errno; } } int libxl__self_pipe_eatall(int fd) { char buf[256]; for (;;) { int r = read(fd, buf, sizeof(buf)); if (r == sizeof(buf)) continue; if (r >= 0) return 0; assert(r == -1); if (errno == EINTR) continue; if (errno == EWOULDBLOCK) return 0; assert(errno); return errno; } } /* * Manipulation of pollers */ int libxl__poller_init(libxl__gc *gc, libxl__poller *p) { int rc; p->fd_polls = 0; p->fd_rindices = 0; p->fds_changed = 0; rc = libxl__pipe_nonblock(CTX, p->wakeup_pipe); if (rc) goto out; return 0; out: libxl__poller_dispose(p); return rc; } void libxl__poller_dispose(libxl__poller *p) { libxl__pipe_close(p->wakeup_pipe); free(p->fd_polls); free(p->fd_rindices); } libxl__poller *libxl__poller_get(libxl__gc *gc) { /* must be called with ctx locked */ int rc; libxl__poller *p = LIBXL_LIST_FIRST(&CTX->pollers_idle); if (p) { LIBXL_LIST_REMOVE(p, entry); } else { p = libxl__zalloc(NOGC, sizeof(*p)); rc = libxl__poller_init(gc, p); if (rc) { free(p); return NULL; } } LIBXL_LIST_INSERT_HEAD(&CTX->pollers_fds_changed, p, fds_changed_entry); return p; } void libxl__poller_put(libxl_ctx *ctx, libxl__poller *p) { if (!p) return; LIBXL_LIST_REMOVE(p, fds_changed_entry); LIBXL_LIST_INSERT_HEAD(&ctx->pollers_idle, p, entry); } void libxl__poller_wakeup(libxl__egc *egc, libxl__poller *p) { int e = libxl__self_pipe_wakeup(p->wakeup_pipe[1]); if (e) LIBXL__EVENT_DISASTER(egc, "cannot poke watch pipe", e, 0); } /* * Main event loop iteration */ static int eventloop_iteration(libxl__egc *egc, libxl__poller *poller) { /* The CTX must be locked EXACTLY ONCE so that this function * can unlock it when it polls. */ EGC_GC; int rc, nfds; struct timeval now; rc = libxl__gettimeofday(gc, &now); if (rc) goto out; int timeout; for (;;) { nfds = poller->fd_polls_allocd; timeout = -1; rc = beforepoll_internal(gc, poller, &nfds, poller->fd_polls, &timeout, now); if (!rc) break; if (rc != ERROR_BUFFERFULL) goto out; struct pollfd *newarray = (nfds > INT_MAX / sizeof(struct pollfd) / 2) ? 0 : libxl__realloc(NOGC, poller->fd_polls, sizeof(*newarray) * nfds); if (!newarray) { rc = ERROR_NOMEM; goto out; } poller->fd_polls = newarray; poller->fd_polls_allocd = nfds; } CTX_UNLOCK; rc = poll(poller->fd_polls, nfds, timeout); CTX_LOCK; if (rc < 0) { if (errno == EINTR) return 0; /* will go round again if caller requires */ LOGEV(ERROR, errno, "poll failed"); rc = ERROR_FAIL; goto out; } rc = libxl__gettimeofday(gc, &now); if (rc) goto out; afterpoll_internal(egc, poller, nfds, poller->fd_polls, now); rc = 0; out: return rc; } int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r, uint64_t typemask, libxl_event_predicate *pred, void *pred_user) { int rc; libxl__poller *poller = NULL; EGC_INIT(ctx); CTX_LOCK; poller = libxl__poller_get(gc); if (!poller) { rc = ERROR_FAIL; goto out; } for (;;) { rc = event_check_internal(egc, event_r, typemask, pred, pred_user); if (rc != ERROR_NOT_READY) goto out; rc = eventloop_iteration(egc, poller); if (rc) goto out; /* we unlock and cleanup the egc each time we go through this loop, * so that (a) we don't accumulate garbage and (b) any events * which are to be dispatched by callback are actually delivered * in a timely fashion. */ CTX_UNLOCK; libxl__egc_cleanup(egc); CTX_LOCK; } out: libxl__poller_put(ctx, poller); CTX_UNLOCK; EGC_FREE; return rc; } /* * The two possible state flow of an ao: * * Completion before initiator return: * * Initiator thread Possible other threads * * * ao_create allocates memory and * initialises the struct * * * the initiator function does its * work, setting up various internal * asynchronous operations -----------> * asynchronous operations * start to take place and * might cause ao completion * | * * initiator calls ao_inprogress | * - if synchronous, run event loop | * until the ao completes | * - ao completes on some thread * - completing thread releases the lock * <--------------' * - ao_inprogress takes the lock * - destroy the ao * * * Completion after initiator return (asynch. only): * * * Initiator thread Possible other threads * * * ao_create allocates memory and * initialises the struct * * * the initiator function does its * work, setting up various internal * asynchronous operations -----------> * asynchronous operations * start to take place and * might cause ao completion * | * * initiator calls ao_inprogress | * - observes event not yet done, | * - returns to caller | * | * - ao completes on some thread * - generate the event or call the callback * - destroy the ao */ /* * A "manip" is a libxl public function manipulating this ao, which * has a pointer to it. We have to not destroy it while that's the * case, obviously. Callers must have the ctx locked, obviously. */ static void ao__manip_enter(libxl__ao *ao) { assert(ao->manip_refcnt < INT_MAX); ao->manip_refcnt++; } static void ao__manip_leave(libxl_ctx *ctx, libxl__ao *ao) { assert(ao->manip_refcnt > 0); ao->manip_refcnt--; ao__check_destroy(ctx, ao); } static void ao__check_destroy(libxl_ctx *ctx, libxl__ao *ao) { if (!ao->manip_refcnt && ao->notified) { assert(ao->complete); libxl__ao__destroy(ctx, ao); } } void libxl__ao__destroy(libxl_ctx *ctx, libxl__ao *ao) { AO_GC; if (!ao) return; LOG(DEBUG,"ao %p: destroy",ao); libxl__poller_put(ctx, ao->poller); ao->magic = LIBXL__AO_MAGIC_DESTROYED; libxl__free_all(&ao->gc); free(ao); } void libxl__ao_create_fail(libxl__ao *ao) { AO_GC; LOG(DEBUG,"ao %p: create fail",ao); assert(ao->magic == LIBXL__AO_MAGIC); assert(ao->in_initiator); assert(!ao->complete); assert(!ao->progress_reports_outstanding); assert(!ao->aborting); LIBXL_LIST_REMOVE(ao, inprogress_entry); libxl__ao__destroy(CTX, ao); } libxl__gc *libxl__ao_inprogress_gc(libxl__ao *ao) { assert(ao); assert(ao->magic == LIBXL__AO_MAGIC); assert(!ao->complete); return &ao->gc; } void libxl__ao_complete(libxl__egc *egc, libxl__ao *ao, int rc) { AO_GC; LOG(DEBUG,"ao %p: complete, rc=%d",ao,rc); assert(ao->magic == LIBXL__AO_MAGIC); assert(!ao->complete); assert(!ao->nested_root); assert(!ao->nested_progeny); ao->complete = 1; ao->rc = rc; LIBXL_LIST_REMOVE(ao, inprogress_entry); libxl__ao_complete_check_progress_reports(egc, ao); } static bool ao_work_outstanding(libxl__ao *ao) { /* * We don't consider an ao complete if it has any outstanding * callbacks. These callbacks might be outstanding on other * threads, queued up in the other threads' egc's. Those threads * will, after making the callback, take out the lock again, * decrement progress_reports_outstanding, and call * libxl__ao_complete_check_progress_reports. */ return !ao->complete || ao->progress_reports_outstanding; } void libxl__ao_complete_check_progress_reports(libxl__egc *egc, libxl__ao *ao) { EGC_GC; libxl_ctx *ctx = libxl__gc_owner(&egc->gc); assert(ao->progress_reports_outstanding >= 0); if (ao_work_outstanding(ao)) return; if (ao->poller) { assert(ao->in_initiator); if (!ao->constructing) /* don't bother with this if we're not in the event loop */ libxl__poller_wakeup(egc, ao->poller); } else if (ao->how.callback) { LOG(DEBUG, "ao %p: complete for callback", ao); LIBXL_TAILQ_INSERT_TAIL(&egc->aos_for_callback, ao, entry_for_callback); } else { libxl_event *ev; ev = NEW_EVENT(egc, OPERATION_COMPLETE, ao->domid, ao->how.u.for_event); if (ev) { ev->u.operation_complete.rc = ao->rc; libxl__event_occurred(egc, ev); } ao->notified = 1; } ao__check_destroy(ctx, ao); } libxl__ao *libxl__ao_create(libxl_ctx *ctx, uint32_t domid, const libxl_asyncop_how *how, const char *file, int line, const char *func) { libxl__ao *ao; ao = calloc(1, sizeof(*ao)); if (!ao) goto out; ao->magic = LIBXL__AO_MAGIC; ao->constructing = 1; ao->in_initiator = 1; ao__manip_enter(ao); ao->poller = 0; ao->domid = domid; LIBXL_INIT_GC(ao->gc, ctx); if (how) { ao->how = *how; } else { ao->poller = libxl__poller_get(&ao->gc); if (!ao->poller) goto out; } libxl__log(ctx,XTL_DEBUG,-1,file,line,func,domid, "ao %p: create: how=%p callback=%p poller=%p", ao, how, ao->how.callback, ao->poller); LIBXL_LIST_INSERT_HEAD(&ctx->aos_inprogress, ao, inprogress_entry); return ao; out: if (ao) libxl__ao__destroy(ctx, ao); return NULL; } int libxl__ao_inprogress(libxl__ao *ao, const char *file, int line, const char *func) { AO_GC; int rc; uint32_t domid = ao->domid; assert(ao->magic == LIBXL__AO_MAGIC); assert(ao->constructing); assert(ao->in_initiator); ao->constructing = 0; if (ao->nested_root) domid = ao->nested_root->domid; libxl__log(CTX,XTL_DEBUG,-1,file,line,func,domid, "ao %p: inprogress: poller=%p, flags=%s%s%s%s", ao, ao->poller, ao->constructing ? "o" : "", ao->in_initiator ? "i" : "", ao->complete ? "c" : "", ao->notified ? "n" : ""); if (ao->poller) { /* Caller wants it done synchronously. */ /* We use a fresh gc, so that we can free things * each time round the loop. */ libxl__egc egc; LIBXL_INIT_EGC(egc,CTX); for (;;) { assert(ao->magic == LIBXL__AO_MAGIC); if (!ao_work_outstanding(ao)) { rc = ao->rc; ao->notified = 1; break; } DBG("ao %p: not ready, waiting",ao); rc = eventloop_iteration(&egc,ao->poller); if (rc) { /* Oh dear, this is quite unfortunate. */ LOG(ERROR, "Error waiting for"" event during long-running operation (rc=%d)", rc); sleep(1); /* It's either this or return ERROR_I_DONT_KNOW_WHETHER * _THE_THING_YOU_ASKED_FOR_WILL_BE_DONE_LATER_WHEN * _YOU_DIDNT_EXPECT_IT, since we don't have a * synchronous cancellation ability. */ } CTX_UNLOCK; libxl__egc_cleanup(&egc); CTX_LOCK; } } else { rc = 0; } ao->in_initiator = 0; ao__manip_leave(CTX, ao); return rc; } /* abort requests */ static int ao__abort(libxl_ctx *ctx, libxl__ao *parent) /* Temporarily unlocks ctx, which must be locked exactly once on entry. */ { int rc; ao__manip_enter(parent); if (parent->aborting) { rc = ERROR_ABORTED; goto out; } parent->aborting = 1; if (LIBXL_LIST_EMPTY(&parent->abortables)) { LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "ao %p: abort requested and noted, but no-one interested", parent); rc = 0; goto out; } /* We keep calling abort hooks until there are none left */ while (!LIBXL_LIST_EMPTY(&parent->abortables)) { libxl__egc egc; LIBXL_INIT_EGC(egc,ctx); assert(!parent->complete); libxl__ao_abortable *abrt = LIBXL_LIST_FIRST(&parent->abortables); assert(parent == ao_nested_root(abrt->ao)); LIBXL_LIST_REMOVE(abrt, entry); abrt->registered = 0; LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "ao %p: abrt=%p: aborting", parent, abrt->ao); abrt->callback(&egc, abrt, ERROR_ABORTED); libxl__ctx_unlock(ctx); libxl__egc_cleanup(&egc); libxl__ctx_lock(ctx); } rc = 0; out: ao__manip_leave(ctx, parent); return rc; } int libxl_ao_abort(libxl_ctx *ctx, const libxl_asyncop_how *how) { libxl__ao *search; libxl__ctx_lock(ctx); int rc; LIBXL_LIST_FOREACH(search, &ctx->aos_inprogress, inprogress_entry) { if (how) { /* looking for ao to be reported by callback or event */ if (search->poller) /* sync */ continue; if (how->callback != search->how.callback) continue; if (how->callback ? (how->u.for_callback != search->how.u.for_callback) : (how->u.for_event != search->how.u.for_event)) continue; } else { /* looking for synchronous call */ if (!search->poller) /* async */ continue; } goto found; } rc = ERROR_NOTFOUND; goto out; found: rc = ao__abort(ctx, search); out: libxl__ctx_unlock(ctx); return rc; } int libxl__ao_aborting(libxl__ao *ao) { libxl__ao *root = ao_nested_root(ao); if (root->aborting) { DBG("ao=%p: aborting at explicit check (root=%p)", ao, root); return ERROR_ABORTED; } return 0; } int libxl__ao_abortable_register(libxl__ao_abortable *abrt) { libxl__ao *ao = abrt->ao; libxl__ao *root = ao_nested_root(ao); AO_GC; if (root->aborting) { DBG("ao=%p: preemptively aborting ao_abortable registration %p (root=%p)", ao, abrt, root); return ERROR_ABORTED; } DBG("ao=%p, abrt=%p: registering (root=%p)", ao, abrt, root); LIBXL_LIST_INSERT_HEAD(&root->abortables, abrt, entry); abrt->registered = 1; return 0; } _hidden void libxl__ao_abortable_deregister(libxl__ao_abortable *abrt) { if (!abrt->registered) return; libxl__ao *ao = abrt->ao; libxl__ao *root __attribute__((unused)) = ao_nested_root(ao); AO_GC; DBG("ao=%p, abrt=%p: deregistering (root=%p)", ao, abrt, root); LIBXL_LIST_REMOVE(abrt, entry); abrt->registered = 0; } /* progress reporting */ /* The application indicates a desire to ignore events by passing NULL * for how. But we want to copy *how. So we have this dummy function * whose address is stored in callback if the app passed how==NULL. */ static void dummy_asyncprogress_callback_ignore (libxl_ctx *ctx, libxl_event *ev, void *for_callback) { } void libxl__ao_progress_gethow(libxl_asyncprogress_how *in_state, const libxl_asyncprogress_how *from_app) { if (from_app) *in_state = *from_app; else in_state->callback = dummy_asyncprogress_callback_ignore; } void libxl__ao_progress_report(libxl__egc *egc, libxl__ao *ao, const libxl_asyncprogress_how *how, libxl_event *ev) { AO_GC; assert(!ao->nested_root); if (how->callback == dummy_asyncprogress_callback_ignore) { LOG(DEBUG,"ao %p: progress report: ignored",ao); libxl_event_free(CTX,ev); /* ignore */ } else if (how->callback) { libxl__aop_occurred *aop = libxl__zalloc(&egc->gc, sizeof(*aop)); ao->progress_reports_outstanding++; aop->ao = ao; aop->ev = ev; aop->how = how; LIBXL_TAILQ_INSERT_TAIL(&egc->aops_for_callback, aop, entry); LOG(DEBUG,"ao %p: progress report: callback queued aop=%p",ao,aop); } else { LOG(DEBUG,"ao %p: progress report: event queued ev=%p type=%s", ao, ev, libxl_event_type_to_string(ev->type)); libxl__event_occurred(egc, ev); } } /* nested ao */ static libxl__ao *ao_nested_root(libxl__ao *ao) { libxl__ao *root = ao->nested_root ? : ao; assert(!root->nested_root); return root; } _hidden libxl__ao *libxl__nested_ao_create(libxl__ao *parent) { libxl__ao *child = NULL, *root; libxl_ctx *ctx = libxl__gc_owner(&parent->gc); assert(parent->magic == LIBXL__AO_MAGIC); root = ao_nested_root(parent); child = libxl__zalloc(&ctx->nogc_gc, sizeof(*child)); child->magic = LIBXL__AO_MAGIC; child->nested_root = root; assert(root->nested_progeny < INT_MAX); root->nested_progeny++; LIBXL_INIT_GC(child->gc, ctx); libxl__gc *gc = &child->gc; LOG(DEBUG,"ao %p: nested ao, parent %p", child, parent); return child; } _hidden void libxl__nested_ao_free(libxl__ao *child) { assert(child->magic == LIBXL__AO_MAGIC); libxl__ao *root = child->nested_root; assert(root); assert(root->nested_progeny > 0); root->nested_progeny--; libxl_ctx *ctx = libxl__gc_owner(&child->gc); libxl__ao__destroy(ctx, child); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_paths.c0000664000175000017500000000240613256712137015406 0ustar smbsmb/* * Copyright (C) 2010 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" const char *libxl__private_bindir_path(void) { return LIBEXEC_BIN; } const char *libxl__xenfirmwaredir_path(void) { return XENFIRMWAREDIR; } const char *libxl__xen_script_dir_path(void) { return XEN_SCRIPT_DIR; } const char *libxl__run_dir_path(void) { return XEN_RUN_DIR; } const char *libxl__seabios_path(void) { #ifdef SEABIOS_PATH return SEABIOS_PATH; #else return NULL; #endif } const char *libxl__ovmf_path(void) { #ifdef OVMF_PATH return OVMF_PATH; #else return NULL; #endif } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/flexarray.c0000664000175000017500000000565213256712137015100 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_internal.h" #include /* * It is safe to store gc in the struct because: * - If it an actual gc, then the flexarray should not be used after the gc * have been freed. * - If it is a NOGC, then this point to a structure embedded in libxl_ctx, * therefore will survive across several libxl calls. */ flexarray_t *flexarray_make(libxl__gc *gc, int size, int autogrow) { flexarray_t *array; GCNEW(array); array->size = size; array->autogrow = autogrow; array->count = 0; array->gc = gc; GCNEW_ARRAY(array->data, size); return array; } void flexarray_free(flexarray_t *array) { assert(!libxl__gc_is_real(array->gc)); free(array->data); free(array); } void flexarray_grow(flexarray_t *array, int extents) { int newsize; libxl__gc *gc = array->gc; newsize = array->size + extents; GCREALLOC_ARRAY(array->data, newsize); array->size += extents; } int flexarray_set(flexarray_t *array, unsigned int idx, void *ptr) { if (idx >= array->size) { int newsize; if (!array->autogrow) return 1; newsize = (array->size * 2 < idx) ? idx + 1 : array->size * 2; flexarray_grow(array, newsize - array->size); } if ( idx + 1 > array->count ) array->count = idx + 1; array->data[idx] = ptr; return 0; } int flexarray_append(flexarray_t *array, void *ptr) { return flexarray_set(array, array->count, ptr); } int flexarray_append_pair(flexarray_t *array, void *ptr1, void *ptr2) { int rc = flexarray_append(array, ptr1); if (!rc) rc = flexarray_append(array, ptr2); return rc; } int flexarray_vappend(flexarray_t *array, ...) { va_list va; void *ptr; int ret; va_start(va, array); for(ret = 0; (ptr = va_arg(va, void *)); ret++) { if ( flexarray_append(array, ptr) ) break; } va_end(va); return ret; } int flexarray_get(flexarray_t *array, int idx, void **ptr) { if (idx >= array->size) return 1; *ptr = array->data[idx]; return 0; } void **flexarray_contents(flexarray_t *array) { void **data; data = array->data; if (!libxl__gc_is_real(array->gc)) free(array); return data; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/check-xl-disk-parse0000775000175000017500000000726313256712137016423 0ustar smbsmb#!/bin/bash set -e if [ -x ./xl ] ; then export LD_LIBRARY_PATH=.:../libxc:../xenstore:../blktap2/control XL=./xl else XL=xl fi fprefix=tmp.check-xl-disk-parse expected () { cat >$fprefix.expected } failures=0 one () { expected_rc=$1; shift printf "test case %s...\n" "$*" set +e ${XL} -N block-attach 0 "$@" $fprefix.actual 2>/dev/null actual_rc=$? diff -u $fprefix.expected $fprefix.actual diff_rc=$? set -e if [ $actual_rc != $expected_rc ] || [ $diff_rc != 0 ]; then echo >&2 "test case \`$*' failed ($actual_rc $diff_rc)" failures=$(( $failures + 1 )) fi } complete () { if [ "$failures" = 0 ]; then echo all ok.; exit 0 else echo "$failures tests failed."; exit 1 fi } e=1 #---------- test data ---------- # # culled from docs/misc/xl-disk-configuration.txt expected * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" /*** drbd implementation ***/ const int DRBD_SEND_CHECKPOINT = 20; const int DRBD_WAIT_CHECKPOINT_ACK = 30; typedef struct libxl__remus_drbd_disk { int ctl_fd; int ackwait; } libxl__remus_drbd_disk; int init_subkind_drbd_disk(libxl__checkpoint_devices_state *cds) { libxl__remus_state *rs = cds->concrete_data; STATE_AO_GC(cds->ao); rs->drbd_probe_script = GCSPRINTF("%s/block-drbd-probe", libxl__xen_script_dir_path()); return 0; } void cleanup_subkind_drbd_disk(libxl__checkpoint_devices_state *cds) { return; } /*----- match(), setup() and teardown() -----*/ /* callbacks */ static void match_async_exec_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status); /* implementations */ static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev); static void drbd_setup(libxl__egc *egc, libxl__checkpoint_device *dev) { STATE_AO_GC(dev->cds->ao); match_async_exec(egc, dev); } static void match_async_exec(libxl__egc *egc, libxl__checkpoint_device *dev) { int arraysize, nr = 0, rc; const libxl_device_disk *disk = dev->backend_dev; libxl__async_exec_state *aes = &dev->aodev.aes; libxl__remus_state *rs = dev->cds->concrete_data; STATE_AO_GC(dev->cds->ao); /* setup env & args */ arraysize = 1; GCNEW_ARRAY(aes->env, arraysize); aes->env[nr++] = NULL; assert(nr <= arraysize); arraysize = 3; nr = 0; GCNEW_ARRAY(aes->args, arraysize); aes->args[nr++] = rs->drbd_probe_script; aes->args[nr++] = disk->pdev_path; aes->args[nr++] = NULL; assert(nr <= arraysize); aes->ao = dev->cds->ao; aes->what = GCSPRINTF("%s %s", aes->args[0], aes->args[1]); aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000; aes->callback = match_async_exec_cb; aes->stdfds[0] = -1; aes->stdfds[1] = -1; aes->stdfds[2] = -1; rc = libxl__async_exec_start(aes); if (rc) goto out; return; out: dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); } static void match_async_exec_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status) { libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes); libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev); libxl__remus_drbd_disk *drbd_disk; const libxl_device_disk *disk = dev->backend_dev; STATE_AO_GC(aodev->ao); if (rc) goto out; if (status) { rc = ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH; /* BUG: seems to assume that any exit status means `no match' */ /* BUG: exit status will have been logged as an error */ goto out; } /* ops matched */ dev->matched = true; GCNEW(drbd_disk); dev->concrete_data = drbd_disk; drbd_disk->ackwait = 0; drbd_disk->ctl_fd = open(disk->pdev_path, O_RDONLY); if (drbd_disk->ctl_fd < 0) { rc = ERROR_FAIL; goto out; } rc = 0; out: aodev->rc = rc; aodev->callback(egc, aodev); } static void drbd_teardown(libxl__egc *egc, libxl__checkpoint_device *dev) { libxl__remus_drbd_disk *drbd_disk = dev->concrete_data; STATE_AO_GC(dev->cds->ao); close(drbd_disk->ctl_fd); dev->aodev.rc = 0; dev->aodev.callback(egc, &dev->aodev); } /*----- checkpointing APIs -----*/ /* callbacks */ static void checkpoint_async_call_done(libxl__egc *egc, libxl__ev_child *child, pid_t pid, int status); /* API implementations */ /* this op will not wait and block, so implement as sync op */ static void drbd_postsuspend(libxl__egc *egc, libxl__checkpoint_device *dev) { STATE_AO_GC(dev->cds->ao); libxl__remus_drbd_disk *rdd = dev->concrete_data; if (!rdd->ackwait) { if (ioctl(rdd->ctl_fd, DRBD_SEND_CHECKPOINT, 0) <= 0) rdd->ackwait = 1; } dev->aodev.rc = 0; dev->aodev.callback(egc, &dev->aodev); } static void drbd_preresume_async(libxl__checkpoint_device *dev); static void drbd_preresume(libxl__egc *egc, libxl__checkpoint_device *dev) { ASYNC_CALL(egc, dev->cds->ao, &dev->aodev.child, dev, drbd_preresume_async, checkpoint_async_call_done); } static void drbd_preresume_async(libxl__checkpoint_device *dev) { libxl__remus_drbd_disk *rdd = dev->concrete_data; int ackwait = rdd->ackwait; if (ackwait) { ioctl(rdd->ctl_fd, DRBD_WAIT_CHECKPOINT_ACK, 0); ackwait = 0; } _exit(ackwait); } static void checkpoint_async_call_done(libxl__egc *egc, libxl__ev_child *child, pid_t pid, int status) { int rc; libxl__ao_device *aodev = CONTAINER_OF(child, *aodev, child); libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev); libxl__remus_drbd_disk *rdd = dev->concrete_data; STATE_AO_GC(aodev->ao); if (!WIFEXITED(status)) { rc = ERROR_FAIL; goto out; } rdd->ackwait = WEXITSTATUS(status); rc = 0; out: aodev->rc = rc; aodev->callback(egc, aodev); } const libxl__checkpoint_device_instance_ops remus_device_drbd_disk = { .kind = LIBXL__DEVICE_KIND_VBD, .setup = drbd_setup, .teardown = drbd_teardown, .postsuspend = drbd_postsuspend, .preresume = drbd_preresume, }; xen-4.9.2/tools/libxl/libxl_bootloader.c0000664000175000017500000005042213256712137016422 0ustar smbsmb/* * Copyright (C) 2010 Citrix Ltd. * Author Ian Campbell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include #ifdef HAVE_UTMP_H #include #endif #ifdef INCLUDE_LIBUTIL_H #include INCLUDE_LIBUTIL_H #endif #include "libxl_internal.h" #define BOOTLOADER_BUF_OUT 65536 #define BOOTLOADER_BUF_IN 4096 static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op); static void bootloader_keystrokes_copyfail(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); static void bootloader_display_copyfail(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc, int rc); static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child, pid_t pid, int status); /*----- bootloader arguments -----*/ static void bootloader_arg(libxl__bootloader_state *bl, const char *arg) { assert(bl->nargs < bl->argsspace); bl->args[bl->nargs++] = arg; } static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, const char *bootloader_path) { const libxl_domain_build_info *info = bl->info; bl->argsspace = 9 + libxl_string_list_length(&info->u.pv.bootloader_args); GCNEW_ARRAY(bl->args, bl->argsspace); #define ARG(arg) bootloader_arg(bl, (arg)) ARG(bootloader_path); if (info->kernel) ARG(GCSPRINTF("--kernel=%s", info->kernel)); if (info->ramdisk) ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk)); if (info->cmdline && *info->cmdline != '\0') ARG(GCSPRINTF("--args=%s", info->cmdline)); ARG(GCSPRINTF("--output=%s", bl->outputpath)); ARG("--output-format=simple0"); ARG(GCSPRINTF("--output-directory=%s", bl->outputdir)); if (info->u.pv.bootloader_args) { char **p = info->u.pv.bootloader_args; while (*p) { ARG(*p); p++; } } ARG(bl->dls.diskpath); /* Sentinel for execv */ ARG(NULL); #undef ARG } /*----- synchronous subroutines -----*/ static int setup_xenconsoled_pty(libxl__egc *egc, libxl__bootloader_state *bl, char *slave_path, size_t slave_path_len) { STATE_AO_GC(bl->ao); struct termios termattr; int r, rc; int slave = libxl__carefd_fd(bl->ptys[1].slave); int master = libxl__carefd_fd(bl->ptys[1].master); r = ttyname_r(slave, slave_path, slave_path_len); if (r == -1) { LOGED(ERROR, bl->domid, "ttyname_r failed"); rc = ERROR_FAIL; goto out; } /* * On Solaris, the pty master side will get cranky if we try * to write to it while there is no slave. To work around this, * keep the slave descriptor open until we're done. Set it * to raw terminal parameters, otherwise it will echo back * characters, which will confuse the I/O loop below. * Furthermore, a raw master pty device has no terminal * semantics on Solaris, so don't try to set any attributes * for it. */ tcgetattr(master, &termattr); cfmakeraw(&termattr); tcsetattr(master, TCSANOW, &termattr); return 0; out: return rc; } static const char *bootloader_result_command(libxl__gc *gc, const char *buf, const char *prefix, size_t prefixlen, uint32_t domid) { if (strncmp(buf, prefix, prefixlen)) return 0; const char *rhs = buf + prefixlen; if (!CTYPE(isspace,*rhs)) return 0; while (CTYPE(isspace,*rhs)) rhs++; LOGD(DEBUG, domid, "bootloader output contained %s %s", prefix, rhs); return rhs; } static int parse_bootloader_result(libxl__egc *egc, libxl__bootloader_state *bl) { STATE_AO_GC(bl->ao); char buf[PATH_MAX*2]; FILE *f = 0; int rc = ERROR_FAIL; f = fopen(bl->outputpath, "r"); if (!f) { LOGED(ERROR, bl->domid, "open bootloader output file %s", bl->outputpath); goto out; } for (;;) { /* Read a nul-terminated "line" and put the result in * buf, and its length (not including the nul) in l */ int l = 0, c; while ((c = getc(f)) != EOF && c != '\0') { if (l < sizeof(buf)-1) buf[l] = c; l++; } if (c == EOF) { if (ferror(f)) { LOGED(ERROR, bl->domid, "read bootloader output file %s", bl->outputpath); goto out; } if (!l) break; } if (l >= sizeof(buf)) { LOGD(WARN, bl->domid, "bootloader output contained" " overly long item `%.150s...'", buf); continue; } buf[l] = 0; const char *rhs; #define COMMAND(s) ((rhs = bootloader_result_command(gc, buf, s, sizeof(s)-1, bl->domid))) if (COMMAND("kernel")) { bl->kernel->path = libxl__strdup(gc, rhs); libxl__file_reference_map(bl->kernel); unlink(bl->kernel->path); } else if (COMMAND("ramdisk")) { bl->ramdisk->path = libxl__strdup(gc, rhs); libxl__file_reference_map(bl->ramdisk); unlink(bl->ramdisk->path); } else if (COMMAND("args")) { bl->cmdline = libxl__strdup(gc, rhs); } else if (l) { LOGD(WARN, bl->domid, "unexpected output from bootloader: `%s'", buf); } } rc = 0; out: if (f) fclose(f); return rc; } /*----- init and cleanup -----*/ void libxl__bootloader_init(libxl__bootloader_state *bl) { assert(bl->ao); bl->rc = 0; bl->dls.diskpath = NULL; bl->openpty.ao = bl->ao; bl->dls.ao = bl->ao; bl->ptys[0].master = bl->ptys[0].slave = 0; bl->ptys[1].master = bl->ptys[1].slave = 0; libxl__ev_child_init(&bl->child); libxl__domaindeathcheck_init(&bl->deathcheck); bl->keystrokes.ao = bl->ao; libxl__datacopier_init(&bl->keystrokes); bl->display.ao = bl->ao; libxl__datacopier_init(&bl->display); bl->got_pollhup = 0; } static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl) { STATE_AO_GC(bl->ao); int i; if (bl->outputpath) libxl__remove_file(gc, bl->outputpath); if (bl->outputdir) libxl__remove_directory(gc, bl->outputdir); libxl__domaindeathcheck_stop(gc,&bl->deathcheck); libxl__datacopier_kill(&bl->keystrokes); libxl__datacopier_kill(&bl->display); for (i=0; i<2; i++) { libxl__carefd_close(bl->ptys[i].master); libxl__carefd_close(bl->ptys[i].slave); } if (bl->display.log) { fclose(bl->display.log); bl->display.log = NULL; } } static void bootloader_setpaths(libxl__gc *gc, libxl__bootloader_state *bl) { uint32_t domid = bl->domid; bl->outputdir = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".d", domid); bl->outputpath = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".out", domid); } /* Callbacks */ static void bootloader_local_detached_cb(libxl__egc *egc, libxl__disk_local_state *dls, int rc); static void bootloader_callback(libxl__egc *egc, libxl__bootloader_state *bl, int rc) { if (!bl->rc) bl->rc = rc; bootloader_cleanup(egc, bl); bl->dls.callback = bootloader_local_detached_cb; libxl__device_disk_local_initiate_detach(egc, &bl->dls); } static void bootloader_local_detached_cb(libxl__egc *egc, libxl__disk_local_state *dls, int rc) { STATE_AO_GC(dls->ao); libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls); if (rc) { LOGD(ERROR, bl->domid, "unable to detach locally attached disk"); if (!bl->rc) bl->rc = rc; } bl->callback(egc, bl, bl->rc); } /* might be called at any time, provided it's init'd */ static void bootloader_stop(libxl__egc *egc, libxl__bootloader_state *bl, int rc) { STATE_AO_GC(bl->ao); int r; libxl__datacopier_kill(&bl->keystrokes); libxl__datacopier_kill(&bl->display); if (libxl__ev_child_inuse(&bl->child)) { r = kill(bl->child.pid, SIGTERM); if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]", rc ? "after failure, " : "", (unsigned long)bl->child.pid); } if (!bl->rc) bl->rc = rc; } /*----- main flow of control -----*/ /* Callbacks */ static void bootloader_disk_attached_cb(libxl__egc *egc, libxl__disk_local_state *dls, int rc); void libxl__bootloader_run(libxl__egc *egc, libxl__bootloader_state *bl) { STATE_AO_GC(bl->ao); const libxl_domain_build_info *info = bl->info; uint32_t domid = bl->domid; char *logfile_tmp = NULL; int rc, r; libxl__bootloader_init(bl); if (info->type != LIBXL_DOMAIN_TYPE_PV) { LOGD(DEBUG, domid, "not a PV domain, skipping bootloader"); rc = 0; goto out_ok; } if (!info->u.pv.bootloader) { LOGD(DEBUG, domid, "no bootloader configured, using user supplied kernel"); bl->kernel->path = bl->info->kernel; bl->ramdisk->path = bl->info->ramdisk; bl->cmdline = bl->info->cmdline; rc = 0; goto out_ok; } if (!bl->disk) { LOGD(ERROR, domid, "cannot run bootloader with no boot disk"); rc = ERROR_FAIL; goto out; } bootloader_setpaths(gc, bl); const char *logfile_leaf = GCSPRINTF("bootloader.%"PRIu32, domid); rc = libxl_create_logfile(CTX, logfile_leaf, &logfile_tmp); if (rc) goto out; /* Transfer ownership of log filename to bl and the gc */ bl->logfile = logfile_tmp; libxl__ptr_add(gc, logfile_tmp); logfile_tmp = NULL; bl->display.log = fopen(bl->logfile, "a"); if (!bl->display.log) { LOGED(ERROR, domid, "failed to create bootloader logfile %s", bl->logfile); rc = ERROR_FAIL; goto out; } for (;;) { r = mkdir(bl->outputdir, 0600); if (!r) break; if (errno == EINTR) continue; if (errno == EEXIST) break; LOGED(ERROR, domid, "failed to create bootloader dir %s", bl->outputdir); rc = ERROR_FAIL; goto out; } for (;;) { r = open(bl->outputpath, O_WRONLY|O_CREAT|O_TRUNC, 0600); if (r>=0) { close(r); break; } if (errno == EINTR) continue; LOGED(ERROR, domid, "failed to precreate bootloader output %s", bl->outputpath); rc = ERROR_FAIL; goto out; } /* This sets the state of the dls struct from Undefined to Idle */ libxl__device_disk_local_init(&bl->dls); bl->dls.ao = ao; bl->dls.in_disk = bl->disk; bl->dls.blkdev_start = info->blkdev_start; bl->dls.callback = bootloader_disk_attached_cb; libxl__device_disk_local_initiate_attach(egc, &bl->dls); return; out: assert(rc); out_ok: free(logfile_tmp); bootloader_callback(egc, bl, rc); } static void bootloader_disk_attached_cb(libxl__egc *egc, libxl__disk_local_state *dls, int rc) { STATE_AO_GC(dls->ao); libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls); const libxl_domain_build_info *info = bl->info; const char *bootloader; if (rc) { LOGD(ERROR, bl->domid, "failed to attach local disk for bootloader execution"); goto out; } LOGD(DEBUG, bl->domid, "Config bootloader value: %s", info->u.pv.bootloader); if ( !strcmp(info->u.pv.bootloader, "/usr/bin/pygrub") ) LOGD(WARN, bl->domid, "bootloader='/usr/bin/pygrub' is deprecated; use " \ "bootloader='pygrub' instead"); bootloader = info->u.pv.bootloader; /* If the full path is not specified, check in the libexec path */ if ( bootloader[0] != '/' ) { const char *bltmp; struct stat st; bltmp = libxl__abs_path(gc, bootloader, libxl__private_bindir_path()); /* Check to see if the file exists in this location; if not, * fall back to checking the path */ LOGD(DEBUG, bl->domid, "Checking for bootloader in libexec path: %s", bltmp); if ( lstat(bltmp, &st) ) LOGD(DEBUG, bl->domid, "%s doesn't exist, falling back to config path", bltmp); else bootloader = bltmp; } make_bootloader_args(gc, bl, bootloader); bl->openpty.ao = ao; bl->openpty.callback = bootloader_gotptys; bl->openpty.count = 2; bl->openpty.results = bl->ptys; rc = libxl__openptys(&bl->openpty, 0,0); if (rc) goto out; return; out: assert(rc); bootloader_callback(egc, bl, rc); } static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op) { libxl__bootloader_state *bl = CONTAINER_OF(op, *bl, openpty); STATE_AO_GC(bl->ao); int rc, r; char *const env[] = { "TERM", "vt100", NULL }; if (bl->openpty.rc) { rc = bl->openpty.rc; goto out; } /* * We need to present the bootloader's tty as a pty slave that xenconsole * can access. Since the bootloader itself needs a pty slave, * we end up with a connection like this: * * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader * * where we copy characters between the two master fds, as well as * listening on the bootloader's fifo for the results. */ char *dom_console_xs_path; char dom_console_slave_tty_path[PATH_MAX]; rc = setup_xenconsoled_pty(egc, bl, &dom_console_slave_tty_path[0], sizeof(dom_console_slave_tty_path)); if (rc) goto out; char *dompath = libxl__xs_get_dompath(gc, bl->domid); if (!dompath) { rc = ERROR_FAIL; goto out; } dom_console_xs_path = GCSPRINTF("%s/console/tty", dompath); rc = libxl__xs_printf(gc, XBT_NULL, dom_console_xs_path, "%s", dom_console_slave_tty_path); if (rc) { LOGED(ERROR, bl->domid, "xs write console path %s := %s failed", dom_console_xs_path, dom_console_slave_tty_path); rc = ERROR_FAIL; goto out; } bl->deathcheck.what = "stopping bootloader"; bl->deathcheck.domid = bl->domid; bl->deathcheck.callback = bootloader_domaindeath; rc = libxl__domaindeathcheck_start(ao, &bl->deathcheck); if (rc) goto out; if (bl->console_available) bl->console_available(egc, bl); int bootloader_master = libxl__carefd_fd(bl->ptys[0].master); int xenconsole_master = libxl__carefd_fd(bl->ptys[1].master); libxl_fd_set_nonblock(CTX, bootloader_master, 1); libxl_fd_set_nonblock(CTX, xenconsole_master, 1); bl->keystrokes.writefd = bl->display.readfd = bootloader_master; bl->keystrokes.writewhat = bl->display.readwhat = "bootloader pty"; bl->keystrokes.readfd = bl->display.writefd = xenconsole_master; bl->keystrokes.readwhat = bl->display.writewhat = "xenconsole client pty"; bl->keystrokes.ao = ao; bl->keystrokes.maxsz = BOOTLOADER_BUF_OUT; bl->keystrokes.bytes_to_read = -1; bl->keystrokes.copywhat = GCSPRINTF("bootloader input for domain %"PRIu32, bl->domid); bl->keystrokes.callback = bootloader_keystrokes_copyfail; bl->keystrokes.callback_pollhup = bootloader_keystrokes_copyfail; /* pollhup gets called with errnoval==-1 which is not otherwise * possible since errnos are nonnegative, so it's unambiguous */ rc = libxl__datacopier_start(&bl->keystrokes); if (rc) goto out; bl->display.ao = ao; bl->display.maxsz = BOOTLOADER_BUF_IN; bl->display.bytes_to_read = -1; bl->display.copywhat = GCSPRINTF("bootloader output for domain %"PRIu32, bl->domid); bl->display.callback = bootloader_display_copyfail; bl->display.callback_pollhup = bootloader_display_copyfail; rc = libxl__datacopier_start(&bl->display); if (rc) goto out; LOGD(DEBUG, bl->domid, "executing bootloader: %s", bl->args[0]); for (const char **blarg = bl->args; *blarg; blarg++) LOGD(DEBUG, bl->domid, " bootloader arg: %s", *blarg); struct termios termattr; pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished); if (pid == -1) { rc = ERROR_FAIL; goto out; } if (!pid) { /* child */ r = login_tty(libxl__carefd_fd(bl->ptys[0].slave)); if (r) { LOGED(ERROR, bl->domid, "login_tty failed"); exit(-1); } libxl__exec(gc, -1, -1, -1, bl->args[0], (char **) bl->args, env); } /* parent */ /* * On Solaris, the master pty side does not have terminal semantics, * so don't try to set any attributes, as it will fail. */ #if !defined(__sun__) tcgetattr(bootloader_master, &termattr); cfmakeraw(&termattr); tcsetattr(bootloader_master, TCSANOW, &termattr); #endif return; out: bootloader_callback(egc, bl, rc); } /* perhaps one of these will be called, but perhaps not */ static void bootloader_copyfail(libxl__egc *egc, const char *which, libxl__bootloader_state *bl, int ondisplay, int rc, int onwrite, int errnoval) { STATE_AO_GC(bl->ao); if (errnoval==-1) { /* POLLHUP */ if (!!ondisplay != !!onwrite) { rc = 0; bl->got_pollhup = 1; } else { LOGD(ERROR, bl->domid, "unexpected POLLHUP on %s", which); } } else if (!rc) { LOGD(ERROR, bl->domid, "unexpected eof copying %s", which); rc = ERROR_FAIL; } bootloader_stop(egc, bl, rc); } static void bootloader_keystrokes_copyfail(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, keystrokes); bootloader_copyfail(egc, "bootloader input", bl, 0, rc,onwrite,errnoval); } static void bootloader_display_copyfail(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display); bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval); } static void bootloader_domaindeath(libxl__egc *egc, libxl__domaindeathcheck *dc, int rc) { libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, deathcheck); bootloader_stop(egc, bl, rc); } static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child, pid_t pid, int status) { libxl__bootloader_state *bl = CONTAINER_OF(child, *bl, child); STATE_AO_GC(bl->ao); int rc; libxl__datacopier_kill(&bl->keystrokes); libxl__datacopier_kill(&bl->display); if (status) { if (bl->got_pollhup && WIFSIGNALED(status) && WTERMSIG(status)==SIGTERM) LOGD(ERROR, bl->domid, "got POLLHUP, sent SIGTERM"); LOGD(ERROR, bl->domid, "bootloader failed - consult logfile %s", bl->logfile); libxl_report_child_exitstatus(CTX, XTL_ERROR, "bootloader", pid, status); rc = ERROR_FAIL; goto out; } else { LOGD(DEBUG, bl->domid, "bootloader completed"); } if (bl->rc) { /* datacopier went wrong */ rc = bl->rc; goto out; } rc = parse_bootloader_result(egc, bl); if (rc) goto out; rc = 0; LOGD(DEBUG, bl->domid, "bootloader execution successful"); out: bootloader_callback(egc, bl, rc); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_arm.c0000664000175000017500000007333413256712137015056 0ustar smbsmb#include "libxl_internal.h" #include "libxl_arch.h" #include "libxl_libfdt_compat.h" #include "libxl_arm.h" #include #include #include #include /** * IRQ line type. * DT_IRQ_TYPE_NONE - default, unspecified type * DT_IRQ_TYPE_EDGE_RISING - rising edge triggered * DT_IRQ_TYPE_EDGE_FALLING - falling edge triggered * DT_IRQ_TYPE_EDGE_BOTH - rising and falling edge triggered * DT_IRQ_TYPE_LEVEL_HIGH - high level triggered * DT_IRQ_TYPE_LEVEL_LOW - low level triggered */ #define DT_IRQ_TYPE_NONE 0x00000000 #define DT_IRQ_TYPE_EDGE_RISING 0x00000001 #define DT_IRQ_TYPE_EDGE_FALLING 0x00000002 #define DT_IRQ_TYPE_EDGE_BOTH \ (DT_IRQ_TYPE_EDGE_FALLING | DT_IRQ_TYPE_EDGE_RISING) #define DT_IRQ_TYPE_LEVEL_HIGH 0x00000004 #define DT_IRQ_TYPE_LEVEL_LOW 0x00000008 static const char *gicv_to_string(uint8_t gic_version) { switch (gic_version) { case XEN_DOMCTL_CONFIG_GIC_V2: return "V2"; case XEN_DOMCTL_CONFIG_GIC_V3: return "V3"; default: return "unknown"; } } int libxl__arch_domain_prepare_config(libxl__gc *gc, libxl_domain_config *d_config, xc_domain_configuration_t *xc_config) { uint32_t nr_spis = 0; unsigned int i; for (i = 0; i < d_config->b_info.num_irqs; i++) { uint32_t irq = d_config->b_info.irqs[i]; uint32_t spi; if (irq < 32) continue; spi = irq - 32; if (nr_spis <= spi) nr_spis = spi + 1; } LOG(DEBUG, "Configure the domain"); xc_config->nr_spis = nr_spis; LOG(DEBUG, " - Allocate %u SPIs", nr_spis); switch (d_config->b_info.arch_arm.gic_version) { case LIBXL_GIC_VERSION_DEFAULT: xc_config->gic_version = XEN_DOMCTL_CONFIG_GIC_NATIVE; break; case LIBXL_GIC_VERSION_V2: xc_config->gic_version = XEN_DOMCTL_CONFIG_GIC_V2; break; case LIBXL_GIC_VERSION_V3: xc_config->gic_version = XEN_DOMCTL_CONFIG_GIC_V3; break; default: LOG(ERROR, "Unknown GIC version %d", d_config->b_info.arch_arm.gic_version); return ERROR_FAIL; } return 0; } int libxl__arch_domain_save_config(libxl__gc *gc, libxl_domain_config *d_config, const xc_domain_configuration_t *xc_config) { switch (xc_config->gic_version) { case XEN_DOMCTL_CONFIG_GIC_V2: d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V2; break; case XEN_DOMCTL_CONFIG_GIC_V3: d_config->b_info.arch_arm.gic_version = LIBXL_GIC_VERSION_V3; break; default: LOG(ERROR, "Unexpected gic version %u", xc_config->gic_version); return ERROR_FAIL; } return 0; } int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid) { return 0; } int libxl__arch_extra_memory(libxl__gc *gc, const libxl_domain_build_info *info, uint64_t *out) { int rc = 0; uint64_t size = 0; if (libxl_defbool_val(info->acpi)) { rc = libxl__get_acpi_size(gc, info, &size); if (rc < 0) { rc = ERROR_FAIL; goto out; } } *out = LIBXL_MAXMEM_CONSTANT + DIV_ROUNDUP(size, 1024); out: return rc; } static struct arch_info { const char *guest_type; const char *timer_compat; const char *cpu_compat; } arch_info[] = { {"xen-3.0-armv7l", "arm,armv7-timer", "arm,cortex-a15" }, {"xen-3.0-aarch64", "arm,armv8-timer", "arm,armv8" }, }; /* * The device tree compiler (DTC) is allocating the phandle from 1 to * onwards. Reserve a high value for the GIC phandle. */ #define PHANDLE_GIC (65000) typedef uint32_t be32; typedef be32 gic_interrupt[3]; #define ROOT_ADDRESS_CELLS 2 #define ROOT_SIZE_CELLS 2 #define PROP_INITRD_START "linux,initrd-start" #define PROP_INITRD_END "linux,initrd-end" static void set_cell(be32 **cellp, int size, uint64_t val) { int cells = size; while (size--) { (*cellp)[size] = cpu_to_fdt32(val); val >>= 32; } (*cellp) += cells; } static void set_interrupt(gic_interrupt interrupt, unsigned int irq, unsigned int cpumask, unsigned int level) { be32 *cells = interrupt; int is_ppi = (irq < 32); /* SGIs are not describe in the device tree */ assert(irq >= 16); irq -= (is_ppi) ? 16: 32; /* PPIs start at 16, SPIs at 32 */ /* See linux Documentation/devictree/bindings/arm/gic.txt */ set_cell(&cells, 1, is_ppi); /* is a PPI? */ set_cell(&cells, 1, irq); set_cell(&cells, 1, (cpumask << 8) | level); } static void set_range(be32 **cellp, int address_cells, int size_cells, uint64_t address, uint64_t size) { set_cell(cellp, address_cells, address); set_cell(cellp, size_cells, size); } static int fdt_property_compat(libxl__gc *gc, void *fdt, unsigned nr_compat, ...) { const char *compats[nr_compat]; int i; size_t sz; va_list ap; char *compat, *p; va_start(ap, nr_compat); sz = 0; for (i = 0; i < nr_compat; i++) { const char *c = va_arg(ap, const char *); compats[i] = c; sz += strlen(compats[i]) + 1; } va_end(ap); p = compat = libxl__zalloc(gc, sz); for (i = 0; i < nr_compat; i++) { strcpy(p, compats[i]); p += strlen(compats[i]) + 1; } return fdt_property(fdt, "compatible", compat, sz); } static int fdt_property_interrupts(libxl__gc *gc, void *fdt, gic_interrupt *intr, unsigned num_irq) { int res; res = fdt_property(fdt, "interrupts", intr, sizeof (intr[0]) * num_irq); if (res) return res; res = fdt_property_cell(fdt, "interrupt-parent", PHANDLE_GIC); if (res) return res; return 0; } static int fdt_property_regs(libxl__gc *gc, void *fdt, unsigned addr_cells, unsigned size_cells, unsigned num_regs, ...) { uint32_t regs[num_regs*(addr_cells+size_cells)]; be32 *cells = ®s[0]; int i; va_list ap; uint64_t base, size; va_start(ap, num_regs); for (i = 0 ; i < num_regs; i++) { base = addr_cells ? va_arg(ap, uint64_t) : 0; size = size_cells ? va_arg(ap, uint64_t) : 0; set_range(&cells, addr_cells, size_cells, base, size); } va_end(ap); return fdt_property(fdt, "reg", regs, sizeof(regs)); } static int make_root_properties(libxl__gc *gc, const libxl_version_info *vers, void *fdt) { int res; res = fdt_property_string(fdt, "model", GCSPRINTF("XENVM-%d.%d", vers->xen_version_major, vers->xen_version_minor)); if (res) return res; res = fdt_property_compat(gc, fdt, 2, GCSPRINTF("xen,xenvm-%d.%d", vers->xen_version_major, vers->xen_version_minor), "xen,xenvm"); if (res) return res; res = fdt_property_cell(fdt, "interrupt-parent", PHANDLE_GIC); if (res) return res; res = fdt_property_cell(fdt, "#address-cells", ROOT_ADDRESS_CELLS); if (res) return res; res = fdt_property_cell(fdt, "#size-cells", ROOT_SIZE_CELLS); if (res) return res; return 0; } static int make_chosen_node(libxl__gc *gc, void *fdt, bool ramdisk, libxl__domain_build_state *state, const libxl_domain_build_info *info) { int res; /* See linux Documentation/devicetree/... */ res = fdt_begin_node(fdt, "chosen"); if (res) return res; if (state->pv_cmdline) { LOG(DEBUG, "/chosen/bootargs = %s", state->pv_cmdline); res = fdt_property_string(fdt, "bootargs", state->pv_cmdline); if (res) return res; } if (ramdisk) { uint64_t dummy = 0; LOG(DEBUG, "/chosen adding placeholder linux,initrd properties"); res = fdt_property(fdt, PROP_INITRD_START, &dummy, sizeof(dummy)); if (res) return res; res = fdt_property(fdt, PROP_INITRD_END, &dummy, sizeof(dummy)); if (res) return res; } if (libxl_defbool_val(info->acpi)) { const uint64_t acpi_base = GUEST_ACPI_BASE; const char *name = GCSPRINTF("module@%"PRIx64, acpi_base); res = fdt_begin_node(fdt, name); if (res) return res; res = fdt_property_compat(gc, fdt, 2, "xen,guest-acpi", "multiboot,module"); if (res) return res; res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS, 1, 0, 0); if (res) return res; res = fdt_end_node(fdt); if (res) return res; } res = fdt_end_node(fdt); if (res) return res; return 0; } static int make_cpus_node(libxl__gc *gc, void *fdt, int nr_cpus, const struct arch_info *ainfo) { int res, i; uint64_t mpidr_aff; res = fdt_begin_node(fdt, "cpus"); if (res) return res; res = fdt_property_cell(fdt, "#address-cells", 1); if (res) return res; res = fdt_property_cell(fdt, "#size-cells", 0); if (res) return res; for (i = 0; i < nr_cpus; i++) { const char *name; mpidr_aff = libxl__compute_mpdir(i); name = GCSPRINTF("cpu@%"PRIx64, mpidr_aff); res = fdt_begin_node(fdt, name); if (res) return res; res = fdt_property_string(fdt, "device_type", "cpu"); if (res) return res; res = fdt_property_compat(gc, fdt, 1, ainfo->cpu_compat); if (res) return res; res = fdt_property_string(fdt, "enable-method", "psci"); if (res) return res; res = fdt_property_regs(gc, fdt, 1, 0, 1, mpidr_aff); if (res) return res; res = fdt_end_node(fdt); if (res) return res; } res = fdt_end_node(fdt); if (res) return res; return 0; } static int make_psci_node(libxl__gc *gc, void *fdt) { int res; res = fdt_begin_node(fdt, "psci"); if (res) return res; res = fdt_property_compat(gc, fdt, 2, "arm,psci-0.2","arm,psci"); if (res) return res; res = fdt_property_string(fdt, "method", "hvc"); if (res) return res; res = fdt_property_cell(fdt, "cpu_off", PSCI_cpu_off); if (res) return res; res = fdt_property_cell(fdt, "cpu_on", PSCI_cpu_on); if (res) return res; res = fdt_end_node(fdt); if (res) return res; return 0; } static int make_memory_nodes(libxl__gc *gc, void *fdt, const struct xc_dom_image *dom) { int res, i; const char *name; const uint64_t bankbase[] = GUEST_RAM_BANK_BASES; for (i = 0; i < GUEST_RAM_BANKS; i++) { name = GCSPRINTF("memory@%"PRIx64, bankbase[i]); LOG(DEBUG, "Creating placeholder node /%s", name); res = fdt_begin_node(fdt, name); if (res) return res; res = fdt_property_string(fdt, "device_type", "memory"); if (res) return res; res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS, 1, 0, 0); if (res) return res; res = fdt_end_node(fdt); if (res) return res; } return 0; } static int make_gicv2_node(libxl__gc *gc, void *fdt, uint64_t gicd_base, uint64_t gicd_size, uint64_t gicc_base, uint64_t gicc_size) { int res; const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base); res = fdt_begin_node(fdt, name); if (res) return res; res = fdt_property_compat(gc, fdt, 2, "arm,cortex-a15-gic", "arm,cortex-a9-gic"); if (res) return res; res = fdt_property_cell(fdt, "#interrupt-cells", 3); if (res) return res; res = fdt_property_cell(fdt, "#address-cells", 0); if (res) return res; res = fdt_property(fdt, "interrupt-controller", NULL, 0); if (res) return res; res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS, 2, gicd_base, gicd_size, gicc_base, gicc_size); if (res) return res; res = fdt_property_cell(fdt, "linux,phandle", PHANDLE_GIC); if (res) return res; res = fdt_property_cell(fdt, "phandle", PHANDLE_GIC); if (res) return res; res = fdt_end_node(fdt); if (res) return res; return 0; } static int make_gicv3_node(libxl__gc *gc, void *fdt) { int res; const uint64_t gicd_base = GUEST_GICV3_GICD_BASE; const uint64_t gicd_size = GUEST_GICV3_GICD_SIZE; const uint64_t gicr0_base = GUEST_GICV3_GICR0_BASE; const uint64_t gicr0_size = GUEST_GICV3_GICR0_SIZE; const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base); res = fdt_begin_node(fdt, name); if (res) return res; res = fdt_property_compat(gc, fdt, 1, "arm,gic-v3"); if (res) return res; res = fdt_property_cell(fdt, "#interrupt-cells", 3); if (res) return res; res = fdt_property_cell(fdt, "#address-cells", 0); if (res) return res; res = fdt_property(fdt, "interrupt-controller", NULL, 0); if (res) return res; res = fdt_property_cell(fdt, "redistributor-stride", GUEST_GICV3_RDIST_STRIDE); if (res) return res; res = fdt_property_cell(fdt, "#redistributor-regions", GUEST_GICV3_RDIST_REGIONS); if (res) return res; res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS, 2, gicd_base, gicd_size, gicr0_base, gicr0_size); if (res) return res; res = fdt_property_cell(fdt, "linux,phandle", PHANDLE_GIC); if (res) return res; res = fdt_property_cell(fdt, "phandle", PHANDLE_GIC); if (res) return res; res = fdt_end_node(fdt); if (res) return res; return 0; } static int make_timer_node(libxl__gc *gc, void *fdt, const struct arch_info *ainfo, uint32_t frequency) { int res; gic_interrupt ints[3]; res = fdt_begin_node(fdt, "timer"); if (res) return res; res = fdt_property_compat(gc, fdt, 1, ainfo->timer_compat); if (res) return res; set_interrupt(ints[0], GUEST_TIMER_PHYS_S_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW); set_interrupt(ints[1], GUEST_TIMER_PHYS_NS_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW); set_interrupt(ints[2], GUEST_TIMER_VIRT_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW); res = fdt_property_interrupts(gc, fdt, ints, 3); if (res) return res; if ( frequency ) fdt_property_u32(fdt, "clock-frequency", frequency); res = fdt_end_node(fdt); if (res) return res; return 0; } static int make_hypervisor_node(libxl__gc *gc, void *fdt, const libxl_version_info *vers) { int res; gic_interrupt intr; /* See linux Documentation/devicetree/bindings/arm/xen.txt */ res = fdt_begin_node(fdt, "hypervisor"); if (res) return res; res = fdt_property_compat(gc, fdt, 2, GCSPRINTF("xen,xen-%d.%d", vers->xen_version_major, vers->xen_version_minor), "xen,xen"); if (res) return res; /* reg 0 is grant table space */ res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS, 1,GUEST_GNTTAB_BASE, GUEST_GNTTAB_SIZE); if (res) return res; /* * interrupts is evtchn upcall: * - Active-low level-sensitive * - All cpus */ set_interrupt(intr, GUEST_EVTCHN_PPI, 0xf, DT_IRQ_TYPE_LEVEL_LOW); res = fdt_property_interrupts(gc, fdt, &intr, 1); if (res) return res; res = fdt_end_node(fdt); if (res) return res; return 0; } static const struct arch_info *get_arch_info(libxl__gc *gc, const struct xc_dom_image *dom) { int i; for (i=0; i < ARRAY_SIZE(arch_info); i++) { const struct arch_info *info = &arch_info[i]; if (!strcmp(dom->guest_type, info->guest_type)) return info; } LOG(ERROR, "Unable to find arch FDT info for %s", dom->guest_type); return NULL; } static void debug_dump_fdt(libxl__gc *gc, void *fdt) { int fd = -1, rc, r; const char *dtb = getenv("LIBXL_DEBUG_DUMP_DTB"); if (!dtb) goto out; fd = open(dtb, O_CREAT|O_TRUNC|O_WRONLY, 0666); if (fd < 0) { LOGE(DEBUG, "cannot open %s for LIBXL_DEBUG_DUMP_DTB", dtb); goto out; } rc = libxl_write_exactly(CTX, fd, fdt, fdt_totalsize(fdt), dtb, "dtb"); if (rc < 0) goto out; out: if (fd >= 0) { r = close(fd); if (r < 0) LOGE(DEBUG, "failed to close DTB debug dump output"); } } #ifdef ENABLE_PARTIAL_DEVICE_TREE static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size) { int r; if (fdt_magic(fdt) != FDT_MAGIC) { LOG(ERROR, "Partial FDT is not a valid Flat Device Tree"); return ERROR_FAIL; } r = fdt_check_header(fdt); if (r) { LOG(ERROR, "Failed to check the partial FDT (%d)", r); return ERROR_FAIL; } if (fdt_totalsize(fdt) > size) { LOG(ERROR, "Partial FDT totalsize is too big"); return ERROR_FAIL; } return 0; } static int copy_properties(libxl__gc *gc, void *fdt, void *pfdt, int nodeoff) { int propoff, nameoff, r; const struct fdt_property *prop; for (propoff = fdt_first_property_offset(pfdt, nodeoff); propoff >= 0; propoff = fdt_next_property_offset(pfdt, propoff)) { if (!(prop = fdt_get_property_by_offset(pfdt, propoff, NULL))) { return -FDT_ERR_INTERNAL; } nameoff = fdt32_to_cpu(prop->nameoff); r = fdt_property(fdt, fdt_string(pfdt, nameoff), prop->data, fdt32_to_cpu(prop->len)); if (r) return r; } /* FDT_ERR_NOTFOUND => There is no more properties for this node */ return (propoff != -FDT_ERR_NOTFOUND)? propoff : 0; } /* Copy a node from the partial device tree to the guest device tree */ static int copy_node(libxl__gc *gc, void *fdt, void *pfdt, int nodeoff, int depth) { int r; r = fdt_begin_node(fdt, fdt_get_name(pfdt, nodeoff, NULL)); if (r) return r; r = copy_properties(gc, fdt, pfdt, nodeoff); if (r) return r; for (nodeoff = fdt_first_subnode(pfdt, nodeoff); nodeoff >= 0; nodeoff = fdt_next_subnode(pfdt, nodeoff)) { r = copy_node(gc, fdt, pfdt, nodeoff, depth + 1); if (r) return r; } if (nodeoff != -FDT_ERR_NOTFOUND) return nodeoff; r = fdt_end_node(fdt); if (r) return r; return 0; } static int copy_node_by_path(libxl__gc *gc, const char *path, void *fdt, void *pfdt) { int nodeoff, r; const char *name = strrchr(path, '/'); if (!name) return -FDT_ERR_INTERNAL; name++; /* * The FDT function to look at a node doesn't take into account the * unit (i.e anything after @) when search by name. Check if the * name exactly matches. */ nodeoff = fdt_path_offset(pfdt, path); if (nodeoff < 0) return nodeoff; if (strcmp(fdt_get_name(pfdt, nodeoff, NULL), name)) return -FDT_ERR_NOTFOUND; r = copy_node(gc, fdt, pfdt, nodeoff, 0); if (r) return r; return 0; } /* * The partial device tree is not copied entirely. Only the relevant bits are * copied to the guest device tree: * - /passthrough node * - /aliases node */ static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt) { int r; r = copy_node_by_path(gc, "/passthrough", fdt, pfdt); if (r < 0) { LOG(ERROR, "Can't copy the node \"/passthrough\" from the partial FDT"); return r; } r = copy_node_by_path(gc, "/aliases", fdt, pfdt); if (r < 0 && r != -FDT_ERR_NOTFOUND) { LOG(ERROR, "Can't copy the node \"/aliases\" from the partial FDT"); return r; } return 0; } #else static int check_partial_fdt(libxl__gc *gc, void *fdt, size_t size) { LOG(ERROR, "partial device tree not supported"); return ERROR_FAIL; } static int copy_partial_fdt(libxl__gc *gc, void *fdt, void *pfdt) { /* * We should never be here when the partial device tree is not * supported. * */ return -FDT_ERR_INTERNAL; } #endif /* ENABLE_PARTIAL_DEVICE_TREE */ #define FDT_MAX_SIZE (1<<20) static int libxl__prepare_dtb(libxl__gc *gc, libxl_domain_build_info *info, libxl__domain_build_state *state, struct xc_dom_image *dom) { void *fdt = NULL; void *pfdt = NULL; int rc, res; size_t fdt_size = 0; int pfdt_size = 0; const libxl_version_info *vers; const struct arch_info *ainfo; /* convenience aliases */ xc_domain_configuration_t *xc_config = &state->config; vers = libxl_get_version_info(CTX); if (vers == NULL) return ERROR_FAIL; ainfo = get_arch_info(gc, dom); if (ainfo == NULL) return ERROR_FAIL; LOG(DEBUG, "constructing DTB for Xen version %d.%d guest", vers->xen_version_major, vers->xen_version_minor); LOG(DEBUG, " - vGIC version: %s", gicv_to_string(xc_config->gic_version)); if (info->device_tree) { LOG(DEBUG, " - Partial device tree provided: %s", info->device_tree); rc = libxl_read_file_contents(CTX, info->device_tree, &pfdt, &pfdt_size); if (rc) { LOGEV(ERROR, rc, "failed to read the partial device file %s", info->device_tree); return ERROR_FAIL; } libxl__ptr_add(gc, pfdt); if (check_partial_fdt(gc, pfdt, pfdt_size)) return ERROR_FAIL; } /* * Call "call" handling FDT_ERR_*. Will either: * - loop back to retry_resize * - set rc and goto out * - fall through successfully * * On FDT_ERR_NOSPACE we start again from scratch rather than * realloc+libfdt_open_into because "call" may have failed half way * through a series of steps leaving the partial tree in an * inconsistent state, e.g. leaving a node open. */ #define FDT( call ) do { \ int fdt_res = (call); \ if (fdt_res == -FDT_ERR_NOSPACE && fdt_size < FDT_MAX_SIZE) \ goto next_resize; \ else if (fdt_res < 0) { \ LOG(ERROR, "FDT: %s failed: %d = %s", \ #call, fdt_res, fdt_strerror(fdt_res)); \ rc = ERROR_FAIL; \ goto out; \ } \ } while(0) for (;;) { next_resize: if (fdt_size) { fdt_size <<= 1; LOG(DEBUG, "Increasing FDT size to %zd and retrying", fdt_size); } else { fdt_size = 4096; } fdt = libxl__realloc(gc, fdt, fdt_size); FDT( fdt_create(fdt, fdt_size) ); FDT( fdt_finish_reservemap(fdt) ); FDT( fdt_begin_node(fdt, "") ); FDT( make_root_properties(gc, vers, fdt) ); FDT( make_chosen_node(gc, fdt, !!dom->ramdisk_blob, state, info) ); FDT( make_cpus_node(gc, fdt, info->max_vcpus, ainfo) ); FDT( make_psci_node(gc, fdt) ); FDT( make_memory_nodes(gc, fdt, dom) ); switch (xc_config->gic_version) { case XEN_DOMCTL_CONFIG_GIC_V2: FDT( make_gicv2_node(gc, fdt, GUEST_GICD_BASE, GUEST_GICD_SIZE, GUEST_GICC_BASE, GUEST_GICC_SIZE) ); break; case XEN_DOMCTL_CONFIG_GIC_V3: FDT( make_gicv3_node(gc, fdt) ); break; default: LOG(ERROR, "Unknown GIC version %s", gicv_to_string(xc_config->gic_version)); rc = ERROR_FAIL; goto out; } FDT( make_timer_node(gc, fdt, ainfo, xc_config->clock_frequency) ); FDT( make_hypervisor_node(gc, fdt, vers) ); if (pfdt) FDT( copy_partial_fdt(gc, fdt, pfdt) ); FDT( fdt_end_node(fdt) ); FDT( fdt_finish(fdt) ); break; } #undef FDT LOG(DEBUG, "fdt total size %d", fdt_totalsize(fdt)); res = xc_dom_devicetree_mem(dom, fdt, fdt_totalsize(fdt)); if (res) { LOGE(ERROR, "xc_dom_devicetree_mem failed"); rc = ERROR_FAIL; goto out; } rc = 0; out: return rc; } int libxl__arch_domain_init_hw_description(libxl__gc *gc, libxl_domain_build_info *info, libxl__domain_build_state *state, struct xc_dom_image *dom) { int rc; uint64_t val; assert(info->type == LIBXL_DOMAIN_TYPE_PV); /* Set the value of domain param HVM_PARAM_CALLBACK_IRQ. */ val = MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI, HVM_PARAM_CALLBACK_IRQ_TYPE_MASK); /* Active-low level-sensitive */ val |= MASK_INSR(HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_LOW_LEVEL, HVM_PARAM_CALLBACK_TYPE_PPI_FLAG_MASK); val |= GUEST_EVTCHN_PPI; rc = xc_hvm_param_set(dom->xch, dom->guest_domid, HVM_PARAM_CALLBACK_IRQ, val); if (rc) return rc; rc = libxl__prepare_dtb(gc, info, state, dom); if (rc) goto out; if (!libxl_defbool_val(info->acpi)) { LOG(DEBUG, "Generating ACPI tables is disabled by user."); rc = 0; goto out; } if (strcmp(dom->guest_type, "xen-3.0-aarch64")) { /* ACPI is only supported for 64-bit guest currently. */ LOG(ERROR, "Can not enable libxl option 'acpi' for %s", dom->guest_type); rc = ERROR_FAIL; goto out; } rc = libxl__prepare_acpi(gc, info, dom); out: return rc; } static void finalise_one_node(libxl__gc *gc, void *fdt, const char *uname, uint64_t base, uint64_t size) { int node, res; const char *name = GCSPRINTF("%s@%"PRIx64, uname, base); node = fdt_path_offset(fdt, name); assert(node > 0); if (size == 0) { LOG(DEBUG, "Nopping out placeholder node %s", name); fdt_nop_node(fdt, node); } else { uint32_t regs[ROOT_ADDRESS_CELLS+ROOT_SIZE_CELLS]; be32 *cells = ®s[0]; LOG(DEBUG, "Populating placeholder node %s", name); set_range(&cells, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS, base, size); res = fdt_setprop_inplace(fdt, node, "reg", regs, sizeof(regs)); assert(!res); } } int libxl__arch_domain_finalise_hw_description(libxl__gc *gc, libxl_domain_build_info *info, struct xc_dom_image *dom) { void *fdt = dom->devicetree_blob; int i; const uint64_t bankbase[] = GUEST_RAM_BANK_BASES; const struct xc_dom_seg *ramdisk = dom->ramdisk_blob ? &dom->ramdisk_seg : NULL; if (ramdisk) { int chosen, res; uint64_t val; /* Neither the fdt_path_offset() nor either of the * fdt_setprop_inplace() calls can fail. If they do then * make_chosen_node() (see above) has got something very * wrong. */ chosen = fdt_path_offset(fdt, "/chosen"); assert(chosen > 0); LOG(DEBUG, "/chosen updating initrd properties to cover " "%"PRIx64"-%"PRIx64, ramdisk->vstart, ramdisk->vend); val = cpu_to_fdt64(ramdisk->vstart); res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_START, &val, sizeof(val)); assert(!res); val = cpu_to_fdt64(ramdisk->vend); res = fdt_setprop_inplace(fdt, chosen, PROP_INITRD_END, &val, sizeof(val)); assert(!res); } for (i = 0; i < GUEST_RAM_BANKS; i++) { const uint64_t size = (uint64_t)dom->rambank_size[i] << XC_PAGE_SHIFT; finalise_one_node(gc, fdt, "/memory", bankbase[i], size); } if (dom->acpi_modules[0].data) { finalise_one_node(gc, fdt, "/chosen/module", GUEST_ACPI_BASE, dom->acpi_modules[0].length); } debug_dump_fdt(gc, fdt); return 0; } int libxl__arch_vnuma_build_vmemrange(libxl__gc *gc, uint32_t domid, libxl_domain_build_info *info, libxl__domain_build_state *state) { return libxl__vnuma_build_vmemrange_pv_generic(gc, domid, info, state); } int libxl__arch_domain_map_irq(libxl__gc *gc, uint32_t domid, int irq) { return xc_domain_bind_pt_spi_irq(CTX->xch, domid, irq, irq); } int libxl__arch_domain_construct_memmap(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid, struct xc_dom_image *dom) { return 0; } void libxl__arch_domain_build_info_acpi_setdefault( libxl_domain_build_info *b_info) { libxl_defbool_setdefault(&b_info->acpi, false); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/gentypes.py0000664000175000017500000006540213256712137015146 0ustar smbsmb#!/usr/bin/python import sys import re import idl def libxl_C_instance_of(ty, instancename): if isinstance(ty, idl.Aggregate) and ty.typename is None: if instancename is None: return libxl_C_type_define(ty) else: return libxl_C_type_define(ty) + " " + instancename s = "" if isinstance(ty, idl.Array): s += libxl_C_instance_of(ty.lenvar.type, ty.lenvar.name) + ";\n" return s + ty.typename + " " + instancename def libxl_C_type_define(ty, indent = ""): s = "" if isinstance(ty, idl.Enumeration): if ty.typename is None: s += "enum {\n" else: s += "typedef enum %s {\n" % ty.typename for v in ty.values: x = "%s = %d" % (v.name, v.value) x = x.replace("\n", "\n ") s += " " + x + ",\n" if ty.typename is None: s += "}" else: s += "} %s" % ty.typename elif isinstance(ty, idl.Aggregate): if isinstance(ty, idl.KeyedUnion): s += libxl_C_instance_of(ty.keyvar.type, ty.keyvar.name) + ";\n" if ty.typename is None: s += "%s {\n" % ty.kind else: s += "typedef %s %s {\n" % (ty.kind, ty.typename) for f in ty.fields: if isinstance(ty, idl.KeyedUnion) and f.type is None: continue x = libxl_C_instance_of(f.type, f.name) if f.const: x = "const " + x x = x.replace("\n", "\n ") s += " " + x + ";\n" if ty.typename is None: s += "}" else: s += "} %s" % ty.typename else: raise NotImplementedError("%s" % type(ty)) return s.replace("\n", "\n%s" % indent) def libxl_C_type_dispose(ty, v, indent = " ", parent = None): s = "" if isinstance(ty, idl.KeyedUnion): if parent is None: raise Exception("KeyedUnion type must have a parent") s += "switch (%s) {\n" % (parent + ty.keyvar.name) for f in ty.fields: (nparent,fexpr) = ty.member(v, f, parent is None) s += "case %s:\n" % f.enumname if f.type is not None: s += libxl_C_type_dispose(f.type, fexpr, indent + " ", nparent) s += " break;\n" s += "}\n" elif isinstance(ty, idl.Array): if parent is None: raise Exception("Array type must have a parent") if ty.elem_type.dispose_fn is not None: s += "{\n" s += " int i;\n" s += " for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name) s += libxl_C_type_dispose(ty.elem_type, v+"[i]", indent + " ", parent) if ty.dispose_fn is not None: if ty.elem_type.dispose_fn is not None: s += " " s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None)) if ty.elem_type.dispose_fn is not None: s += "}\n" elif isinstance(ty, idl.Struct) and (parent is None or ty.dispose_fn is None): for f in [f for f in ty.fields if not f.const]: (nparent,fexpr) = ty.member(v, f, parent is None) s += libxl_C_type_dispose(f.type, fexpr, "", nparent) else: if ty.dispose_fn is not None: s += "%s(%s);\n" % (ty.dispose_fn, ty.pass_arg(v, parent is None)) if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) def libxl_C_type_copy(ty, v, w, indent = " ", vparent = None, wparent = None): s = "" if vparent is None: s += "GC_INIT(ctx);\n"; if isinstance(ty, idl.KeyedUnion): if vparent is None or wparent is None: raise Exception("KeyedUnion type must have a parent") s += "%s = %s;\n" % ((vparent + ty.keyvar.name), (wparent + ty.keyvar.name)) s += "switch (%s) {\n" % (wparent + ty.keyvar.name) for f in ty.fields: (vnparent,vfexpr) = ty.member(v, f, vparent is None) (wnparent,wfexpr) = ty.member(w, f, wparent is None) s += "case %s:\n" % f.enumname if f.type is not None: s += libxl_C_type_copy(f.type, vfexpr, wfexpr, indent + " ", vnparent, wnparent) s += " break;\n" s += "}\n" elif isinstance(ty, idl.Array): if vparent is None or wparent is None: raise Exception("Array type must have a parent") s += "%s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (ty.pass_arg(v, vparent is None), (wparent + ty.lenvar.name), ty.pass_arg(w, wparent is None)) s += "%s = %s;\n" % ((vparent + ty.lenvar.name), (wparent + ty.lenvar.name)) s += "{\n" s += " int i;\n" s += " for (i=0; i<%s; i++)\n" % (wparent + ty.lenvar.name) s += libxl_C_type_copy(ty.elem_type, v+"[i]", w+"[i]", indent + " ", vparent, wparent) s += "}\n" elif isinstance(ty, idl.Struct) and ((vparent is None and wparent is None) or ty.copy_fn is None): for f in [f for f in ty.fields if not f.const and not f.type.private]: (vnparent,vfexpr) = ty.member(v, f, vparent is None) (wnparent,wfexpr) = ty.member(w, f, wparent is None) s += libxl_C_type_copy(f.type, vfexpr, wfexpr, "", vnparent, wnparent) else: if ty.copy_fn is not None: s += "%s(ctx, %s, %s);\n" % (ty.copy_fn, ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_REFERENCE), ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_REFERENCE)) else: s += "%s = %s;\n" % (ty.pass_arg(v, vparent is None, passby=idl.PASS_BY_VALUE), ty.pass_arg(w, wparent is None, passby=idl.PASS_BY_VALUE)) if vparent is None: s += "GC_FREE;\n" if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) def libxl_init_members(ty, nesting = 0): """Returns a list of members of ty which require a separate init""" if isinstance(ty, idl.Aggregate): return [f for f in ty.fields if not f.const and isinstance(f.type,idl.KeyedUnion)] else: return [] def _libxl_C_type_init(ty, v, indent = " ", parent = None, subinit=False): s = "" if isinstance(ty, idl.KeyedUnion): if parent is None: raise Exception("KeyedUnion type must have a parent") if subinit: s += "switch (%s) {\n" % (parent + ty.keyvar.name) for f in ty.fields: (nparent,fexpr) = ty.member(v, f, parent is None) s += "case %s:\n" % f.enumname if f.type is not None: s += _libxl_C_type_init(f.type, fexpr, " ", nparent) s += " break;\n" s += "}\n" else: if ty.keyvar.init_val: s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.init_val) elif ty.keyvar.type.init_val: s += "%s = %s;\n" % (parent + ty.keyvar.name, ty.keyvar.type.init_val) elif isinstance(ty, idl.Struct) and (parent is None or ty.init_fn is None): for f in [f for f in ty.fields if not f.const]: (nparent,fexpr) = ty.member(v, f, parent is None) if f.init_val is not None: s += "%s = %s;\n" % (fexpr, f.init_val) else: s += _libxl_C_type_init(f.type, fexpr, "", nparent) else: if ty.init_val is not None: s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), ty.init_val) elif ty.init_fn is not None: s += "%s(%s);\n" % (ty.init_fn, ty.pass_arg(v, parent is None)) if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) def libxl_C_type_init(ty): s = "" s += "void %s(%s)\n" % (ty.init_fn, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)) s += "{\n" s += " memset(p, '\\0', sizeof(*p));\n" s += _libxl_C_type_init(ty, "p") s += "}\n" s += "\n" return s def libxl_C_type_member_init(ty, field): if not isinstance(field.type, idl.KeyedUnion): raise Exception("Only KeyedUnion is supported for member init") ku = field.type s = "" s += "void %s(%s, %s)\n" % (ty.init_fn + "_" + ku.keyvar.name, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE), ku.keyvar.type.make_arg(ku.keyvar.name)) s += "{\n" if ku.keyvar.init_val is not None: init_val = ku.keyvar.init_val elif ku.keyvar.type.init_val is not None: init_val = ku.keyvar.type.init_val else: init_val = None (nparent,fexpr) = ty.member(ty.pass_arg("p"), ku.keyvar, isref=True) if init_val is not None: s += " assert(%s == %s);\n" % (fexpr, init_val) else: s += " assert(!%s);\n" % (fexpr) s += " %s = %s;\n" % (fexpr, ku.keyvar.name) (nparent,fexpr) = ty.member(ty.pass_arg("p"), field, isref=True) s += _libxl_C_type_init(ku, fexpr, parent=nparent, subinit=True) s += "}\n" s += "\n" return s def libxl_C_type_gen_map_key(f, parent, indent = ""): s = "" if isinstance(f.type, idl.KeyedUnion): s += "switch (%s) {\n" % (parent + f.type.keyvar.name) for x in f.type.fields: v = f.type.keyvar.name + "." + x.name s += "case %s:\n" % x.enumname s += " s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (v, v) s += " if (s != yajl_gen_status_ok)\n" s += " goto out;\n" s += " break;\n" s += "}\n" else: s += "s = yajl_gen_string(hand, (const unsigned char *)\"%s\", sizeof(\"%s\")-1);\n" % (f.name, f.name) s += "if (s != yajl_gen_status_ok)\n" s += " goto out;\n" if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) def get_init_val(f): if f.init_val is not None: return f.init_val elif f.type.init_val is not None: return f.type.init_val return None def get_default_expr(f, nparent, fexpr): if isinstance(f.type, idl.Aggregate): return "1 /* always generate JSON output for aggregate type */" if isinstance(f.type, idl.Array): return "%s && %s" % (fexpr, nparent + f.type.lenvar.name) init_val = get_init_val(f) if init_val is not None: return "%s != %s" % (fexpr, init_val) if f.type.check_default_fn: return "!%s(&%s)" % (f.type.check_default_fn, fexpr) return "%s" % fexpr def libxl_C_type_gen_json(ty, v, indent = " ", parent = None): s = "" if parent is None: s += "yajl_gen_status s;\n" if isinstance(ty, idl.Array): if parent is None: raise Exception("Array type must have a parent") s += "{\n" s += " int i;\n" s += " s = yajl_gen_array_open(hand);\n" s += " if (s != yajl_gen_status_ok)\n" s += " goto out;\n" s += " for (i=0; i<%s; i++) {\n" % (parent + ty.lenvar.name) s += libxl_C_type_gen_json(ty.elem_type, v+"[i]", indent + " ", parent) s += " }\n" s += " s = yajl_gen_array_close(hand);\n" s += " if (s != yajl_gen_status_ok)\n" s += " goto out;\n" s += "}\n" elif isinstance(ty, idl.Enumeration): s += "s = libxl__yajl_gen_enum(hand, %s_to_string(%s));\n" % (ty.typename, ty.pass_arg(v, parent is None)) s += "if (s != yajl_gen_status_ok)\n" s += " goto out;\n" elif isinstance(ty, idl.KeyedUnion): if parent is None: raise Exception("KeyedUnion type must have a parent") s += "switch (%s) {\n" % (parent + ty.keyvar.name) for f in ty.fields: (nparent,fexpr) = ty.member(v, f, parent is None) s += "case %s:\n" % f.enumname if f.type is not None: s += libxl_C_type_gen_json(f.type, fexpr, indent + " ", nparent) else: s += " s = yajl_gen_map_open(hand);\n" s += " if (s != yajl_gen_status_ok)\n" s += " goto out;\n" s += " s = yajl_gen_map_close(hand);\n" s += " if (s != yajl_gen_status_ok)\n" s += " goto out;\n" s += " break;\n" s += "}\n" elif isinstance(ty, idl.Struct) and (parent is None or ty.json_gen_fn is None): s += "s = yajl_gen_map_open(hand);\n" s += "if (s != yajl_gen_status_ok)\n" s += " goto out;\n" for f in [f for f in ty.fields if not f.const and not f.type.private]: (nparent,fexpr) = ty.member(v, f, parent is None) default_expr = get_default_expr(f, nparent, fexpr) s += "if (%s) {\n" % default_expr s += libxl_C_type_gen_map_key(f, nparent, " ") s += libxl_C_type_gen_json(f.type, fexpr, " ", nparent) s += "}\n" s += "s = yajl_gen_map_close(hand);\n" s += "if (s != yajl_gen_status_ok)\n" s += " goto out;\n" else: if ty.json_gen_fn is not None: s += "s = %s(hand, %s);\n" % (ty.json_gen_fn, ty.pass_arg(v, parent is None)) s += "if (s != yajl_gen_status_ok)\n" s += " goto out;\n" if parent is None: s += "out:\n" s += "return s;\n" if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) def libxl_C_type_to_json(ty, v, indent = " "): s = "" gen = "(libxl__gen_json_callback)&%s_gen_json" % ty.typename s += "return libxl__object_to_json(ctx, \"%s\", %s, (void *)%s);\n" % (ty.typename, gen, ty.pass_arg(v, passby=idl.PASS_BY_REFERENCE)) if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) def libxl_C_type_parse_json(ty, w, v, indent = " ", parent = None, discriminator = None): s = "" if parent is None: s += "int rc = 0;\n" s += "const libxl__json_object *x = o;\n" if isinstance(ty, idl.Array): if parent is None: raise Exception("Array type must have a parent") if discriminator is not None: raise Exception("Only KeyedUnion can have discriminator") lenvar = parent + ty.lenvar.name s += "{\n" s += " libxl__json_object *t;\n" s += " int i;\n" s += " if (!libxl__json_object_is_array(x)) {\n" s += " rc = -1;\n" s += " goto out;\n" s += " }\n" s += " %s = x->u.array->count;\n" % lenvar s += " %s = libxl__calloc(NOGC, %s, sizeof(*%s));\n" % (v, lenvar, v) s += " if (!%s && %s != 0) {\n" % (v, lenvar) s += " rc = -1;\n" s += " goto out;\n" s += " }\n" s += " for (i=0; (t=libxl__json_array_get(x,i)); i++) {\n" s += libxl_C_type_parse_json(ty.elem_type, "t", v+"[i]", indent + " ", parent) s += " }\n" s += " if (i != %s) {\n" % lenvar s += " rc = -1;\n" s += " goto out;\n" s += " }\n" s += "}\n" elif isinstance(ty, idl.Enumeration): if discriminator is not None: raise Exception("Only KeyedUnion can have discriminator") s += "{\n" s += " const char *enum_str;\n" s += " if (!libxl__json_object_is_string(x)) {\n" s += " rc = -1;\n" s += " goto out;\n" s += " }\n" s += " enum_str = libxl__json_object_get_string(x);\n" s += " rc = %s_from_string(enum_str, %s);\n" % (ty.typename, ty.pass_arg(v, parent is None, idl.PASS_BY_REFERENCE)) s += " if (rc)\n" s += " goto out;\n" s += "}\n" elif isinstance(ty, idl.KeyedUnion): if parent is None: raise Exception("KeyedUnion type must have a parent") if discriminator is None: raise Excpetion("KeyedUnion type must have a discriminator") for f in ty.fields: if f.enumname != discriminator: continue (nparent,fexpr) = ty.member(v, f, parent is None) if f.type is not None: s += libxl_C_type_parse_json(f.type, w, fexpr, indent + " ", nparent) elif isinstance(ty, idl.Struct) and (parent is None or ty.json_parse_fn is None): if discriminator is not None: raise Exception("Only KeyedUnion can have discriminator") for f in [f for f in ty.fields if not f.const and not f.type.private]: saved_var_name = "saved_%s" % f.name s += "{\n" s += " const libxl__json_object *%s = x;\n" % saved_var_name if isinstance(f.type, idl.KeyedUnion): for x in f.type.fields: s += " x = libxl__json_map_get(\"%s\", %s, JSON_MAP);\n" % \ (f.type.keyvar.name + "." + x.name, w) s += " if (x) {\n" (nparent, fexpr) = ty.member(v, f.type.keyvar, parent is None) s += " %s_init_%s(%s, %s);\n" % (ty.typename, f.type.keyvar.name, v, x.enumname) (nparent,fexpr) = ty.member(v, f, parent is None) s += libxl_C_type_parse_json(f.type, "x", fexpr, " ", nparent, x.enumname) s += " }\n" else: s += " x = libxl__json_map_get(\"%s\", %s, %s);\n" % (f.name, w, f.type.json_parse_type) s += " if (x) {\n" (nparent,fexpr) = ty.member(v, f, parent is None) s += libxl_C_type_parse_json(f.type, "x", fexpr, " ", nparent) s += " }\n" s += " x = %s;\n" % saved_var_name s += "}\n" else: if discriminator is not None: raise Exception("Only KeyedUnion can have discriminator") if ty.json_parse_fn is not None: s += "rc = %s(gc, %s, &%s);\n" % (ty.json_parse_fn, w, v) s += "if (rc)\n" s += " goto out;\n" if parent is None: s += "out:\n" s += "return rc;\n" if s != "": s = indent +s return s.replace("\n", "\n%s" % indent).rstrip(indent) def libxl_C_type_from_json(ty, v, w, indent = " "): s = "" parse = "(libxl__json_parse_callback)&%s_parse_json" % (ty.namespace + "_" + ty.rawname) s += "return libxl__object_from_json(ctx, \"%s\", %s, %s, %s);\n" % (ty.typename, parse, v, w) if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) def libxl_C_enum_to_string(ty, e, indent = " "): s = "" s += "switch(%s) {\n" % e for v in ty.values: s += " case %s:\n" % (v.name) s += " return \"%s\";\n" % (v.valuename.lower()) s += " default:\n " s += " return NULL;\n" s += "}\n" if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) def libxl_C_enum_strings(ty, indent=""): s = "" s += "libxl_enum_string_table %s_string_table[] = {\n" % (ty.typename) for v in ty.values: s += " { .s = \"%s\", .v = %s },\n" % (v.valuename.lower(), v.name) s += " { NULL, -1 },\n" s += "};\n" s += "\n" if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) def libxl_C_enum_from_string(ty, str, e, indent = " "): s = "" s += "return libxl__enum_from_string(%s_string_table,\n" % ty.typename s += " %s, (int *)%s);\n" % (str, e) if s != "": s = indent + s return s.replace("\n", "\n%s" % indent).rstrip(indent) if __name__ == '__main__': if len(sys.argv) != 6: print >>sys.stderr, "Usage: gentypes.py
" sys.exit(1) (_, idlname, header, header_private, header_json, impl) = sys.argv (builtins,types) = idl.parse(idlname) print "outputting libxl type definitions to %s" % header f = open(header, "w") header_define = header.upper().replace('.','_') f.write("""#ifndef %s #define %s /* * DO NOT EDIT. * * This file is autogenerated by * "%s" */ """ % (header_define, header_define, " ".join(sys.argv))) for ty in types: f.write(libxl_C_type_define(ty) + ";\n") if ty.dispose_fn is not None: f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.dispose_fn, ty.make_arg("p"))) if ty.copy_fn is not None: f.write("%svoid %s(libxl_ctx *ctx, %s, const %s);\n" % (ty.hidden(), ty.copy_fn, ty.make_arg("dst"), ty.make_arg("src"))) if ty.init_fn is not None: f.write("%svoid %s(%s);\n" % (ty.hidden(), ty.init_fn, ty.make_arg("p"))) for field in libxl_init_members(ty): if not isinstance(field.type, idl.KeyedUnion): raise Exception("Only KeyedUnion is supported for member init") ku = field.type f.write("%svoid %s(%s, %s);\n" % (ty.hidden(), ty.init_fn + "_" + ku.keyvar.name, ty.make_arg("p"), ku.keyvar.type.make_arg(ku.keyvar.name))) if ty.json_gen_fn is not None: f.write("%schar *%s_to_json(libxl_ctx *ctx, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p"))) if ty.json_parse_fn is not None: f.write("%sint %s_from_json(libxl_ctx *ctx, %s, const char *s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))) if isinstance(ty, idl.Enumeration): f.write("%sconst char *%s_to_string(%s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p"))) f.write("%sint %s_from_string(const char *s, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("e", passby=idl.PASS_BY_REFERENCE))) f.write("%sextern libxl_enum_string_table %s_string_table[];\n" % (ty.hidden(), ty.typename)) f.write("\n") f.write("""#endif /* %s */\n""" % (header_define)) f.close() print "outputting libxl JSON definitions to %s" % header_json f = open(header_json, "w") header_json_define = header_json.upper().replace('.','_') f.write("""#ifndef %s #define %s /* * DO NOT EDIT. * * This file is autogenerated by * "%s" */ """ % (header_json_define, header_json_define, " ".join(sys.argv))) for ty in [ty for ty in types if ty.json_gen_fn is not None]: f.write("%syajl_gen_status %s_gen_json(yajl_gen hand, %s);\n" % (ty.hidden(), ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))) f.write("\n") f.write("""#endif /* %s */\n""" % header_json_define) f.close() print "outputting libxl type internal definitions to %s" % header_private f = open(header_private, "w") header_private_define = header_private.upper().replace('.','_') f.write("""#ifndef %s #define %s /* * DO NOT EDIT. * * This file is autogenerated by * "%s" */ """ % (header_private_define, header_private_define, " ".join(sys.argv))) for ty in [ty for ty in types if ty.json_parse_fn is not None]: f.write("%sint %s_parse_json(libxl__gc *gc, const libxl__json_object *o, %s);\n" % \ (ty.hidden(), ty.namespace + "_" + ty.rawname, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))) f.write("\n") f.write("""#endif /* %s */\n""" % header_json_define) f.close() print "outputting libxl type implementations to %s" % impl f = open(impl, "w") f.write(""" /* DO NOT EDIT. * * This file is autogenerated by * "%s" */ #include "libxl_osdeps.h" #include #include #include #include "libxl_internal.h" """ % " ".join(sys.argv)) for ty in [t for t in types if t.dispose_fn is not None and t.autogenerate_dispose_fn]: f.write("void %s(%s)\n" % (ty.dispose_fn, ty.make_arg("p"))) f.write("{\n") f.write(" if (!p) return;\n") f.write(libxl_C_type_dispose(ty, "p")) f.write(" memset(p, 0, sizeof(*p));\n") f.write("}\n") f.write("\n") for ty in [t for t in types if t.copy_fn and t.autogenerate_copy_fn]: f.write("void %s(libxl_ctx *ctx, %s, const %s)\n" % (ty.copy_fn, ty.make_arg("dst", passby=idl.PASS_BY_REFERENCE), ty.make_arg("src", passby=idl.PASS_BY_REFERENCE))) f.write("{\n") f.write(libxl_C_type_copy(ty, "dst", "src")) f.write("}\n") f.write("\n") for ty in [t for t in types if t.init_fn is not None and t.autogenerate_init_fn]: f.write(libxl_C_type_init(ty)) for field in libxl_init_members(ty): f.write(libxl_C_type_member_init(ty, field)) for ty in [t for t in types if isinstance(t,idl.Enumeration)]: f.write("const char *%s_to_string(%s e)\n" % (ty.typename, ty.typename)) f.write("{\n") f.write(libxl_C_enum_to_string(ty, "e")) f.write("}\n") f.write("\n") f.write(libxl_C_enum_strings(ty)) f.write("int %s_from_string(const char *s, %s *e)\n" % (ty.typename, ty.typename)) f.write("{\n") f.write(libxl_C_enum_from_string(ty, "s", "e")) f.write("}\n") f.write("\n") for ty in [t for t in types if t.json_gen_fn is not None]: f.write("yajl_gen_status %s_gen_json(yajl_gen hand, %s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))) f.write("{\n") f.write(libxl_C_type_gen_json(ty, "p")) f.write("}\n") f.write("\n") f.write("char *%s_to_json(libxl_ctx *ctx, %s)\n" % (ty.typename, ty.make_arg("p"))) f.write("{\n") f.write(libxl_C_type_to_json(ty, "p")) f.write("}\n") f.write("\n") for ty in [t for t in types if t.json_parse_fn is not None]: f.write("int %s_parse_json(libxl__gc *gc, const libxl__json_object *%s, %s)\n" % \ (ty.namespace + "_" + ty.rawname,"o",ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))) f.write("{\n") f.write(libxl_C_type_parse_json(ty, "o", "p")) f.write("}\n") f.write("\n") f.write("int %s_from_json(libxl_ctx *ctx, %s, const char *s)\n" % (ty.typename, ty.make_arg("p", passby=idl.PASS_BY_REFERENCE))) f.write("{\n") if not isinstance(ty, idl.Enumeration): f.write(" %s_init(p);\n" % ty.typename) f.write(libxl_C_type_from_json(ty, "p", "s")) f.write("}\n") f.write("\n") f.close() xen-4.9.2/tools/libxl/libxl_blktap2.c0000664000175000017500000000442313256712137015627 0ustar smbsmb/* * Copyright (C) 2010 Advanced Micro Devices * Author Christoph Egger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" #include "tap-ctl.h" int libxl__blktap_enabled(libxl__gc *gc) { const char *msg; return !tap_ctl_check(&msg); } char *libxl__blktap_devpath(libxl__gc *gc, const char *disk, libxl_disk_format format) { const char *type; char *params, *devname = NULL; tap_list_t tap; int err; type = libxl__device_disk_string_of_format(format); err = tap_ctl_find(type, disk, &tap); if (err == 0) { devname = GCSPRINTF("/dev/xen/blktap-2/tapdev%d", tap.minor); if (devname) return devname; } params = GCSPRINTF("%s:%s", type, disk); err = tap_ctl_create(params, &devname); if (!err) { libxl__ptr_add(gc, devname); return devname; } free(devname); return NULL; } int libxl__device_destroy_tapdisk(libxl__gc *gc, const char *params) { char *type, *disk; int err; tap_list_t tap; type = libxl__strdup(gc, params); disk = strchr(type, ':'); if (!disk) { LOG(ERROR, "Unable to parse params %s", params); return ERROR_INVAL; } *disk++ = '\0'; err = tap_ctl_find(type, disk, &tap); if (err < 0) { /* returns -errno */ LOGEV(ERROR, -err, "Unable to find type %s disk %s", type, disk); return ERROR_FAIL; } err = tap_ctl_destroy(tap.id, tap.minor); if (err < 0) { LOGEV(ERROR, -err, "Failed to destroy tap device id %d minor %d", tap.id, tap.minor); return ERROR_FAIL; } return 0; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/idl.txt0000664000175000017500000001543613256712137014251 0ustar smbsmblibxl IDL --------- Each type in the libxl interface is represented by an object of type idl.Type (or a subclass thereof). Every local variable defined by the .idl file must be an instance of idl.Type (e.g. you may not define Python functions or any other construct other than defining variables) The name of the type must be passed as the first argument to the constructor when defining a new type. The name given should not contain the initial namespace element (e.g. "libxl_"). See below for how to specify a namespace. The Type.typename contains the C name of the type _including_ the namespace element while Type.rawname is always set to the 'base' name of the type. The idl.Type base class has several other properties which apply to all types. The properties are set by passing a named parameter to the constructor. Type.namespace: (default: "libxl_") The namespace in which the type resides. Usually this is "libxl_" but system defined and builtin types may differ. If the typename is not None then the namespace is prepended to the type. Type.passby: (default: idl.PASS_BY_VALUE) Defines the manner in which a type should be passed to C functions. Valid values for this fields are: idl.PASS_BY_VALUE idl.PASS_BY_REFERENCE Type.dispose_fn: (default: typename + "_dispose" or None if type == None) The name of the C function which will free all dynamically allocated memory contained within this type (but not the type itself). Type.autogenerate_dispose_fn: (default: True) Indicates if the above named Type.dispose_fn should be autogenerated. Type.copy_fn: (default: typename + "_copy" or None if type == None) The name of the C function which will deep copy all fields within this type. Type.autogenerate_copy_fn: (default: True) Indicates if the above named Type.copy_fn should be autogenerated. Type.autogenerate_copy_fn Type.init_val: (default: None) C expression for the value to initialise instances of this type to. If present takes precendence over init_fn (see below). Type.init_fn: (default: typename + "_init" if dir in [IN, BOTH] and type != None) The name of the C function which will initialist Type. Type.autogenerate_init_fn: (default: True if dir in [IN, BOTH]) Indicates if the above named Type.init_fn should be autogenerated. Type.json_gen_fn: (default: typename + "_gen_json" or None if type == None) The name of the C function which will generate a YAJL data structure representing this type. Type.json_parse_fn: (default: typename + "_parse_json" or None if type == None) The name of the C function which will parse a libxl JSON structure representing this type to C type. Type.autogenerate_json: (default: True) Indicates if the above named Type.json_*_fn should be autogenerated. Type.check_default_fn: If it's set then calling this function shall return true if this type has been set to default value (internal libxl implementation). If this is not set, that means we can check the type against init_val (if it has one) or zero to determine whether the value is default value. Other simple type-Classes ------------------------- idl.Builtin Instances of this class represent types which are predefined within the system. idl.UInt Instances of this class represent the standard uint_t types. The for a given instance must be passed to the constructor and is then available in UInt.width Complex type-Classes -------------------- idl.Enumeration A class representing an enumeration (named integer values). This class has one property besides the ones defined for the Type class: Enumeration.value_namespace: (default: namespace) The namespace in which the values of the Enumeration (see below) reside. This prefix is prepended to the name of the value. The values are available in the list Enumeration.values. Each element in the list is of type idl.EnumerationValue. Each EnumerationValue has the following properties: EnumerationValue.enum Reference to containing Enumeration EnumerationValue.name The C name of this value, including the namespace and typename of the containing Enumeration (e.g. "LIBXL_FOOENUM_VALUE") EnumerationValue.rawname The C name of this value, excluding the namespace but including the typename of the containing Enumeration (e.g. "FOOENUM_VALUE") EnumerationValue.valuename The name of this value, excluding the name of the containing Enumeration and any namespace (e.g. "VALUE") EnumerationValue.value The integer value associated with this name. idl.Aggregate Base class for type-Classes which contain a number of other types (e.g. structs and unions). The contained types are available in the list Aggregate.fields. Each element in the list is of type idl.Field representing a member of the aggregate. Each field has the following properties: Field.type The type of the member (a idl.Type). Field.name The name of the member (can be None for anonymous fields). Field.const Boolean, true if the member is const. Field.init_val The initialisation value for this field. Takes precendence over both Field.type.init_val and Field.type.init_fn. idl.Struct A subclass of idl.Aggregate representing the C struct type. Struct.kind == "struct" idl.Union A subclass of idl.Aggregate representing the C union type. Union.kind == "union" idl.KeyedUnion A subclass of idl.Aggregate which represents the C union type where the currently valid member of the union can be determined based upon another member in the containing type. An idl.KeyedUnion must always be a member of a containing idl.Aggregate type. The KeyedUnion.keyvar contains an idl.Field, this is the member of the containing type which determines the valid member of the union. The idl.Field.type of the keyvar must be an Enumeration type. idl.Array A class representing an array of similar elements. An idl.Array must always be an idl.Field of a containing idl.Aggregate. idl.Array.elem_type contains an idl.Type which is the type of each element of the array. idl.Array.len_var contains an idl.Field which is added to the parent idl.Aggregate and will contain the length of the array. The field MUST be named num_ARRAYNAME. Standard Types -------------- Several standard types a predefined. They are void (void pointer type) bool size_t integer 24 bit signed integer. uint{8,16,32,64} uint{8,16,32,64}_t domid Domain ID string NULL terminated string xen-4.9.2/tools/libxl/libxl_xshelp.c0000664000175000017500000002003313256712137015566 0ustar smbsmb/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" /* must come before any other headers */ #include "libxl_internal.h" char **libxl__xs_kvs_of_flexarray(libxl__gc *gc, flexarray_t *array) { char **kvs; int i, length; if (!array) return NULL; length = array->count; if (!length) return NULL; kvs = libxl__calloc(gc, length + 2, sizeof(char *)); if (kvs) { for (i = 0; i < length; i += 2) { void *ptr; flexarray_get(array, i, &ptr); kvs[i] = (char *) ptr; flexarray_get(array, i + 1, &ptr); kvs[i + 1] = (char *) ptr; } kvs[i] = NULL; kvs[i + 1] = NULL; } return kvs; } int libxl__xs_writev_perms(libxl__gc *gc, xs_transaction_t t, const char *dir, char *kvs[], struct xs_permissions *perms, unsigned int num_perms) { libxl_ctx *ctx = libxl__gc_owner(gc); char *path; int i; if (!kvs) return 0; for (i = 0; kvs[i] != NULL; i += 2) { path = GCSPRINTF("%s/%s", dir, kvs[i]); if (path && kvs[i + 1]) { int length = strlen(kvs[i + 1]); xs_write(ctx->xsh, t, path, kvs[i + 1], length); if (perms) xs_set_permissions(ctx->xsh, t, path, perms, num_perms); } } return 0; } int libxl__xs_writev(libxl__gc *gc, xs_transaction_t t, const char *dir, char *kvs[]) { return libxl__xs_writev_perms(gc, t, dir, kvs, NULL, 0); } int libxl__xs_writev_atonce(libxl__gc *gc, const char *dir, char *kvs[]) { int rc; xs_transaction_t t = XBT_NULL; for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; rc = libxl__xs_writev(gc, t, dir, kvs); if (rc) goto out; rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc<0) goto out; } out: libxl__xs_transaction_abort(gc, &t); return rc; } int libxl__xs_vprintf(libxl__gc *gc, xs_transaction_t t, const char *path, const char *fmt, va_list ap) { libxl_ctx *ctx = libxl__gc_owner(gc); char *s; bool ok; s = libxl__vsprintf(gc, fmt, ap); ok = xs_write(ctx->xsh, t, path, s, strlen(s)); if (!ok) { LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, s); return ERROR_FAIL; } return 0; } int libxl__xs_printf(libxl__gc *gc, xs_transaction_t t, const char *path, const char *fmt, ...) { va_list ap; int rc; va_start(ap, fmt); rc = libxl__xs_vprintf(gc, t, path, fmt, ap); va_end(ap); return rc; } char * libxl__xs_read(libxl__gc *gc, xs_transaction_t t, const char *path) { libxl_ctx *ctx = libxl__gc_owner(gc); char *ptr; ptr = xs_read(ctx->xsh, t, path, NULL); libxl__ptr_add(gc, ptr); return ptr; } char *libxl__xs_get_dompath(libxl__gc *gc, uint32_t domid) { libxl_ctx *ctx = libxl__gc_owner(gc); char *s = xs_get_domain_path(ctx->xsh, domid); if (!s) { LOGED(ERROR, domid, "Failed to get dompath"); return NULL; } libxl__ptr_add(gc, s); return s; } char **libxl__xs_directory(libxl__gc *gc, xs_transaction_t t, const char *path, unsigned int *nb) { libxl_ctx *ctx = libxl__gc_owner(gc); char **ret = NULL; ret = xs_directory(ctx->xsh, t, path, nb); libxl__ptr_add(gc, ret); return ret; } int libxl__xs_mknod(libxl__gc *gc, xs_transaction_t t, const char *path, struct xs_permissions *perms, unsigned int num_perms) { libxl_ctx *ctx = libxl__gc_owner(gc); bool ok; ok = xs_write(ctx->xsh, t, path, "", 0); if (!ok) { LOGE(ERROR, "xenstore write failed: `%s' = ''", path); return ERROR_FAIL; } ok = xs_set_permissions(ctx->xsh, t, path, perms, num_perms); if (!ok) { LOGE(ERROR, "xenstore set permissions failed on `%s'", path); return ERROR_FAIL; } return 0; } char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t domid) { char *s = GCSPRINTF("/libxl/%i", domid); if (!s) LOGD(ERROR, domid, "cannot allocate create paths"); return s; } int libxl__xs_read_mandatory(libxl__gc *gc, xs_transaction_t t, const char *path, const char **result_out) { char *result = libxl__xs_read(gc, t, path); if (!result) { LOGE(ERROR, "xenstore read failed: `%s'", path); return ERROR_FAIL; } *result_out = result; return 0; } int libxl__xs_read_checked(libxl__gc *gc, xs_transaction_t t, const char *path, const char **result_out) { char *result = libxl__xs_read(gc, t, path); if (!result) { if (errno != ENOENT) { LOGE(ERROR, "xenstore read failed: `%s'", path); return ERROR_FAIL; } } *result_out = result; return 0; } int libxl__xs_write_checked(libxl__gc *gc, xs_transaction_t t, const char *path, const char *string) { size_t length = strlen(string); if (!xs_write(CTX->xsh, t, path, string, length)) { LOGE(ERROR, "xenstore write failed: `%s' = `%s'", path, string); return ERROR_FAIL; } return 0; } int libxl__xs_rm_checked(libxl__gc *gc, xs_transaction_t t, const char *path) { if (!xs_rm(CTX->xsh, t, path)) { if (errno == ENOENT) return 0; LOGE(ERROR, "xenstore rm failed: `%s'", path); return ERROR_FAIL; } return 0; } int libxl__xs_transaction_start(libxl__gc *gc, xs_transaction_t *t) { assert(!*t); *t = xs_transaction_start(CTX->xsh); if (!*t) { LOGE(ERROR, "could not create xenstore transaction"); return ERROR_FAIL; } return 0; } int libxl__xs_transaction_commit(libxl__gc *gc, xs_transaction_t *t) { assert(*t); if (!xs_transaction_end(CTX->xsh, *t, 0)) { *t = 0; if (errno == EAGAIN) return +1; LOGE(ERROR, "could not commit xenstore transaction"); return ERROR_FAIL; } *t = 0; return 0; } void libxl__xs_transaction_abort(libxl__gc *gc, xs_transaction_t *t) { if (!*t) return; if (!xs_transaction_end(CTX->xsh, *t, 1)) LOGE(ERROR, "could not abort xenstore transaction"); *t = 0; } int libxl__xs_path_cleanup(libxl__gc *gc, xs_transaction_t t, const char *user_path) { unsigned int nb = 0; char *path, *last, *val; int rc; /* A path and transaction must be provided by the caller */ assert(user_path && t); path = libxl__strdup(gc, user_path); if (!xs_rm(CTX->xsh, t, path)) { if (errno != ENOENT) LOGE(DEBUG, "unable to remove path %s", path); rc = ERROR_FAIL; goto out; } for (last = strrchr(path, '/'); last != NULL; last = strrchr(path, '/')) { *last = '\0'; if (!strlen(path)) break; val = libxl__xs_read(gc, t, path); if (!val || strlen(val) != 0) break; if (!libxl__xs_directory(gc, t, path, &nb) || nb != 0) break; if (!xs_rm(CTX->xsh, t, path)) { if (errno != ENOENT) LOGE(DEBUG, "unable to remove path %s", path); rc = ERROR_FAIL; goto out; } } rc = 0; out: return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/libxl/libxl_types_internal.idl0000664000175000017500000000235113256712137017654 0ustar smbsmbnamespace("libxl__") hidden(True) libxl_domid = Builtin("domid", namespace="libxl_", json_gen_fn = "yajl_gen_integer", json_parse_fn = "libxl__uint32_parse_json", json_parse_type = "JSON_INTEGER", autogenerate_json = False, copy_fn = None) libxl__qmp_message_type = Enumeration("qmp_message_type", [ (1, "QMP"), (2, "return"), (3, "error"), (4, "event"), (5, "invalid"), ]) libxl__device_kind = Enumeration("device_kind", [ (0, "NONE"), (1, "VIF"), (2, "VBD"), (3, "QDISK"), (4, "PCI"), (5, "VFB"), (6, "VKBD"), (7, "CONSOLE"), (8, "VTPM"), (9, "VUSB"), (10, "QUSB"), (11, "9PFS"), ]) libxl__console_backend = Enumeration("console_backend", [ (1, "XENCONSOLED"), (2, "IOEMU"), ]) libxl__device_console = Struct("device_console", [ ("backend_domid", libxl_domid), ("devid", integer), ("consback", libxl__console_backend), ("output", string), # A regular console has no name. # A console with a name is called a 'channel', see docs/misc/channels.txt ("name", string), ("connection", string), ("path", string), ]) libxl__device_action = Enumeration("device_action", [ (1, "ADD"), (2, "REMOVE"), ]) xen-4.9.2/tools/configure.ac0000664000175000017500000003454513256712137014116 0ustar smbsmb# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.67]) AC_INIT([Xen Hypervisor Tools], m4_esyscmd([../version.sh ../xen/Makefile]), [xen-devel@lists.xen.org], [xen], [http://www.xen.org/]) AC_CONFIG_SRCDIR([libxl/libxl.c]) AC_CONFIG_FILES([ ../config/Tools.mk hotplug/FreeBSD/rc.d/xencommons hotplug/FreeBSD/rc.d/xendriverdomain hotplug/Linux/init.d/sysconfig.xencommons hotplug/Linux/init.d/sysconfig.xendomains hotplug/Linux/init.d/xen-watchdog hotplug/Linux/init.d/xencommons hotplug/Linux/init.d/xendomains hotplug/Linux/init.d/xendriverdomain hotplug/Linux/launch-xenstore hotplug/Linux/vif-setup hotplug/Linux/xen-hotplug-common.sh hotplug/Linux/xendomains hotplug/NetBSD/rc.d/xencommons hotplug/NetBSD/rc.d/xendriverdomain ocaml/xenstored/oxenstored.conf ]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_AUX_DIR([../]) # Check if CFLAGS, LDFLAGS, LIBS, CPPFLAGS or CPP is set and print a warning AS_IF([test -n "$CC$CFLAGS$LDFLAGS$LIBS$CPPFLAGS$CPP"], [ AC_MSG_WARN( [Setting CC, CFLAGS, LDFLAGS, LIBS, CPPFLAGS or CPP is not \ recommended, use PREPEND_INCLUDES, PREPEND_LIB, \ APPEND_INCLUDES and APPEND_LIB instead when possible.]) ]) AC_CANONICAL_HOST case $host_vendor in rumpxen|rumprun) CONFIG_RUMP=y; rump=true ;; *) CONFIG_RUMP=n; rump=false ;; esac AC_SUBST(CONFIG_RUMP) AC_SYS_LARGEFILE case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits ;; esac AC_SUBST(FILE_OFFSET_BITS) # M4 Macro includes m4_include([../m4/savevar.m4]) m4_include([../m4/features.m4]) m4_include([../m4/path_or_fail.m4]) m4_include([../m4/checkpolicy.m4]) m4_include([../m4/set_cflags_ldflags.m4]) m4_include([../m4/python_version.m4]) m4_include([../m4/python_devel.m4]) m4_include([../m4/python_fortify_noopt.m4]) m4_include([../m4/ocaml.m4]) m4_include([../m4/uuid.m4]) m4_include([../m4/pkg.m4]) m4_include([../m4/curses.m4]) m4_include([../m4/pthread.m4]) m4_include([../m4/ptyfuncs.m4]) m4_include([../m4/extfs.m4]) m4_include([../m4/fetcher.m4]) m4_include([../m4/ax_compare_version.m4]) m4_include([../m4/paths.m4]) m4_include([../m4/systemd.m4]) AX_XEN_EXPAND_CONFIG() # Enable/disable options AX_ARG_DEFAULT_DISABLE([rpath], [Build tools with -Wl,-rpath,LIBDIR]) AX_ARG_DEFAULT_DISABLE([githttp], [Download GIT repositories via HTTP]) AX_ARG_DEFAULT_ENABLE([monitors], [Disable xenstat and xentop monitoring tools]) AX_ARG_DEFAULT_ENABLE([ocamltools], [Disable Ocaml tools]) AX_ARG_DEFAULT_ENABLE([xsmpolicy], [Disable XSM policy compilation]) AX_ARG_DEFAULT_DISABLE([ovmf], [Enable OVMF]) AX_ARG_DEFAULT_ENABLE([seabios], [Disable SeaBIOS]) AC_ARG_WITH([linux-backend-modules], AS_HELP_STRING([--with-linux-backend-modules="mod1 mod2"], [List of Linux backend module or modalias names to be autoloaded on startup.]), [LINUX_BACKEND_MODULES="$withval"], [case "$host_os" in *linux*) LINUX_BACKEND_MODULES=" xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback evtchn gntdev netbk blkbk xen-scsibk usbbk pciback xen-acpi-processor blktap2 " ;; *) LINUX_BACKEND_MODULES= ;; esac]) LINUX_BACKEND_MODULES="`eval echo $LINUX_BACKEND_MODULES`" AC_SUBST(LINUX_BACKEND_MODULES) dnl Enable blktap2 on Linux only. AC_ARG_ENABLE([blktap2], AS_HELP_STRING([--enable-blktap2], [Enable blktap2, (DEFAULT is on for Linux, otherwise off)]),,[ case "$host_os" in linux*) enable_blktap2="yes";; *) enable_blktap2="no";; esac ]) AS_IF([test "x$enable_blktap2" = "xyes"], [ AC_DEFINE([HAVE_BLKTAP2], [1], [Blktap2 enabled]) blktap2=y],[ blktap2=n ]) AC_SUBST(blktap2) AC_ARG_ENABLE([qemu-traditional], AS_HELP_STRING([--enable-qemu-traditional], [Enable qemu traditional device model, (DEFAULT is on for Linux or NetBSD x86, otherwise off)]),,[ case "$host_cpu" in i[[3456]]86|x86_64) enable_qemu_traditional="yes";; *) enable_qemu_traditional="no";; esac case "$host_os" in freebsd*) enable_qemu_traditional="no";; esac ]) AS_IF([test "x$enable_qemu_traditional" = "xyes"], [ AC_DEFINE([HAVE_QEMU_TRADITIONAL], [1], [Qemu traditional enabled]) qemu_traditional=y],[ qemu_traditional=n ]) AC_SUBST(qemu_traditional) AC_ARG_ENABLE([rombios], AS_HELP_STRING([--enable-rombios], [Enable ROMBIOS, (DEFAULT is on if qemu-traditional is enabled, otherwise off)]),,[ AS_IF([test "x$enable_qemu_traditional" = "xyes"], [ enable_rombios="yes" ], [ enable_rombios="no" ]) ]) AS_IF([test "x$enable_rombios" = "xyes"], [ dnl as86, ld86, and bcc are only required when building rombios. They dnl are only needed when the host system is x86 but that check is done dnl for us above when checking if we should build with qemu-traditional. AX_PATH_PROG_OR_FAIL([AS86], [as86]) AX_PATH_PROG_OR_FAIL([LD86], [ld86]) AX_PATH_PROG_OR_FAIL([BCC], [bcc]) AC_CHECK_LIB([lzma], [lzma_version_number], [], [AC_MSG_ERROR([Could not find lzma, needed to build rombios])]) AC_DEFINE([HAVE_ROMBIOS], [1], [ROMBIOS enabled]) rombios=y],[ rombios=n ]) AC_SUBST(rombios) AC_ARG_WITH([system-qemu], AS_HELP_STRING([--with-system-qemu@<:@=PATH@:>@], [Use system supplied qemu PATH or qemu (taken from $PATH) as qemu-xen device model instead of building and installing our own version]),[ case $withval in yes) qemu_xen=n ; qemu_xen_path="qemu-system-i386" qemu_xen_systemd="/usr/bin/env $qemu_xen_path" ;; no) qemu_xen=y ;; *) qemu_xen=n ; qemu_xen_path="$withval" ; qemu_xen_systemd="$qemu_xen_path" ;; esac ],[ case "$host_cpu" in i[[3456]]86|x86_64) qemu_xen=y;; arm*|aarch64) qemu_xen=y;; *) qemu_xen=n;; esac ]) AS_IF([test "x$qemu_xen" = "xy"], [ qemu_xen_path="$LIBEXEC_BIN/qemu-system-i386" qemu_xen_systemd="$qemu_xen_path" ]) AC_DEFINE_UNQUOTED([QEMU_XEN_PATH], ["$qemu_xen_path"], [Qemu Xen path]) AC_SUBST(qemu_xen) AC_SUBST(qemu_xen_path) AC_SUBST(qemu_xen_systemd) AC_ARG_WITH([system-seabios], AS_HELP_STRING([--with-system-seabios@<:@=PATH@:>@], [Use system supplied seabios PATH instead of building and installing our own version]),[ # Disable compilation of SeaBIOS. seabios=n case $withval in no) seabios_path= ;; *) seabios_path=$withval ;; esac ],[]) AS_IF([test "x$seabios" = "xy" -o -n "$seabios_path" ], [ AC_DEFINE_UNQUOTED([SEABIOS_PATH], ["${seabios_path:-$XENFIRMWAREDIR/seabios.bin}"], [SeaBIOS path]) ]) AC_ARG_WITH([system-ovmf], AS_HELP_STRING([--with-system-ovmf@<:@=PATH@:>@], [Use system supplied OVMF PATH instead of building and installing our own version]),[ # Disable compilation of OVMF. ovmf=n case $withval in no) ovmf_path= ;; *) ovmf_path=$withval ;; esac ],[]) AS_IF([test "x$ovmf" = "xy" -o -n "$ovmf_path" ], [ AC_DEFINE_UNQUOTED([OVMF_PATH], ["${ovmf_path:-$XENFIRMWAREDIR/ovmf.bin}"], [OVMF path]) ]) AC_ARG_WITH([extra-qemuu-configure-args], AS_HELP_STRING([--with-extra-qemuu-configure-args@<:@="--ARG1 ..."@:>@], [List of additional configure options for upstream qemu]),[ case $withval in no) EXTRA_QEMUU_CONFIGURE_ARGS= ;; *) EXTRA_QEMUU_CONFIGURE_ARGS=$withval ;; esac ],[]) AC_SUBST(EXTRA_QEMUU_CONFIGURE_ARGS) AC_ARG_VAR([PREPEND_INCLUDES], [List of include folders to prepend to CFLAGS (without -I)]) AC_ARG_VAR([PREPEND_LIB], [List of library folders to prepend to LDFLAGS (without -L)]) AC_ARG_VAR([APPEND_INCLUDES], [List of include folders to append to CFLAGS (without -I)]) AC_ARG_VAR([APPEND_LIB], [List of library folders to append to LDFLAGS (without -L)]) AX_SET_FLAGS AC_ARG_VAR([PYTHON], [Path to the Python parser]) AC_ARG_VAR([PERL], [Path to Perl parser]) AC_ARG_VAR([BISON], [Path to Bison parser generator]) AC_ARG_VAR([FLEX], [Path to Flex lexical analyser generator]) AC_ARG_VAR([CURL], [Path to curl-config tool]) AC_ARG_VAR([XML], [Path to xml2-config tool]) AC_ARG_VAR([BASH], [Path to bash shell]) AC_ARG_VAR([XGETTEXT], [Path to xgetttext tool]) AC_ARG_VAR([AS86], [Path to as86 tool]) AC_ARG_VAR([LD86], [Path to ld86 tool]) AC_ARG_VAR([BCC], [Path to bcc tool]) AC_ARG_VAR([IASL], [Path to iasl tool]) AC_ARG_VAR([AWK], [Path to awk tool]) # Checks for programs. AC_PROG_CC AC_PROG_MAKE_SET AC_PROG_INSTALL AC_PATH_PROG([BISON], [bison]) AC_PATH_PROG([FLEX], [flex]) AX_PATH_PROG_OR_FAIL([PERL], [perl]) AX_PATH_PROG_OR_FAIL([AWK], [awk]) AC_PROG_OCAML AC_PROG_FINDLIB AS_IF([test "x$ocamltools" = "xy"], [ AS_IF([test "x$OCAMLOPT" = "xno" || test "x$OCAMLFIND" = "xno"], [ AS_IF([test "x$enable_ocamltools" = "xyes"], [ AC_MSG_ERROR([Ocaml tools enabled, but missing ocamlopt or ocamlfind])]) ocamltools="n" ], [ AX_COMPARE_VERSION([$OCAMLVERSION], [lt], [3.09.3], [ AS_IF([test "x$enable_ocamltools" = "xyes"], [ AC_MSG_ERROR([Your version of OCaml: $OCAMLVERSION is not supported])]) ocamltools="n" ]) ]) ]) m4_include([../m4/xenstored.m4]) AX_XENSTORE_OPTIONS AX_XENSTORE_SET AS_IF([test "x$xsmpolicy" = "xy"], [ AC_PROG_CHECKPOLICY AS_IF([test "x$CHECKPOLICY" = "xno"], [ AS_IF([test "x$enable_xsmpolicy" = "xyes"], [ AC_MSG_ERROR([XSM policy compilation enabled, but unable to find checkpolicy])]) xsmpolicy="n" ]) ]) dnl FreeBSD doesn't require bash (hotplug scripts are in plain sh) case "$host_os" in freebsd*) ;; *) AX_PATH_PROG_OR_FAIL([BASH], [bash]);; esac AS_IF([echo "$PYTHON" | grep -q "^/"], [ PYTHONPATH=$PYTHON PYTHON=`basename $PYTHONPATH` ],[test -z "$PYTHON"], [PYTHON="python"], [AC_MSG_ERROR([PYTHON specified, but is not an absolute path])]) AX_PATH_PROG_OR_FAIL([PYTHONPATH], [$PYTHON]) AX_CHECK_PYTHON_VERSION([2], [3]) AS_IF([test "$cross_compiling" != yes], [ AX_CHECK_PYTHON_DEVEL() AX_CHECK_PYTHON_FORTIFY_NOOPT() ]) if ! $rump; then AX_PATH_PROG_OR_FAIL([XGETTEXT], [xgettext]) dnl as86, ld86, bcc and iasl are only required when the host system is x86*. dnl "host" here means the platform on which the hypervisor and tools is dnl going to run, not the platform on which we are building (known as dnl "build" in gnu speak). case "$host_cpu" in i[[3456]]86|x86_64|aarch64) AX_PATH_PROG_OR_FAIL([IASL], [iasl]) ;; esac AX_CHECK_UUID AX_CHECK_CURSES AS_IF([test "$ncurses" = "y"], [ AC_CHECK_LIB([tinfo], [define_key], [TINFO_LIBS=-ltinfo]) ]) AC_SUBST(TINFO_LIBS) dnl The following are only required when upstream QEMU is built AS_IF([test "x$qemu_xen" = "xy"], [ PKG_CHECK_MODULES(glib, [glib-2.0 >= 2.12]) PKG_CHECK_MODULES(pixman, [pixman-1 >= 0.21.8]) ]) AX_CHECK_FETCHER # Checks for libraries. AC_CHECK_HEADER([bzlib.h], [ AC_CHECK_LIB([bz2], [BZ2_bzDecompressInit], [zlib="$zlib -DHAVE_BZLIB -lbz2"]) ]) AC_CHECK_HEADER([lzma.h], [ AC_CHECK_LIB([lzma], [lzma_stream_decoder], [zlib="$zlib -DHAVE_LZMA -llzma"]) ]) AC_CHECK_HEADER([lzo/lzo1x.h], [ AC_CHECK_LIB([lzo2], [lzo1x_decompress], [zlib="$zlib -DHAVE_LZO1X -llzo2"]) ]) AC_SUBST(zlib) AS_IF([test "x$enable_blktap2" = "xyes"], [ AC_CHECK_LIB([aio], [io_setup], [], [AC_MSG_ERROR([Could not find libaio])]) ]) AC_SUBST(system_aio) AX_CHECK_EXTFS AC_CHECK_LIB([gcrypt], [gcry_md_hash_buffer], [libgcrypt="y"], [libgcrypt="n"]) AC_SUBST(libgcrypt) AX_CHECK_PTHREAD AX_CHECK_PTYFUNCS AC_CHECK_LIB([yajl], [yajl_alloc], [], [AC_MSG_ERROR([Could not find yajl])]) AC_CHECK_LIB([z], [deflateCopy], [], [AC_MSG_ERROR([Could not find zlib])]) AC_CHECK_LIB([iconv], [libiconv_open], [libiconv="y"], [libiconv="n"]) AC_SUBST(libiconv) AC_CHECK_HEADER([argp.h], [ AC_CHECK_LIB([argp], [argp_usage], [argp_ldflags="-largp"]) ], [AC_MSG_ERROR([Could not find argp])]) AC_SUBST(argp_ldflags) # FDT is needed only on ARM case "$host_cpu" in arm*|aarch64) AC_CHECK_LIB([fdt], [fdt_create], [], [AC_MSG_ERROR([Could not find libfdt])]) # Check for libfdt >= 1.4.0. If present enable passthrough # Note that libfdt doesn't provide versionning. So we need to rely on # function present in new version. # Use fdt_first_property_offset which has been correctly exported since v1.4.0 AC_CHECK_FUNC(fdt_first_property_offset, [partial_dt="y"], [partial_dt="n"]) AS_IF([test "x$partial_dt" = "xy" ], [AC_DEFINE([ENABLE_PARTIAL_DEVICE_TREE], [1], [Enabling support partial device tree in libxl])], [AC_MSG_WARN([Disabling support for partial device tree in libxl. Please install libfdt library - version 1.4.0 or higher])]) # The functions fdt_{first,next}_subnode may not be available because: # * It has been introduced in 2013 => Doesn't work on Wheezy # * The prototype exists but the functions are not exposed. Don't ask why... AC_CHECK_FUNCS([fdt_first_subnode fdt_next_subnode]) AC_CHECK_DECLS([fdt_first_subnode, fdt_next_subnode],,,[#include ]) # The helper fdt_property_u32 is only present in libfdt >= 1.4.0 # It's an inline function, so only check if the declaration is present AC_CHECK_DECLS([fdt_property_u32],,,[#include ]) esac # Checks for header files. AC_CHECK_HEADERS([yajl/yajl_version.h sys/eventfd.h valgrind/memcheck.h utmp.h]) # Check for libnl3 >=3.2.8. If present enable remus network buffering. PKG_CHECK_MODULES(LIBNL3, [libnl-3.0 >= 3.2.8 libnl-route-3.0 >= 3.2.8], [libnl3_lib="y"], [libnl3_lib="n"]) AS_IF([test "x$libnl3_lib" = "xn" ], [ AC_MSG_WARN([Disabling support for Remus network buffering and COLO. Please install libnl3 libraries (including libnl3-route), command line tools and devel headers - version 3.2.8 or higher]) AC_SUBST(libnl, [n]) ],[ AC_SUBST(libnl, [y]) ]) AC_SUBST(LIBNL3_LIBS) AC_SUBST(LIBNL3_CFLAGS) fi # ! $rump AX_AVAILABLE_SYSTEMD() AS_IF([test "x$systemd" = "xy"], [ AC_CONFIG_FILES([ hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/var-lib-xenstored.mount hotplug/Linux/systemd/xen-init-dom0.service hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service ]) ]) AC_OUTPUT() xen-4.9.2/tools/xl/0000775000175000017500000000000013256712137012240 5ustar smbsmbxen-4.9.2/tools/xl/bash-completion0000664000175000017500000000061413256712137015250 0ustar smbsmb# Copy this file to /etc/bash_completion.d/xl.sh _xl() { local IFS=$'\n,' local cur opts xl COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" xl=xl if [[ $COMP_CWORD == 1 ]] ; then opts=`${xl} help 2>/dev/null | sed '1,4d' | awk '/^ [^ ]/ {print $1}' | sed 's/$/ ,/g'` && COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi return 0 } complete -F _xl -o nospace -o default xl xen-4.9.2/tools/xl/xl_saverestore.c0000664000175000017500000001712313256712137015455 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" #ifndef LIBXL_HAVE_NO_SUSPEND_RESUME void save_domain_core_begin(uint32_t domid, const char *override_config_file, uint8_t **config_data_r, int *config_len_r) { int rc; libxl_domain_config d_config; char *config_c = 0; /* configuration file in optional data: */ libxl_domain_config_init(&d_config); if (override_config_file) { void *config_v = 0; rc = libxl_read_file_contents(ctx, override_config_file, &config_v, config_len_r); if (rc) { fprintf(stderr, "unable to read overridden config file\n"); exit(EXIT_FAILURE); } parse_config_data(override_config_file, config_v, *config_len_r, &d_config); free(config_v); } else { rc = libxl_retrieve_domain_configuration(ctx, domid, &d_config); if (rc) { fprintf(stderr, "unable to retrieve domain configuration\n"); exit(EXIT_FAILURE); } } config_c = libxl_domain_config_to_json(ctx, &d_config); if (!config_c) { fprintf(stderr, "unable to convert config file to JSON\n"); exit(EXIT_FAILURE); } *config_data_r = (uint8_t *)config_c; *config_len_r = strlen(config_c) + 1; /* including trailing '\0' */ libxl_domain_config_dispose(&d_config); } void save_domain_core_writeconfig(int fd, const char *source, const uint8_t *config_data, int config_len) { struct save_file_header hdr; uint8_t *optdata_begin; union { uint32_t u32; char b[4]; } u32buf; memset(&hdr, 0, sizeof(hdr)); memcpy(hdr.magic, savefileheader_magic, sizeof(hdr.magic)); hdr.byteorder = SAVEFILE_BYTEORDER_VALUE; hdr.mandatory_flags = XL_MANDATORY_FLAG_STREAMv2; optdata_begin= 0; #define ADD_OPTDATA(ptr, len) ({ \ if ((len)) { \ hdr.optional_data_len += (len); \ optdata_begin = xrealloc(optdata_begin, hdr.optional_data_len); \ memcpy(optdata_begin + hdr.optional_data_len - (len), \ (ptr), (len)); \ } \ }) u32buf.u32 = config_len; ADD_OPTDATA(u32buf.b, 4); ADD_OPTDATA(config_data, config_len); if (config_len) hdr.mandatory_flags |= XL_MANDATORY_FLAG_JSON; /* that's the optional data */ CHK_ERRNOVAL(libxl_write_exactly( ctx, fd, &hdr, sizeof(hdr), source, "header")); CHK_ERRNOVAL(libxl_write_exactly( ctx, fd, optdata_begin, hdr.optional_data_len, source, "header")); free(optdata_begin); fprintf(stderr, "Saving to %s new xl format (info" " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n", source, hdr.mandatory_flags, hdr.optional_flags, hdr.optional_data_len); } static int save_domain(uint32_t domid, const char *filename, int checkpoint, int leavepaused, const char *override_config_file) { int fd; uint8_t *config_data; int config_len; save_domain_core_begin(domid, override_config_file, &config_data, &config_len); if (!config_len) { fputs(" Savefile will not contain xl domain config\n", stderr); } fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (fd < 0) { fprintf(stderr, "Failed to open temp file %s for writing\n", filename); exit(EXIT_FAILURE); } save_domain_core_writeconfig(fd, filename, config_data, config_len); int rc = libxl_domain_suspend(ctx, domid, fd, 0, NULL); close(fd); if (rc < 0) { fprintf(stderr, "Failed to save domain, resuming domain\n"); libxl_domain_resume(ctx, domid, 1, 0); } else if (leavepaused || checkpoint) { if (leavepaused) libxl_domain_pause(ctx, domid); libxl_domain_resume(ctx, domid, 1, 0); } else libxl_domain_destroy(ctx, domid, 0); exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } int main_restore(int argc, char **argv) { const char *checkpoint_file = NULL; const char *config_file = NULL; struct domain_create dom_info; int paused = 0, debug = 0, daemonize = 1, monitor = 1, console_autoconnect = 0, vnc = 0, vncautopass = 0; int opt, rc; static struct option opts[] = { {"vncviewer", 0, 0, 'V'}, {"vncviewer-autopass", 0, 0, 'A'}, COMMON_LONG_OPTS }; SWITCH_FOREACH_OPT(opt, "FcpdeVA", opts, "restore", 1) { case 'c': console_autoconnect = 1; break; case 'p': paused = 1; break; case 'd': debug = 1; break; case 'F': daemonize = 0; break; case 'e': daemonize = 0; monitor = 0; break; case 'V': vnc = 1; break; case 'A': vnc = vncautopass = 1; break; } if (argc-optind == 1) { checkpoint_file = argv[optind]; } else if (argc-optind == 2) { config_file = argv[optind]; checkpoint_file = argv[optind + 1]; } else { help("restore"); return EXIT_FAILURE; } memset(&dom_info, 0, sizeof(dom_info)); dom_info.debug = debug; dom_info.daemonize = daemonize; dom_info.monitor = monitor; dom_info.paused = paused; dom_info.config_file = config_file; dom_info.restore_file = checkpoint_file; dom_info.migrate_fd = -1; dom_info.send_back_fd = -1; dom_info.vnc = vnc; dom_info.vncautopass = vncautopass; dom_info.console_autoconnect = console_autoconnect; rc = create_domain(&dom_info); if (rc < 0) return EXIT_FAILURE; return EXIT_SUCCESS; } int main_save(int argc, char **argv) { uint32_t domid; const char *filename; const char *config_filename = NULL; int checkpoint = 0; int leavepaused = 0; int opt; SWITCH_FOREACH_OPT(opt, "cp", NULL, "save", 2) { case 'c': checkpoint = 1; break; case 'p': leavepaused = 1; break; } if (argc-optind > 3) { help("save"); return EXIT_FAILURE; } domid = find_domain(argv[optind]); filename = argv[optind + 1]; if ( argc - optind >= 3 ) config_filename = argv[optind + 2]; save_domain(domid, filename, checkpoint, leavepaused, config_filename); return EXIT_SUCCESS; } #endif /* LIBXL_HAVE_NO_SUSPEND_RESUME */ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_cpupool.c0000664000175000017500000004116313256712137014575 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" int main_cpupoolcreate(int argc, char **argv) { const char *filename = NULL, *config_src=NULL; const char *p; char *extra_config = NULL; int opt; static struct option opts[] = { {"defconfig", 1, 0, 'f'}, {"dryrun", 0, 0, 'n'}, COMMON_LONG_OPTS }; int ret; char *config_data = 0; int config_len = 0; XLU_Config *config; const char *buf; char *name = NULL; uint32_t poolid; libxl_scheduler sched = 0; XLU_ConfigList *cpus; XLU_ConfigList *nodes; int n_cpus, n_nodes, i, n; libxl_bitmap freemap; libxl_bitmap cpumap; libxl_uuid uuid; libxl_cputopology *topology; int rc = EXIT_FAILURE; SWITCH_FOREACH_OPT(opt, "nf:", opts, "cpupool-create", 0) { case 'f': filename = optarg; break; case 'n': dryrun_only = 1; break; } libxl_bitmap_init(&freemap); libxl_bitmap_init(&cpumap); while (optind < argc) { if ((p = strchr(argv[optind], '='))) { string_realloc_append(&extra_config, "\n"); string_realloc_append(&extra_config, argv[optind]); } else if (!filename) { filename = argv[optind]; } else { help("cpupool-create"); goto out; } optind++; } if (filename) { if (libxl_read_file_contents(ctx, filename, (void **)&config_data, &config_len)) { fprintf(stderr, "Failed to read config file: %s: %s\n", filename, strerror(errno)); goto out; } config_src=filename; } else config_src="command line"; if (extra_config && strlen(extra_config)) { if (config_len > INT_MAX - (strlen(extra_config) + 2)) { fprintf(stderr, "Failed to attach extra configuration\n"); goto out; } config_data = xrealloc(config_data, config_len + strlen(extra_config) + 2); if (!config_data) { fprintf(stderr, "Failed to realloc config_data\n"); goto out; } config_data[config_len] = 0; strcat(config_data, extra_config); strcat(config_data, "\n"); config_len += strlen(extra_config) + 1; } config = xlu_cfg_init(stderr, config_src); if (!config) { fprintf(stderr, "Failed to allocate for configuration\n"); goto out; } ret = xlu_cfg_readdata(config, config_data, config_len); if (ret) { fprintf(stderr, "Failed to parse config file: %s\n", strerror(ret)); goto out_cfg; } if (!xlu_cfg_get_string (config, "name", &buf, 0)) name = strdup(buf); else if (filename) name = libxl_basename(filename); else { fprintf(stderr, "Missing cpupool name!\n"); goto out_cfg; } if (!libxl_name_to_cpupoolid(ctx, name, &poolid)) { fprintf(stderr, "Pool name \"%s\" already exists\n", name); goto out_cfg; } if (!xlu_cfg_get_string (config, "sched", &buf, 0)) { if ((libxl_scheduler_from_string(buf, &sched)) < 0) { fprintf(stderr, "Unknown scheduler\n"); goto out_cfg; } } else { rc = libxl_get_scheduler(ctx); if (rc < 0) { fprintf(stderr, "get_scheduler sysctl failed.\n"); goto out_cfg; } sched = rc; } if (libxl_get_freecpus(ctx, &freemap)) { fprintf(stderr, "libxl_get_freecpus failed\n"); goto out_cfg; } if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) { fprintf(stderr, "Failed to allocate cpumap\n"); goto out_cfg; } if (!xlu_cfg_get_list(config, "nodes", &nodes, 0, 0)) { int nr; n_cpus = 0; n_nodes = 0; topology = libxl_get_cpu_topology(ctx, &nr); if (topology == NULL) { fprintf(stderr, "libxl_get_topologyinfo failed\n"); goto out_cfg; } while ((buf = xlu_cfg_get_listitem(nodes, n_nodes)) != NULL) { n = atoi(buf); for (i = 0; i < nr; i++) { if ((topology[i].node == n) && libxl_bitmap_test(&freemap, i)) { libxl_bitmap_set(&cpumap, i); n_cpus++; } } n_nodes++; } libxl_cputopology_list_free(topology, nr); if (n_cpus == 0) { fprintf(stderr, "no free cpu found\n"); goto out_cfg; } } else if (!xlu_cfg_get_list(config, "cpus", &cpus, 0, 1)) { n_cpus = 0; while ((buf = xlu_cfg_get_listitem(cpus, n_cpus)) != NULL) { i = atoi(buf); if ((i < 0) || !libxl_bitmap_test(&freemap, i)) { fprintf(stderr, "cpu %d illegal or not free\n", i); goto out_cfg; } libxl_bitmap_set(&cpumap, i); n_cpus++; } } else if (!xlu_cfg_get_string(config, "cpus", &buf, 0)) { if (parse_cpurange(buf, &cpumap)) goto out_cfg; n_cpus = 0; libxl_for_each_set_bit(i, cpumap) { if (!libxl_bitmap_test(&freemap, i)) { fprintf(stderr, "cpu %d illegal or not free\n", i); goto out_cfg; } n_cpus++; } } else n_cpus = 0; libxl_uuid_generate(&uuid); printf("Using config file \"%s\"\n", config_src); printf("cpupool name: %s\n", name); printf("scheduler: %s\n", libxl_scheduler_to_string(sched)); printf("number of cpus: %d\n", n_cpus); if (!dryrun_only) { poolid = LIBXL_CPUPOOL_POOLID_ANY; if (libxl_cpupool_create(ctx, name, sched, cpumap, &uuid, &poolid)) { fprintf(stderr, "error on creating cpupool\n"); goto out_cfg; } } /* We made it! */ rc = EXIT_SUCCESS; out_cfg: xlu_cfg_destroy(config); out: libxl_bitmap_dispose(&freemap); libxl_bitmap_dispose(&cpumap); free(name); free(config_data); free(extra_config); return rc; } int main_cpupoollist(int argc, char **argv) { int opt; static struct option opts[] = { {"cpus", 0, 0, 'c'}, COMMON_LONG_OPTS }; int opt_cpus = 0; const char *pool = NULL; libxl_cpupoolinfo *poolinfo; int n_pools, p, c, n; uint32_t poolid; char *name; SWITCH_FOREACH_OPT(opt, "c", opts, "cpupool-list", 0) { case 'c': opt_cpus = 1; break; } if (optind < argc) { pool = argv[optind]; if (libxl_name_to_cpupoolid(ctx, pool, &poolid)) { fprintf(stderr, "Pool \'%s\' does not exist\n", pool); return EXIT_FAILURE; } } poolinfo = libxl_list_cpupool(ctx, &n_pools); if (!poolinfo) { fprintf(stderr, "error getting cpupool info\n"); return EXIT_FAILURE; } printf("%-19s", "Name"); if (opt_cpus) printf("CPU list\n"); else printf("CPUs Sched Active Domain count\n"); for (p = 0; p < n_pools; p++) { if (!pool || (poolinfo[p].poolid == poolid)) { name = poolinfo[p].pool_name; printf("%-19s", name); n = 0; libxl_for_each_bit(c, poolinfo[p].cpumap) if (libxl_bitmap_test(&poolinfo[p].cpumap, c)) { if (n && opt_cpus) printf(","); if (opt_cpus) printf("%d", c); n++; } if (!opt_cpus) { printf("%3d %9s y %4d", n, libxl_scheduler_to_string(poolinfo[p].sched), poolinfo[p].n_dom); } printf("\n"); } } libxl_cpupoolinfo_list_free(poolinfo, n_pools); return EXIT_SUCCESS; } int main_cpupooldestroy(int argc, char **argv) { int opt; const char *pool; uint32_t poolid; SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-destroy", 1) { /* No options */ } pool = argv[optind]; if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) || !libxl_cpupoolid_is_valid(ctx, poolid)) { fprintf(stderr, "unknown cpupool '%s'\n", pool); return EXIT_FAILURE; } if (libxl_cpupool_destroy(ctx, poolid)) { fprintf(stderr, "Can't destroy cpupool '%s'\n", pool); return EXIT_FAILURE; } return EXIT_SUCCESS; } int main_cpupoolrename(int argc, char **argv) { int opt; const char *pool; const char *new_name; uint32_t poolid; SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-rename", 2) { /* No options */ } pool = argv[optind++]; if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) || !libxl_cpupoolid_is_valid(ctx, poolid)) { fprintf(stderr, "unknown cpupool '%s'\n", pool); return EXIT_FAILURE; } new_name = argv[optind]; if (libxl_cpupool_rename(ctx, new_name, poolid)) { fprintf(stderr, "Can't rename cpupool '%s'\n", pool); return EXIT_FAILURE; } return EXIT_SUCCESS; } int main_cpupoolcpuadd(int argc, char **argv) { int opt; const char *pool; uint32_t poolid; libxl_bitmap cpumap; int rc = EXIT_FAILURE; SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-cpu-add", 2) { /* No options */ } libxl_bitmap_init(&cpumap); if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) { fprintf(stderr, "Unable to allocate cpumap"); return EXIT_FAILURE; } pool = argv[optind++]; if (parse_cpurange(argv[optind], &cpumap)) goto out; if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) || !libxl_cpupoolid_is_valid(ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", pool); goto out; } if (libxl_cpupool_cpuadd_cpumap(ctx, poolid, &cpumap)) fprintf(stderr, "some cpus may not have been added to %s\n", pool); rc = EXIT_SUCCESS; out: libxl_bitmap_dispose(&cpumap); return rc; } int main_cpupoolcpuremove(int argc, char **argv) { int opt; const char *pool; uint32_t poolid; libxl_bitmap cpumap; int rc = EXIT_FAILURE; libxl_bitmap_init(&cpumap); if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) { fprintf(stderr, "Unable to allocate cpumap"); return EXIT_FAILURE; } SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-cpu-remove", 2) { /* No options */ } pool = argv[optind++]; if (parse_cpurange(argv[optind], &cpumap)) goto out; if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) || !libxl_cpupoolid_is_valid(ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", pool); goto out; } if (libxl_cpupool_cpuremove_cpumap(ctx, poolid, &cpumap)) { fprintf(stderr, "Some cpus may have not or only partially been removed from '%s'.\n", pool); fprintf(stderr, "If a cpu can't be added to another cpupool, add it to '%s' again and retry.\n", pool); } rc = EXIT_SUCCESS; out: libxl_bitmap_dispose(&cpumap); return rc; } int main_cpupoolmigrate(int argc, char **argv) { int opt; const char *pool; uint32_t poolid; const char *dom; uint32_t domid; SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-migrate", 2) { /* No options */ } dom = argv[optind++]; pool = argv[optind]; if (libxl_domain_qualifier_to_domid(ctx, dom, &domid) || !libxl_domid_to_name(ctx, domid)) { fprintf(stderr, "unknown domain '%s'\n", dom); return EXIT_FAILURE; } if (libxl_cpupool_qualifier_to_cpupoolid(ctx, pool, &poolid, NULL) || !libxl_cpupoolid_is_valid(ctx, poolid)) { fprintf(stderr, "unknown cpupool '%s'\n", pool); return EXIT_FAILURE; } if (libxl_cpupool_movedomain(ctx, poolid, domid)) return EXIT_FAILURE; return EXIT_SUCCESS; } int main_cpupoolnumasplit(int argc, char **argv) { int rc; int opt; int p; int c; int n; uint32_t poolid; libxl_scheduler sched; int n_pools; int node; int n_cpus; char *name = NULL; libxl_uuid uuid; libxl_bitmap cpumap; libxl_cpupoolinfo *poolinfo; libxl_cputopology *topology; libxl_dominfo info; SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-numa-split", 0) { /* No options */ } libxl_dominfo_init(&info); rc = EXIT_FAILURE; libxl_bitmap_init(&cpumap); poolinfo = libxl_list_cpupool(ctx, &n_pools); if (!poolinfo) { fprintf(stderr, "error getting cpupool info\n"); return EXIT_FAILURE; } poolid = poolinfo[0].poolid; sched = poolinfo[0].sched; libxl_cpupoolinfo_list_free(poolinfo, n_pools); if (n_pools > 1) { fprintf(stderr, "splitting not possible, already cpupools in use\n"); return EXIT_FAILURE; } topology = libxl_get_cpu_topology(ctx, &n_cpus); if (topology == NULL) { fprintf(stderr, "libxl_get_topologyinfo failed\n"); return EXIT_FAILURE; } if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) { fprintf(stderr, "Failed to allocate cpumap\n"); goto out; } /* Reset Pool-0 to 1st node: first add cpus, then remove cpus to avoid a cpupool without cpus in between */ node = topology[0].node; if (libxl_cpupool_cpuadd_node(ctx, 0, node, &n)) { fprintf(stderr, "error on adding cpu to Pool 0\n"); goto out; } xasprintf(&name, "Pool-node%d", node); if (libxl_cpupool_rename(ctx, name, 0)) { fprintf(stderr, "error on renaming Pool 0\n"); goto out; } n = 0; for (c = 0; c < n_cpus; c++) { if (topology[c].node == node) { topology[c].node = LIBXL_CPUTOPOLOGY_INVALID_ENTRY; libxl_bitmap_set(&cpumap, n); n++; } } if (libxl_domain_info(ctx, &info, 0)) { fprintf(stderr, "error on getting info for Domain-0\n"); goto out; } if (info.vcpu_online > n && libxl_set_vcpuonline(ctx, 0, &cpumap)) { fprintf(stderr, "error on removing vcpus for Domain-0\n"); goto out; } for (c = 0; c < 10; c++) { /* We've called libxl_dominfo_init before the loop and will * call libxl_dominfo_dispose after the loop when we're done * with info. */ libxl_dominfo_dispose(&info); libxl_dominfo_init(&info); if (libxl_domain_info(ctx, &info, 0)) { fprintf(stderr, "error on getting info for Domain-0\n"); goto out; } if (info.vcpu_online <= n) { break; } sleep(1); } if (info.vcpu_online > n) { fprintf(stderr, "failed to offline vcpus\n"); goto out; } libxl_bitmap_set_none(&cpumap); for (c = 0; c < n_cpus; c++) { if (topology[c].node == LIBXL_CPUTOPOLOGY_INVALID_ENTRY) { continue; } node = topology[c].node; if (libxl_cpupool_cpuremove_node(ctx, 0, node, &n)) { fprintf(stderr, "error on removing cpu from Pool 0\n"); goto out; } free(name); xasprintf(&name, "Pool-node%d", node); libxl_uuid_generate(&uuid); poolid = 0; if (libxl_cpupool_create(ctx, name, sched, cpumap, &uuid, &poolid)) { fprintf(stderr, "error on creating cpupool\n"); goto out; } if (libxl_cpupool_cpuadd_node(ctx, poolid, node, &n)) { fprintf(stderr, "error on adding cpus to cpupool\n"); goto out; } for (p = c; p < n_cpus; p++) { if (topology[p].node == node) { topology[p].node = LIBXL_CPUTOPOLOGY_INVALID_ENTRY; } } } rc = EXIT_SUCCESS; out: libxl_cputopology_list_free(topology, n_cpus); libxl_bitmap_dispose(&cpumap); libxl_dominfo_dispose(&info); free(name); return rc; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_info.c0000664000175000017500000006071513256712137014053 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "xl.h" #include "xl_utils.h" /* Possibly select a specific piece of `xl info` to print. */ static const char *info_name; static int maybe_printf(const char *fmt, ...) __attribute__((format(printf,1,2))); static int maybe_printf(const char *fmt, ...) { va_list ap; char *str; int count = 0; va_start(ap, fmt); if (vasprintf(&str, fmt, ap) != -1) { if (info_name) { char *s; if (!strncmp(str, info_name, strlen(info_name)) && (s = strchr(str, ':')) && s[1] == ' ') count = fputs(&s[2], stdout); } else count = fputs(str, stdout); free(str); } va_end(ap); return count; } static yajl_gen_status printf_info_one_json(yajl_gen hand, int domid, libxl_domain_config *d_config) { yajl_gen_status s; s = yajl_gen_map_open(hand); if (s != yajl_gen_status_ok) goto out; s = yajl_gen_string(hand, (const unsigned char *)"domid", sizeof("domid")-1); if (s != yajl_gen_status_ok) goto out; if (domid != -1) s = yajl_gen_integer(hand, domid); else s = yajl_gen_null(hand); if (s != yajl_gen_status_ok) goto out; s = yajl_gen_string(hand, (const unsigned char *)"config", sizeof("config")-1); if (s != yajl_gen_status_ok) goto out; s = libxl_domain_config_gen_json(hand, d_config); if (s != yajl_gen_status_ok) goto out; s = yajl_gen_map_close(hand); if (s != yajl_gen_status_ok) goto out; out: return s; } void printf_info(enum output_format output_format, int domid, libxl_domain_config *d_config, FILE *fh); void printf_info(enum output_format output_format, int domid, libxl_domain_config *d_config, FILE *fh) { if (output_format == OUTPUT_FORMAT_SXP) return printf_info_sexp(domid, d_config, fh); const char *buf; libxl_yajl_length len = 0; yajl_gen_status s; yajl_gen hand; hand = libxl_yajl_gen_alloc(NULL); if (!hand) { fprintf(stderr, "unable to allocate JSON generator\n"); return; } s = printf_info_one_json(hand, domid, d_config); if (s != yajl_gen_status_ok) goto out; s = yajl_gen_get_buf(hand, (const unsigned char **)&buf, &len); if (s != yajl_gen_status_ok) goto out; fputs(buf, fh); out: yajl_gen_free(hand); if (s != yajl_gen_status_ok) fprintf(stderr, "unable to format domain config as JSON (YAJL:%d)\n", s); flush_stream(fh); } static void output_xeninfo(void) { const libxl_version_info *info; libxl_scheduler sched; int rc; if (!(info = libxl_get_version_info(ctx))) { fprintf(stderr, "libxl_get_version_info failed.\n"); return; } rc = libxl_get_scheduler(ctx); if (rc < 0) { fprintf(stderr, "get_scheduler sysctl failed.\n"); return; } sched = rc; maybe_printf("xen_major : %d\n", info->xen_version_major); maybe_printf("xen_minor : %d\n", info->xen_version_minor); maybe_printf("xen_extra : %s\n", info->xen_version_extra); maybe_printf("xen_version : %d.%d%s\n", info->xen_version_major, info->xen_version_minor, info->xen_version_extra); maybe_printf("xen_caps : %s\n", info->capabilities); maybe_printf("xen_scheduler : %s\n", libxl_scheduler_to_string(sched)); maybe_printf("xen_pagesize : %u\n", info->pagesize); maybe_printf("platform_params : virt_start=0x%"PRIx64"\n", info->virt_start); maybe_printf("xen_changeset : %s\n", info->changeset); maybe_printf("xen_commandline : %s\n", info->commandline); maybe_printf("cc_compiler : %s\n", info->compiler); maybe_printf("cc_compile_by : %s\n", info->compile_by); maybe_printf("cc_compile_domain : %s\n", info->compile_domain); maybe_printf("cc_compile_date : %s\n", info->compile_date); maybe_printf("build_id : %s\n", info->build_id); return; } static void output_nodeinfo(void) { struct utsname utsbuf; if (uname(&utsbuf) < 0) return; maybe_printf("host : %s\n", utsbuf.nodename); maybe_printf("release : %s\n", utsbuf.release); maybe_printf("version : %s\n", utsbuf.version); maybe_printf("machine : %s\n", utsbuf.machine); } static void output_physinfo(void) { libxl_physinfo info; const libxl_version_info *vinfo; unsigned int i; libxl_bitmap cpumap; int n = 0; if (libxl_get_physinfo(ctx, &info) != 0) { fprintf(stderr, "libxl_physinfo failed.\n"); return; } maybe_printf("nr_cpus : %d\n", info.nr_cpus); maybe_printf("max_cpu_id : %d\n", info.max_cpu_id); maybe_printf("nr_nodes : %d\n", info.nr_nodes); maybe_printf("cores_per_socket : %d\n", info.cores_per_socket); maybe_printf("threads_per_core : %d\n", info.threads_per_core); maybe_printf("cpu_mhz : %d\n", info.cpu_khz / 1000); maybe_printf("hw_caps : %08x:%08x:%08x:%08x:%08x:%08x:%08x:%08x\n", info.hw_cap[0], info.hw_cap[1], info.hw_cap[2], info.hw_cap[3], info.hw_cap[4], info.hw_cap[5], info.hw_cap[6], info.hw_cap[7] ); maybe_printf("virt_caps :%s%s\n", info.cap_hvm ? " hvm" : "", info.cap_hvm_directio ? " hvm_directio" : "" ); vinfo = libxl_get_version_info(ctx); if (vinfo) { i = (1 << 20) / vinfo->pagesize; maybe_printf("total_memory : %"PRIu64"\n", info.total_pages / i); maybe_printf("free_memory : %"PRIu64"\n", (info.free_pages - info.outstanding_pages) / i); maybe_printf("sharing_freed_memory : %"PRIu64"\n", info.sharing_freed_pages / i); maybe_printf("sharing_used_memory : %"PRIu64"\n", info.sharing_used_frames / i); maybe_printf("outstanding_claims : %"PRIu64"\n", info.outstanding_pages / i); } if (!libxl_get_freecpus(ctx, &cpumap)) { libxl_for_each_bit(i, cpumap) if (libxl_bitmap_test(&cpumap, i)) n++; maybe_printf("free_cpus : %d\n", n); free(cpumap.map); } libxl_physinfo_dispose(&info); return; } static void output_numainfo(void) { libxl_numainfo *info; int i, j, nr; info = libxl_get_numainfo(ctx, &nr); if (info == NULL) { fprintf(stderr, "libxl_get_numainfo failed.\n"); return; } printf("numa_info :\n"); printf("node: memsize memfree distances\n"); for (i = 0; i < nr; i++) { if (info[i].size != LIBXL_NUMAINFO_INVALID_ENTRY) { printf("%4d: %6"PRIu64" %6"PRIu64" %d", i, info[i].size >> 20, info[i].free >> 20, info[i].dists[0]); for (j = 1; j < info[i].num_dists; j++) printf(",%d", info[i].dists[j]); printf("\n"); } } libxl_numainfo_list_free(info, nr); return; } static void output_topologyinfo(void) { libxl_cputopology *cpuinfo; int i, nr; libxl_pcitopology *pciinfo; int valid_devs = 0; cpuinfo = libxl_get_cpu_topology(ctx, &nr); if (cpuinfo == NULL) { fprintf(stderr, "libxl_get_cpu_topology failed.\n"); return; } printf("cpu_topology :\n"); printf("cpu: core socket node\n"); for (i = 0; i < nr; i++) { if (cpuinfo[i].core != LIBXL_CPUTOPOLOGY_INVALID_ENTRY) printf("%3d: %4d %4d %4d\n", i, cpuinfo[i].core, cpuinfo[i].socket, cpuinfo[i].node); } libxl_cputopology_list_free(cpuinfo, nr); pciinfo = libxl_get_pci_topology(ctx, &nr); if (pciinfo == NULL) { fprintf(stderr, "libxl_get_pci_topology failed.\n"); return; } printf("device topology :\n"); printf("device node\n"); for (i = 0; i < nr; i++) { if (pciinfo[i].node != LIBXL_PCITOPOLOGY_INVALID_ENTRY) { printf("%04x:%02x:%02x.%01x %d\n", pciinfo[i].seg, pciinfo[i].bus, ((pciinfo[i].devfn >> 3) & 0x1f), (pciinfo[i].devfn & 7), pciinfo[i].node); valid_devs++; } } if (valid_devs == 0) printf("No device topology data available\n"); libxl_pcitopology_list_free(pciinfo, nr); return; } static void print_info(int numa) { output_nodeinfo(); output_physinfo(); if (numa) { output_topologyinfo(); output_numainfo(); } output_xeninfo(); maybe_printf("xend_config_format : 4\n"); return; } static void list_vm(void) { libxl_vminfo *info; char *domname; int nb_vm, i; info = libxl_list_vm(ctx, &nb_vm); if (!info) { fprintf(stderr, "libxl_list_vm failed.\n"); exit(EXIT_FAILURE); } printf("UUID ID name\n"); for (i = 0; i < nb_vm; i++) { domname = libxl_domid_to_name(ctx, info[i].domid); printf(LIBXL_UUID_FMT " %d %-30s\n", LIBXL_UUID_BYTES(info[i].uuid), info[i].domid, domname); free(domname); } libxl_vminfo_list_free(info, nb_vm); } static void list_domains(bool verbose, bool context, bool claim, bool numa, bool cpupool, const libxl_dominfo *info, int nb_domain) { int i; static const char shutdown_reason_letters[]= "-rscwS"; libxl_bitmap nodemap; libxl_physinfo physinfo; libxl_bitmap_init(&nodemap); libxl_physinfo_init(&physinfo); printf("Name ID Mem VCPUs\tState\tTime(s)"); if (verbose) printf(" UUID Reason-Code\tSecurity Label"); if (context && !verbose) printf(" Security Label"); if (claim) printf(" Claimed"); if (cpupool) printf(" Cpupool"); if (numa) { if (libxl_node_bitmap_alloc(ctx, &nodemap, 0)) { fprintf(stderr, "libxl_node_bitmap_alloc_failed.\n"); exit(EXIT_FAILURE); } if (libxl_get_physinfo(ctx, &physinfo) != 0) { fprintf(stderr, "libxl_physinfo failed.\n"); libxl_bitmap_dispose(&nodemap); exit(EXIT_FAILURE); } printf(" NODE Affinity"); } printf("\n"); for (i = 0; i < nb_domain; i++) { char *domname; libxl_shutdown_reason shutdown_reason; domname = libxl_domid_to_name(ctx, info[i].domid); shutdown_reason = info[i].shutdown ? info[i].shutdown_reason : 0; printf("%-40s %5d %5lu %5d %c%c%c%c%c%c %8.1f", domname, info[i].domid, (unsigned long) ((info[i].current_memkb + info[i].outstanding_memkb)/ 1024), info[i].vcpu_online, info[i].running ? 'r' : '-', info[i].blocked ? 'b' : '-', info[i].paused ? 'p' : '-', info[i].shutdown ? 's' : '-', (shutdown_reason >= 0 && shutdown_reason < sizeof(shutdown_reason_letters)-1 ? shutdown_reason_letters[shutdown_reason] : '?'), info[i].dying ? 'd' : '-', ((float)info[i].cpu_time / 1e9)); free(domname); if (verbose) { printf(" " LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info[i].uuid)); if (info[i].shutdown) printf(" %8x", shutdown_reason); else printf(" %8s", "-"); } if (claim) printf(" %5lu", (unsigned long)info[i].outstanding_memkb / 1024); if (verbose || context) printf(" %16s", info[i].ssid_label ? : "-"); if (cpupool) { char *poolname = libxl_cpupoolid_to_name(ctx, info[i].cpupool); printf("%16s", poolname); free(poolname); } if (numa) { libxl_domain_get_nodeaffinity(ctx, info[i].domid, &nodemap); putchar(' '); print_bitmap(nodemap.map, physinfo.nr_nodes, stdout); } putchar('\n'); } libxl_bitmap_dispose(&nodemap); libxl_physinfo_dispose(&physinfo); } static void list_domains_details(const libxl_dominfo *info, int nb_domain) { libxl_domain_config d_config; int i, rc; yajl_gen hand = NULL; yajl_gen_status s; const char *buf; libxl_yajl_length yajl_len = 0; if (default_output_format == OUTPUT_FORMAT_JSON) { hand = libxl_yajl_gen_alloc(NULL); if (!hand) { fprintf(stderr, "unable to allocate JSON generator\n"); return; } s = yajl_gen_array_open(hand); if (s != yajl_gen_status_ok) goto out; } else s = yajl_gen_status_ok; for (i = 0; i < nb_domain; i++) { libxl_domain_config_init(&d_config); rc = libxl_retrieve_domain_configuration(ctx, info[i].domid, &d_config); if (rc) continue; if (default_output_format == OUTPUT_FORMAT_JSON) s = printf_info_one_json(hand, info[i].domid, &d_config); else printf_info_sexp(info[i].domid, &d_config, stdout); libxl_domain_config_dispose(&d_config); if (s != yajl_gen_status_ok) goto out; } if (default_output_format == OUTPUT_FORMAT_JSON) { s = yajl_gen_array_close(hand); if (s != yajl_gen_status_ok) goto out; s = yajl_gen_get_buf(hand, (const unsigned char **)&buf, &yajl_len); if (s != yajl_gen_status_ok) goto out; puts(buf); } out: if (default_output_format == OUTPUT_FORMAT_JSON) { yajl_gen_free(hand); if (s != yajl_gen_status_ok) fprintf(stderr, "unable to format domain config as JSON (YAJL:%d)\n", s); } } int main_list(int argc, char **argv) { int opt; bool verbose = false; bool context = false; bool details = false; bool cpupool = false; bool numa = false; static struct option opts[] = { {"long", 0, 0, 'l'}, {"verbose", 0, 0, 'v'}, {"context", 0, 0, 'Z'}, {"cpupool", 0, 0, 'c'}, {"numa", 0, 0, 'n'}, COMMON_LONG_OPTS }; libxl_dominfo info_buf; libxl_dominfo *info, *info_free=0; int nb_domain, rc; SWITCH_FOREACH_OPT(opt, "lvhZcn", opts, "list", 0) { case 'l': details = true; break; case 'v': verbose = true; break; case 'Z': context = true; break; case 'c': cpupool = true; break; case 'n': numa = true; break; } libxl_dominfo_init(&info_buf); if (optind >= argc) { info = libxl_list_domain(ctx, &nb_domain); if (!info) { fprintf(stderr, "libxl_list_domain failed.\n"); return EXIT_FAILURE; } info_free = info; } else if (optind == argc-1) { uint32_t domid = find_domain(argv[optind]); rc = libxl_domain_info(ctx, &info_buf, domid); if (rc == ERROR_DOMAIN_NOTFOUND) { fprintf(stderr, "Error: Domain \'%s\' does not exist.\n", argv[optind]); return EXIT_FAILURE; } if (rc) { fprintf(stderr, "libxl_domain_info failed (code %d).\n", rc); return EXIT_FAILURE; } info = &info_buf; nb_domain = 1; } else { help("list"); return EXIT_FAILURE; } if (details) list_domains_details(info, nb_domain); else list_domains(verbose, context, false /* claim */, numa, cpupool, info, nb_domain); if (info_free) libxl_dominfo_list_free(info, nb_domain); libxl_dominfo_dispose(&info_buf); return EXIT_SUCCESS; } int main_vm_list(int argc, char **argv) { int opt; SWITCH_FOREACH_OPT(opt, "", NULL, "vm-list", 0) { /* No options */ } list_vm(); return EXIT_SUCCESS; } int main_info(int argc, char **argv) { int opt; static struct option opts[] = { {"numa", 0, 0, 'n'}, COMMON_LONG_OPTS }; int numa = 0; SWITCH_FOREACH_OPT(opt, "n", opts, "info", 0) { case 'n': numa = 1; break; } /* * If an extra argument is provided, filter out a specific piece of * information. */ if (numa == 0 && argc > optind) info_name = argv[optind]; print_info(numa); return 0; } int main_domid(int argc, char **argv) { uint32_t domid; int opt; const char *domname = NULL; SWITCH_FOREACH_OPT(opt, "", NULL, "domid", 1) { /* No options */ } domname = argv[optind]; if (libxl_name_to_domid(ctx, domname, &domid)) { fprintf(stderr, "Can't get domid of domain name '%s', maybe this domain does not exist.\n", domname); return EXIT_FAILURE; } printf("%u\n", domid); return EXIT_SUCCESS; } int main_domname(int argc, char **argv) { uint32_t domid; int opt; char *domname = NULL; char *endptr = NULL; SWITCH_FOREACH_OPT(opt, "", NULL, "domname", 1) { /* No options */ } domid = strtol(argv[optind], &endptr, 10); if (domid == 0 && !strcmp(endptr, argv[optind])) { /*no digits at all*/ fprintf(stderr, "Invalid domain id.\n\n"); return EXIT_FAILURE; } domname = libxl_domid_to_name(ctx, domid); if (!domname) { fprintf(stderr, "Can't get domain name of domain id '%u', maybe this domain does not exist.\n", domid); return EXIT_FAILURE; } printf("%s\n", domname); free(domname); return EXIT_SUCCESS; } static char *uptime_to_string(unsigned long uptime, int short_mode) { int sec, min, hour, day; char *time_string; day = (int)(uptime / 86400); uptime -= (day * 86400); hour = (int)(uptime / 3600); uptime -= (hour * 3600); min = (int)(uptime / 60); uptime -= (min * 60); sec = uptime; if (short_mode) if (day > 1) xasprintf(&time_string, "%d days, %2d:%02d", day, hour, min); else if (day == 1) xasprintf(&time_string, "%d day, %2d:%02d", day, hour, min); else xasprintf(&time_string, "%2d:%02d", hour, min); else if (day > 1) xasprintf(&time_string, "%d days, %2d:%02d:%02d", day, hour, min, sec); else if (day == 1) xasprintf(&time_string, "%d day, %2d:%02d:%02d", day, hour, min, sec); else xasprintf(&time_string, "%2d:%02d:%02d", hour, min, sec); return time_string; } int main_claims(int argc, char **argv) { libxl_dominfo *info; int opt; int nb_domain; SWITCH_FOREACH_OPT(opt, "", NULL, "claims", 0) { /* No options */ } if (!claim_mode) fprintf(stderr, "claim_mode not enabled (see man xl.conf).\n"); info = libxl_list_domain(ctx, &nb_domain); if (!info) { fprintf(stderr, "libxl_list_domain failed.\n"); return 1; } list_domains(false /* verbose */, false /* context */, true /* claim */, false /* numa */, false /* cpupool */, info, nb_domain); libxl_dominfo_list_free(info, nb_domain); return 0; } static char *current_time_to_string(time_t now) { char now_str[100]; struct tm *tmp; tmp = localtime(&now); if (tmp == NULL) { fprintf(stderr, "Get localtime error"); exit(-1); } if (strftime(now_str, sizeof(now_str), "%H:%M:%S", tmp) == 0) { fprintf(stderr, "strftime returned 0"); exit(-1); } return strdup(now_str); } static void print_dom0_uptime(int short_mode, time_t now) { int fd; ssize_t nr; char buf[512]; uint32_t uptime = 0; char *uptime_str = NULL; char *now_str = NULL; char *domname; fd = open("/proc/uptime", O_RDONLY); if (fd == -1) goto err; nr = read(fd, buf, sizeof(buf) - 1); if (nr == -1) { close(fd); goto err; } close(fd); buf[nr] = '\0'; strtok(buf, " "); uptime = strtoul(buf, NULL, 10); domname = libxl_domid_to_name(ctx, 0); if (short_mode) { now_str = current_time_to_string(now); uptime_str = uptime_to_string(uptime, 1); printf(" %s up %s, %s (%d)\n", now_str, uptime_str, domname, 0); } else { now_str = NULL; uptime_str = uptime_to_string(uptime, 0); printf("%-33s %4d %s\n", domname, 0, uptime_str); } free(now_str); free(uptime_str); free(domname); return; err: fprintf(stderr, "Can not get Dom0 uptime.\n"); exit(-1); } static void print_domU_uptime(uint32_t domuid, int short_mode, time_t now) { uint32_t s_time = 0; uint32_t uptime = 0; char *uptime_str = NULL; char *now_str = NULL; char *domname; s_time = libxl_vm_get_start_time(ctx, domuid); if (s_time == -1) return; uptime = now - s_time; domname = libxl_domid_to_name(ctx, domuid); if (short_mode) { now_str = current_time_to_string(now); uptime_str = uptime_to_string(uptime, 1); printf(" %s up %s, %s (%d)\n", now_str, uptime_str, domname, domuid); } else { now_str = NULL; uptime_str = uptime_to_string(uptime, 0); printf("%-33s %4d %s\n", domname, domuid, uptime_str); } free(domname); free(now_str); free(uptime_str); return; } static void print_uptime(int short_mode, uint32_t doms[], int nb_doms) { libxl_vminfo *info; time_t now; int nb_vm, i; now = time(NULL); if (!short_mode) printf("%-33s %4s %s\n", "Name", "ID", "Uptime"); if (nb_doms == 0) { print_dom0_uptime(short_mode, now); info = libxl_list_vm(ctx, &nb_vm); if (info == NULL) { fprintf(stderr, "Could not list vms.\n"); return; } for (i = 0; i < nb_vm; i++) { if (info[i].domid == 0) continue; print_domU_uptime(info[i].domid, short_mode, now); } libxl_vminfo_list_free(info, nb_vm); } else { for (i = 0; i < nb_doms; i++) { if (doms[i] == 0) print_dom0_uptime(short_mode, now); else print_domU_uptime(doms[i], short_mode, now); } } } int main_uptime(int argc, char **argv) { const char *dom; int short_mode = 0; uint32_t domains[100]; int nb_doms = 0; int opt; SWITCH_FOREACH_OPT(opt, "s", NULL, "uptime", 0) { case 's': short_mode = 1; break; } for (;(dom = argv[optind]) != NULL; nb_doms++,optind++) domains[nb_doms] = find_domain(dom); print_uptime(short_mode, domains, nb_doms); return 0; } int main_dmesg(int argc, char **argv) { unsigned int clear = 0; libxl_xen_console_reader *cr; char *line; int opt, ret = 1; SWITCH_FOREACH_OPT(opt, "c", NULL, "dmesg", 0) { case 'c': clear = 1; break; } cr = libxl_xen_console_read_start(ctx, clear); if (!cr) goto finish; while ((ret = libxl_xen_console_read_line(ctx, cr, &line)) > 0) printf("%s", line); finish: if (cr) libxl_xen_console_read_finish(ctx, cr); return ret ? EXIT_FAILURE : EXIT_SUCCESS; } int main_top(int argc, char **argv) { int opt; SWITCH_FOREACH_OPT(opt, "", NULL, "top", 0) { /* No options */ } return system("xentop"); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_flask.c0000664000175000017500000000656113256712137014217 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include "xl.h" int main_getenforce(int argc, char **argv) { int ret; ret = libxl_flask_getenforce(ctx); if (ret < 0) { if (errno == ENOSYS) printf("Flask XSM Disabled\n"); else fprintf(stderr, "Failed to get enforcing mode\n"); } else if (ret == 1) printf("Enforcing\n"); else if (ret == 0) printf("Permissive\n"); return ret; } int main_setenforce(int argc, char **argv) { int ret, mode; const char *p = NULL; if (optind >= argc) { help("setenforce"); return 2; } p = argv[optind]; if (!strcmp(p, "0")) mode = 0; else if (!strcmp(p, "1")) mode = 1; else if (!strcasecmp(p, "permissive")) mode = 0; else if (!strcasecmp(p, "enforcing")) mode = 1; else { help("setenforce"); return 2; } ret = libxl_flask_setenforce(ctx, mode); if (ret) { if (errno == ENOSYS) { fprintf(stderr, "Flask XSM disabled\n"); } else fprintf(stderr, "error occured while setting enforcing mode (%i)\n", ret); } return ret; } int main_loadpolicy(int argc, char **argv) { const char *polFName; int polFd = -1; void *polMemCp = NULL; struct stat info; int ret; if (optind >= argc) { help("loadpolicy"); return 2; } polFName = argv[optind]; polFd = open(polFName, O_RDONLY); if (polFd < 0) { fprintf(stderr, "Error occurred opening policy file '%s': %s\n", polFName, strerror(errno)); ret = -1; goto done; } ret = stat(polFName, &info); if (ret < 0) { fprintf(stderr, "Error occurred retrieving information about" "policy file '%s': %s\n", polFName, strerror(errno)); goto done; } polMemCp = malloc(info.st_size); ret = read(polFd, polMemCp, info.st_size); if ( ret < 0 ) { fprintf(stderr, "Unable to read new Flask policy file: %s\n", strerror(errno)); goto done; } ret = libxl_flask_loadpolicy(ctx, polMemCp, info.st_size); if (ret < 0) { if (errno == ENOSYS) { fprintf(stderr, "Flask XSM disabled\n"); } else { errno = -ret; fprintf(stderr, "Unable to load new Flask policy: %s\n", strerror(errno)); ret = -1; } } else { printf("Successfully loaded policy.\n"); } done: free(polMemCp); if (polFd >= 0) close(polFd); return ret; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_pci.c0000664000175000017500000001417313256712137013670 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" static void pcilist(uint32_t domid) { libxl_device_pci *pcidevs; int num, i; pcidevs = libxl_device_pci_list(ctx, domid, &num); if (pcidevs == NULL) return; printf("Vdev Device\n"); for (i = 0; i < num; i++) { printf("%02x.%01x %04x:%02x:%02x.%01x\n", (pcidevs[i].vdevfn >> 3) & 0x1f, pcidevs[i].vdevfn & 0x7, pcidevs[i].domain, pcidevs[i].bus, pcidevs[i].dev, pcidevs[i].func); libxl_device_pci_dispose(&pcidevs[i]); } free(pcidevs); } int main_pcilist(int argc, char **argv) { uint32_t domid; int opt; SWITCH_FOREACH_OPT(opt, "", NULL, "pci-list", 1) { /* No options */ } domid = find_domain(argv[optind]); pcilist(domid); return 0; } static int pcidetach(uint32_t domid, const char *bdf, int force) { libxl_device_pci pcidev; XLU_Config *config; int r = 0; libxl_device_pci_init(&pcidev); config = xlu_cfg_init(stderr, "command line"); if (!config) { perror("xlu_cfg_inig"); exit(-1); } if (xlu_pci_parse_bdf(config, &pcidev, bdf)) { fprintf(stderr, "pci-detach: malformed BDF specification \"%s\"\n", bdf); exit(2); } if (force) { if (libxl_device_pci_destroy(ctx, domid, &pcidev, 0)) r = 1; } else { if (libxl_device_pci_remove(ctx, domid, &pcidev, 0)) r = 1; } libxl_device_pci_dispose(&pcidev); xlu_cfg_destroy(config); return r; } int main_pcidetach(int argc, char **argv) { uint32_t domid; int opt; int force = 0; const char *bdf = NULL; SWITCH_FOREACH_OPT(opt, "f", NULL, "pci-detach", 2) { case 'f': force = 1; break; } domid = find_domain(argv[optind]); bdf = argv[optind + 1]; if (pcidetach(domid, bdf, force)) return EXIT_FAILURE; return EXIT_SUCCESS; } static int pciattach(uint32_t domid, const char *bdf, const char *vs) { libxl_device_pci pcidev; XLU_Config *config; int r = 0; libxl_device_pci_init(&pcidev); config = xlu_cfg_init(stderr, "command line"); if (!config) { perror("xlu_cfg_inig"); exit(-1); } if (xlu_pci_parse_bdf(config, &pcidev, bdf)) { fprintf(stderr, "pci-attach: malformed BDF specification \"%s\"\n", bdf); exit(2); } if (libxl_device_pci_add(ctx, domid, &pcidev, 0)) r = 1; libxl_device_pci_dispose(&pcidev); xlu_cfg_destroy(config); return r; } int main_pciattach(int argc, char **argv) { uint32_t domid; int opt; const char *bdf = NULL, *vs = NULL; SWITCH_FOREACH_OPT(opt, "", NULL, "pci-attach", 2) { /* No options */ } domid = find_domain(argv[optind]); bdf = argv[optind + 1]; if (optind + 1 < argc) vs = argv[optind + 2]; if (pciattach(domid, bdf, vs)) return EXIT_FAILURE; return EXIT_SUCCESS; } static void pciassignable_list(void) { libxl_device_pci *pcidevs; int num, i; pcidevs = libxl_device_pci_assignable_list(ctx, &num); if ( pcidevs == NULL ) return; for (i = 0; i < num; i++) { printf("%04x:%02x:%02x.%01x\n", pcidevs[i].domain, pcidevs[i].bus, pcidevs[i].dev, pcidevs[i].func); libxl_device_pci_dispose(&pcidevs[i]); } free(pcidevs); } int main_pciassignable_list(int argc, char **argv) { int opt; SWITCH_FOREACH_OPT(opt, "", NULL, "pci-assignable-list", 0) { /* No options */ } pciassignable_list(); return 0; } static int pciassignable_add(const char *bdf, int rebind) { libxl_device_pci pcidev; XLU_Config *config; int r = 0; libxl_device_pci_init(&pcidev); config = xlu_cfg_init(stderr, "command line"); if (!config) { perror("xlu_cfg_init"); exit(-1); } if (xlu_pci_parse_bdf(config, &pcidev, bdf)) { fprintf(stderr, "pci-assignable-add: malformed BDF specification \"%s\"\n", bdf); exit(2); } if (libxl_device_pci_assignable_add(ctx, &pcidev, rebind)) r = 1; libxl_device_pci_dispose(&pcidev); xlu_cfg_destroy(config); return r; } int main_pciassignable_add(int argc, char **argv) { int opt; const char *bdf = NULL; SWITCH_FOREACH_OPT(opt, "", NULL, "pci-assignable-add", 1) { /* No options */ } bdf = argv[optind]; if (pciassignable_add(bdf, 1)) return EXIT_FAILURE; return EXIT_SUCCESS; } static int pciassignable_remove(const char *bdf, int rebind) { libxl_device_pci pcidev; XLU_Config *config; int r = 0; libxl_device_pci_init(&pcidev); config = xlu_cfg_init(stderr, "command line"); if (!config) { perror("xlu_cfg_init"); exit(-1); } if (xlu_pci_parse_bdf(config, &pcidev, bdf)) { fprintf(stderr, "pci-assignable-remove: malformed BDF specification \"%s\"\n", bdf); exit(2); } if (libxl_device_pci_assignable_remove(ctx, &pcidev, rebind)) r = 1; libxl_device_pci_dispose(&pcidev); xlu_cfg_destroy(config); return r; } int main_pciassignable_remove(int argc, char **argv) { int opt; const char *bdf = NULL; int rebind = 0; SWITCH_FOREACH_OPT(opt, "r", NULL, "pci-assignable-remove", 1) { case 'r': rebind=1; break; } bdf = argv[optind]; if (pciassignable_remove(bdf, rebind)) return EXIT_FAILURE; return EXIT_SUCCESS; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_block.c0000664000175000017500000000720713256712137014207 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" int main_blockattach(int argc, char **argv) { int opt; uint32_t fe_domid; libxl_device_disk disk; XLU_Config *config = 0; SWITCH_FOREACH_OPT(opt, "", NULL, "block-attach", 2) { /* No options */ } if (libxl_domain_qualifier_to_domid(ctx, argv[optind], &fe_domid) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]); return 1; } optind++; parse_disk_config_multistring (&config, argc-optind, (const char* const*)argv + optind, &disk); if (dryrun_only) { char *json = libxl_device_disk_to_json(ctx, &disk); printf("disk: %s\n", json); free(json); if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); } return 0; } if (libxl_device_disk_add(ctx, fe_domid, &disk, 0)) { fprintf(stderr, "libxl_device_disk_add failed.\n"); return 1; } return 0; } int main_blocklist(int argc, char **argv) { int opt; int i, nb; libxl_device_disk *disks; libxl_diskinfo diskinfo; SWITCH_FOREACH_OPT(opt, "", NULL, "block-list", 1) { /* No options */ } printf("%-5s %-3s %-6s %-5s %-6s %-8s %-30s\n", "Vdev", "BE", "handle", "state", "evt-ch", "ring-ref", "BE-path"); for (argv += optind, argc -= optind; argc > 0; --argc, ++argv) { uint32_t domid; if (libxl_domain_qualifier_to_domid(ctx, *argv, &domid) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", *argv); continue; } disks = libxl_device_disk_list(ctx, domid, &nb); if (!disks) { continue; } for (i=0; i= 0) close(nullfd); return rc; * Function calls which might fail (ie most function calls) are handled by putting the return/status value into a variable, and then checking it in a separate statement: char *dompath = libxl__xs_get_dompath(gc, bl->domid); if (!dompath) { rc = ERROR_FAIL; goto out; } * If a resource is freed in the main body of the function (for example, in a loop), the corresponding variable has to be reset to the sentinel at the point where it's freed. Whether to use the `out' path for successful returns as well as error returns is a matter of taste and convenience for the specific function. If you reuse the `out' path for successful returns, there may be resources which are to be returned to the caller rather than freed. In that case you have to reset the local variable to `nothing here', to avoid the resource being freed on the out path. That resetting should be done immediately after the resource value is stored at the applicable _r function parameter (or equivalent). Do not test `rc' in the out section, to discover whether to free things. The uses of the single-line formatting in the examples above are permitted exceptions to the usual xl code formatting rules. FORMATTING AND NAMING --------------------- Blatantly copied from qemu and linux with few modifications. 1. Whitespace Of course, the most important aspect in any coding style is whitespace. Crusty old coders who have trouble spotting the glasses on their noses can tell the difference between a tab and eight spaces from a distance of approximately fifteen parsecs. Many a flamewar have been fought and lost on this issue. Xl indents are four spaces. Tabs are never used, except in Makefiles where they have been irreversibly coded into the syntax. Spaces of course are superior to tabs because: - You have just one way to specify whitespace, not two. Ambiguity breeds mistakes. - The confusion surrounding 'use tabs to indent, spaces to justify' is gone. - Tab indents push your code to the right, making your screen seriously unbalanced. - Tabs will be rendered incorrectly on editors who are misconfigured not to use tab stops of eight positions. - Tabs are rendered badly in patches, causing off-by-one errors in almost every line. - It is the xl coding style. Do not leave whitespace dangling off the ends of lines. 2. Line width Lines are limited to 75-80 characters. Rationale: - Some people like to tile their 24" screens with a 6x4 matrix of 80x24 xterms and use vi in all of them. The best way to punish them is to let them keep doing it. - Code and especially patches is much more readable if limited to a sane line length. Eighty is traditional. - It is the xl coding style. 3. Naming C is a Spartan language, and so should your naming be. Unlike Modula-2 and Pascal programmers, C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable "tmp", which is much easier to write, and not the least more difficult to understand. HOWEVER, while mixed-case names are frowned upon, descriptive names for global variables are a must. To call a global function "foo" is a shooting offense. GLOBAL variables (to be used only if you _really_ need them) need to have descriptive names, as do global functions. If you have a function that counts the number of active users, you should call that "count_active_users()" or similar, you should _not_ call it "cntusr()". Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged - the compiler knows the types anyway and can check those, and it only confuses the programmer. LOCAL variable names should be short, and to the point. If you have some random integer loop counter, it should probably be called "i". Calling it "loop_counter" is non-productive, if there is no chance of it being mis-understood. Similarly, "tmp" can be just about any type of variable that is used to hold a temporary value. Local variables used to store return values should have descriptive name like "rc" or "ret". Following the same reasoning the label used as exit path should be called "out". Function arguments which are used to return values to the caller should be suffixed `_r' or `_out'. Variables, type names and function names are lower_case_with_underscores. Xl should avoid using libxl_ and libxl__ as prefix for its own function names. 4. Statements Don't put multiple statements on a single line. Don't put multiple assignments on a single line either. Error code paths with an if statement and a goto or a return on the same line are allowed. Examples: if (rc) goto out; if (rc < 0) return; Xl coding style is super simple. Avoid tricky expressions. 5. Block structure Every indented statement is braced, but blocks that contain just one statement may have the braces omitted. To avoid confusion, either all the blocks in an if...else chain have braces, or none of them do. The opening brace is on the line that contains the control flow statement that introduces the new block; the closing brace is on the same line as the else keyword, or on a line by itself if there is no else keyword. Examples: if (a == 5) { printf("a was 5.\n"); } else if (a == 6) { printf("a was 6.\n"); } else { printf("a was something else entirely.\n"); } if (a == 5) printf("a was 5.\n"); An exception is the opening brace for a function; for reasons of tradition and clarity it comes on a line by itself: void a_function(void) { do_something(); } Rationale: a consistent (except for functions...) bracing style reduces ambiguity and avoids needless churn when lines are added or removed. Furthermore, it is the xl coding style. xen-4.9.2/tools/xl/xl_sxp.c0000664000175000017500000002453613256712137013733 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * Legacy SXP output handling */ #include #include #include #include #include "xl.h" /* In general you should not add new output to this function since it * is intended only for legacy use. */ void printf_info_sexp(int domid, libxl_domain_config *d_config, FILE *fh) { int i; libxl_dominfo info; libxl_domain_create_info *c_info = &d_config->c_info; libxl_domain_build_info *b_info = &d_config->b_info; fprintf(fh, "(domain\n\t(domid %d)\n", domid); fprintf(fh, "\t(create_info)\n"); fprintf(fh, "\t(hvm %d)\n", c_info->type == LIBXL_DOMAIN_TYPE_HVM); fprintf(fh, "\t(hap %s)\n", libxl_defbool_to_string(c_info->hap)); fprintf(fh, "\t(oos %s)\n", libxl_defbool_to_string(c_info->oos)); fprintf(fh, "\t(ssidref %d)\n", c_info->ssidref); fprintf(fh, "\t(name %s)\n", c_info->name); /* retrieve the UUID from dominfo, since it is probably generated * during parsing and thus does not match the real one */ if (libxl_domain_info(ctx, &info, domid) == 0) { fprintf(fh, "\t(uuid " LIBXL_UUID_FMT ")\n", LIBXL_UUID_BYTES(info.uuid)); } else { fprintf(fh, "\t(uuid )\n"); } if (c_info->pool_name) fprintf(fh, "\t(cpupool %s)\n", c_info->pool_name); if (c_info->xsdata) fprintf(fh, "\t(xsdata contains data)\n"); else fprintf(fh, "\t(xsdata (null))\n"); if (c_info->platformdata) fprintf(fh, "\t(platformdata contains data)\n"); else fprintf(fh, "\t(platformdata (null))\n"); fprintf(fh, "\t(build_info)\n"); fprintf(fh, "\t(max_vcpus %d)\n", b_info->max_vcpus); fprintf(fh, "\t(tsc_mode %s)\n", libxl_tsc_mode_to_string(b_info->tsc_mode)); fprintf(fh, "\t(max_memkb %"PRId64")\n", b_info->max_memkb); fprintf(fh, "\t(target_memkb %"PRId64")\n", b_info->target_memkb); fprintf(fh, "\t(nomigrate %s)\n", libxl_defbool_to_string(b_info->disable_migrate)); if (c_info->type == LIBXL_DOMAIN_TYPE_PV && b_info->u.pv.bootloader) { fprintf(fh, "\t(bootloader %s)\n", b_info->u.pv.bootloader); if (b_info->u.pv.bootloader_args) { fprintf(fh, "\t(bootloader_args"); for (i=0; b_info->u.pv.bootloader_args[i]; i++) fprintf(fh, " %s", b_info->u.pv.bootloader_args[i]); fprintf(fh, ")\n"); } } fprintf(fh, "\t(image\n"); switch (c_info->type) { case LIBXL_DOMAIN_TYPE_HVM: fprintf(fh, "\t\t(hvm\n"); fprintf(fh, "\t\t\t(firmware %s)\n", b_info->u.hvm.firmware); fprintf(fh, "\t\t\t(video_memkb %"PRId64")\n", b_info->video_memkb); fprintf(fh, "\t\t\t(shadow_memkb %"PRId64")\n", b_info->shadow_memkb); fprintf(fh, "\t\t\t(pae %s)\n", libxl_defbool_to_string(b_info->u.hvm.pae)); fprintf(fh, "\t\t\t(apic %s)\n", libxl_defbool_to_string(b_info->u.hvm.apic)); fprintf(fh, "\t\t\t(acpi %s)\n", libxl_defbool_to_string(b_info->u.hvm.acpi)); fprintf(fh, "\t\t\t(nx %s)\n", libxl_defbool_to_string(b_info->u.hvm.nx)); fprintf(fh, "\t\t\t(viridian %s)\n", libxl_defbool_to_string(b_info->u.hvm.viridian)); fprintf(fh, "\t\t\t(hpet %s)\n", libxl_defbool_to_string(b_info->u.hvm.hpet)); fprintf(fh, "\t\t\t(vpt_align %s)\n", libxl_defbool_to_string(b_info->u.hvm.vpt_align)); fprintf(fh, "\t\t\t(timer_mode %s)\n", libxl_timer_mode_to_string(b_info->u.hvm.timer_mode)); fprintf(fh, "\t\t\t(nestedhvm %s)\n", libxl_defbool_to_string(b_info->u.hvm.nested_hvm)); fprintf(fh, "\t\t\t(stdvga %s)\n", b_info->u.hvm.vga.kind == LIBXL_VGA_INTERFACE_TYPE_STD ? "True" : "False"); fprintf(fh, "\t\t\t(vnc %s)\n", libxl_defbool_to_string(b_info->u.hvm.vnc.enable)); fprintf(fh, "\t\t\t(vnclisten %s)\n", b_info->u.hvm.vnc.listen); fprintf(fh, "\t\t\t(vncdisplay %d)\n", b_info->u.hvm.vnc.display); fprintf(fh, "\t\t\t(vncunused %s)\n", libxl_defbool_to_string(b_info->u.hvm.vnc.findunused)); fprintf(fh, "\t\t\t(keymap %s)\n", b_info->u.hvm.keymap); fprintf(fh, "\t\t\t(sdl %s)\n", libxl_defbool_to_string(b_info->u.hvm.sdl.enable)); fprintf(fh, "\t\t\t(opengl %s)\n", libxl_defbool_to_string(b_info->u.hvm.sdl.opengl)); fprintf(fh, "\t\t\t(nographic %s)\n", libxl_defbool_to_string(b_info->u.hvm.nographic)); fprintf(fh, "\t\t\t(spice %s)\n", libxl_defbool_to_string(b_info->u.hvm.spice.enable)); fprintf(fh, "\t\t\t(spiceport %d)\n", b_info->u.hvm.spice.port); fprintf(fh, "\t\t\t(spicetls_port %d)\n", b_info->u.hvm.spice.tls_port); fprintf(fh, "\t\t\t(spicehost %s)\n", b_info->u.hvm.spice.host); fprintf(fh, "\t\t\t(spicedisable_ticketing %s)\n", libxl_defbool_to_string(b_info->u.hvm.spice.disable_ticketing)); fprintf(fh, "\t\t\t(spiceagent_mouse %s)\n", libxl_defbool_to_string(b_info->u.hvm.spice.agent_mouse)); fprintf(fh, "\t\t\t(device_model %s)\n", b_info->device_model ? : "default"); fprintf(fh, "\t\t\t(gfx_passthru %s)\n", libxl_defbool_to_string(b_info->u.hvm.gfx_passthru)); fprintf(fh, "\t\t\t(serial %s)\n", b_info->u.hvm.serial); fprintf(fh, "\t\t\t(boot %s)\n", b_info->u.hvm.boot); fprintf(fh, "\t\t\t(usb %s)\n", libxl_defbool_to_string(b_info->u.hvm.usb)); fprintf(fh, "\t\t\t(usbdevice %s)\n", b_info->u.hvm.usbdevice); fprintf(fh, "\t\t)\n"); break; case LIBXL_DOMAIN_TYPE_PV: fprintf(fh, "\t\t(linux %d)\n", 0); fprintf(fh, "\t\t\t(kernel %s)\n", b_info->kernel); fprintf(fh, "\t\t\t(cmdline %s)\n", b_info->cmdline); fprintf(fh, "\t\t\t(ramdisk %s)\n", b_info->ramdisk); fprintf(fh, "\t\t\t(e820_host %s)\n", libxl_defbool_to_string(b_info->u.pv.e820_host)); fprintf(fh, "\t\t)\n"); break; default: fprintf(stderr, "Unknown domain type %d\n", c_info->type); exit(1); } fprintf(fh, "\t)\n"); for (i = 0; i < d_config->num_disks; i++) { fprintf(fh, "\t(device\n"); fprintf(fh, "\t\t(tap\n"); fprintf(fh, "\t\t\t(backend_domid %d)\n", d_config->disks[i].backend_domid); fprintf(fh, "\t\t\t(frontend_domid %d)\n", domid); fprintf(fh, "\t\t\t(physpath %s)\n", d_config->disks[i].pdev_path); fprintf(fh, "\t\t\t(phystype %d)\n", d_config->disks[i].backend); fprintf(fh, "\t\t\t(virtpath %s)\n", d_config->disks[i].vdev); fprintf(fh, "\t\t\t(unpluggable %d)\n", d_config->disks[i].removable); fprintf(fh, "\t\t\t(readwrite %d)\n", d_config->disks[i].readwrite); fprintf(fh, "\t\t\t(is_cdrom %d)\n", d_config->disks[i].is_cdrom); fprintf(fh, "\t\t)\n"); fprintf(fh, "\t)\n"); } for (i = 0; i < d_config->num_nics; i++) { fprintf(fh, "\t(device\n"); fprintf(fh, "\t\t(vif\n"); if (d_config->nics[i].ifname) fprintf(fh, "\t\t\t(vifname %s)\n", d_config->nics[i].ifname); fprintf(fh, "\t\t\t(backend_domid %d)\n", d_config->nics[i].backend_domid); fprintf(fh, "\t\t\t(frontend_domid %d)\n", domid); fprintf(fh, "\t\t\t(devid %d)\n", d_config->nics[i].devid); fprintf(fh, "\t\t\t(mtu %d)\n", d_config->nics[i].mtu); fprintf(fh, "\t\t\t(model %s)\n", d_config->nics[i].model); fprintf(fh, "\t\t\t(mac %02x%02x%02x%02x%02x%02x)\n", d_config->nics[i].mac[0], d_config->nics[i].mac[1], d_config->nics[i].mac[2], d_config->nics[i].mac[3], d_config->nics[i].mac[4], d_config->nics[i].mac[5]); fprintf(fh, "\t\t)\n"); fprintf(fh, "\t)\n"); } for (i = 0; i < d_config->num_pcidevs; i++) { fprintf(fh, "\t(device\n"); fprintf(fh, "\t\t(pci\n"); fprintf(fh, "\t\t\t(pci dev %04x:%02x:%02x.%01x@%02x)\n", d_config->pcidevs[i].domain, d_config->pcidevs[i].bus, d_config->pcidevs[i].dev, d_config->pcidevs[i].func, d_config->pcidevs[i].vdevfn); fprintf(fh, "\t\t\t(opts msitranslate %d power_mgmt %d)\n", d_config->pcidevs[i].msitranslate, d_config->pcidevs[i].power_mgmt); fprintf(fh, "\t\t)\n"); fprintf(fh, "\t)\n"); } for (i = 0; i < d_config->num_vfbs; i++) { fprintf(fh, "\t(device\n"); fprintf(fh, "\t\t(vfb\n"); fprintf(fh, "\t\t\t(backend_domid %d)\n", d_config->vfbs[i].backend_domid); fprintf(fh, "\t\t\t(frontend_domid %d)\n", domid); fprintf(fh, "\t\t\t(devid %d)\n", d_config->vfbs[i].devid); fprintf(fh, "\t\t\t(vnc %s)\n", libxl_defbool_to_string(d_config->vfbs[i].vnc.enable)); fprintf(fh, "\t\t\t(vnclisten %s)\n", d_config->vfbs[i].vnc.listen); fprintf(fh, "\t\t\t(vncdisplay %d)\n", d_config->vfbs[i].vnc.display); fprintf(fh, "\t\t\t(vncunused %s)\n", libxl_defbool_to_string(d_config->vfbs[i].vnc.findunused)); fprintf(fh, "\t\t\t(keymap %s)\n", d_config->vfbs[i].keymap); fprintf(fh, "\t\t\t(sdl %s)\n", libxl_defbool_to_string(d_config->vfbs[i].sdl.enable)); fprintf(fh, "\t\t\t(opengl %s)\n", libxl_defbool_to_string(d_config->vfbs[i].sdl.opengl)); fprintf(fh, "\t\t\t(display %s)\n", d_config->vfbs[i].sdl.display); fprintf(fh, "\t\t\t(xauthority %s)\n", d_config->vfbs[i].sdl.xauthority); fprintf(fh, "\t\t)\n"); fprintf(fh, "\t)\n"); } fprintf(fh, ")\n"); } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_sched.c0000664000175000017500000006400213256712137014177 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" static int sched_domain_get(libxl_scheduler sched, int domid, libxl_domain_sched_params *scinfo) { if (libxl_domain_sched_params_get(ctx, domid, scinfo)) { fprintf(stderr, "libxl_domain_sched_params_get failed.\n"); return 1; } if (scinfo->sched != sched) { fprintf(stderr, "libxl_domain_sched_params_get returned %s not %s.\n", libxl_scheduler_to_string(scinfo->sched), libxl_scheduler_to_string(sched)); return 1; } return 0; } static int sched_domain_set(int domid, const libxl_domain_sched_params *scinfo) { if (libxl_domain_sched_params_set(ctx, domid, scinfo)) { fprintf(stderr, "libxl_domain_sched_params_set failed.\n"); return 1; } return 0; } static int sched_vcpu_get(libxl_scheduler sched, int domid, libxl_vcpu_sched_params *scinfo) { int rc; rc = libxl_vcpu_sched_params_get(ctx, domid, scinfo); if (rc) { fprintf(stderr, "libxl_vcpu_sched_params_get failed.\n"); exit(EXIT_FAILURE); } if (scinfo->sched != sched) { fprintf(stderr, "libxl_vcpu_sched_params_get returned %s not %s.\n", libxl_scheduler_to_string(scinfo->sched), libxl_scheduler_to_string(sched)); return 1; } return 0; } static int sched_vcpu_get_all(libxl_scheduler sched, int domid, libxl_vcpu_sched_params *scinfo) { int rc; rc = libxl_vcpu_sched_params_get_all(ctx, domid, scinfo); if (rc) { fprintf(stderr, "libxl_vcpu_sched_params_get_all failed.\n"); exit(EXIT_FAILURE); } if (scinfo->sched != sched) { fprintf(stderr, "libxl_vcpu_sched_params_get_all returned %s not %s.\n", libxl_scheduler_to_string(scinfo->sched), libxl_scheduler_to_string(sched)); return 1; } return 0; } static int sched_vcpu_set(int domid, const libxl_vcpu_sched_params *scinfo) { int rc; rc = libxl_vcpu_sched_params_set(ctx, domid, scinfo); if (rc) { fprintf(stderr, "libxl_vcpu_sched_params_set failed.\n"); exit(EXIT_FAILURE); } return 0; } static int sched_vcpu_set_all(int domid, const libxl_vcpu_sched_params *scinfo) { int rc; rc = libxl_vcpu_sched_params_set_all(ctx, domid, scinfo); if (rc) { fprintf(stderr, "libxl_vcpu_sched_params_set_all failed.\n"); exit(EXIT_FAILURE); } return 0; } static int sched_credit_params_set(int poolid, libxl_sched_credit_params *scinfo) { if (libxl_sched_credit_params_set(ctx, poolid, scinfo)) { fprintf(stderr, "libxl_sched_credit_params_set failed.\n"); return 1; } return 0; } static int sched_credit_params_get(int poolid, libxl_sched_credit_params *scinfo) { if (libxl_sched_credit_params_get(ctx, poolid, scinfo)) { fprintf(stderr, "libxl_sched_credit_params_get failed.\n"); return 1; } return 0; } static int sched_credit_domain_output(int domid) { char *domname; libxl_domain_sched_params scinfo; if (domid < 0) { printf("%-33s %4s %6s %4s\n", "Name", "ID", "Weight", "Cap"); return 0; } libxl_domain_sched_params_init(&scinfo); if (sched_domain_get(LIBXL_SCHEDULER_CREDIT, domid, &scinfo)) { libxl_domain_sched_params_dispose(&scinfo); return 1; } domname = libxl_domid_to_name(ctx, domid); printf("%-33s %4d %6d %4d\n", domname, domid, scinfo.weight, scinfo.cap); free(domname); libxl_domain_sched_params_dispose(&scinfo); return 0; } static int sched_credit_pool_output(uint32_t poolid) { libxl_sched_credit_params scparam; char *poolname; poolname = libxl_cpupoolid_to_name(ctx, poolid); if (sched_credit_params_get(poolid, &scparam)) { printf("Cpupool %s: [sched params unavailable]\n", poolname); } else { printf("Cpupool %s: tslice=%dms ratelimit=%dus\n", poolname, scparam.tslice_ms, scparam.ratelimit_us); } free(poolname); return 0; } static int sched_credit2_params_set(int poolid, libxl_sched_credit2_params *scinfo) { if (libxl_sched_credit2_params_set(ctx, poolid, scinfo)) { fprintf(stderr, "libxl_sched_credit2_params_set failed.\n"); return 1; } return 0; } static int sched_credit2_params_get(int poolid, libxl_sched_credit2_params *scinfo) { if (libxl_sched_credit2_params_get(ctx, poolid, scinfo)) { fprintf(stderr, "libxl_sched_credit2_params_get failed.\n"); return 1; } return 0; } static int sched_credit2_domain_output(int domid) { char *domname; libxl_domain_sched_params scinfo; if (domid < 0) { printf("%-33s %4s %6s\n", "Name", "ID", "Weight"); return 0; } libxl_domain_sched_params_init(&scinfo); if (sched_domain_get(LIBXL_SCHEDULER_CREDIT2, domid, &scinfo)) { libxl_domain_sched_params_dispose(&scinfo); return 1; } domname = libxl_domid_to_name(ctx, domid); printf("%-33s %4d %6d\n", domname, domid, scinfo.weight); free(domname); libxl_domain_sched_params_dispose(&scinfo); return 0; } static int sched_credit2_pool_output(uint32_t poolid) { libxl_sched_credit2_params scparam; char *poolname = libxl_cpupoolid_to_name(ctx, poolid); if (sched_credit2_params_get(poolid, &scparam)) printf("Cpupool %s: [sched params unavailable]\n", poolname); else printf("Cpupool %s: ratelimit=%dus\n", poolname, scparam.ratelimit_us); free(poolname); return 0; } static int sched_rtds_domain_output( int domid) { char *domname; libxl_domain_sched_params scinfo; if (domid < 0) { printf("%-33s %4s %9s %9s\n", "Name", "ID", "Period", "Budget"); return 0; } libxl_domain_sched_params_init(&scinfo); if (sched_domain_get(LIBXL_SCHEDULER_RTDS, domid, &scinfo)) { libxl_domain_sched_params_dispose(&scinfo); return 1; } domname = libxl_domid_to_name(ctx, domid); printf("%-33s %4d %9d %9d\n", domname, domid, scinfo.period, scinfo.budget); free(domname); libxl_domain_sched_params_dispose(&scinfo); return 0; } static int sched_rtds_vcpu_output(int domid, libxl_vcpu_sched_params *scinfo) { char *domname; int rc = 0; int i; if (domid < 0) { printf("%-33s %4s %4s %9s %9s\n", "Name", "ID", "VCPU", "Period", "Budget"); return 0; } rc = sched_vcpu_get(LIBXL_SCHEDULER_RTDS, domid, scinfo); if (rc) return 1; domname = libxl_domid_to_name(ctx, domid); for ( i = 0; i < scinfo->num_vcpus; i++ ) { printf("%-33s %4d %4d %9"PRIu32" %9"PRIu32"\n", domname, domid, scinfo->vcpus[i].vcpuid, scinfo->vcpus[i].period, scinfo->vcpus[i].budget); } free(domname); return 0; } static int sched_rtds_vcpu_output_all(int domid, libxl_vcpu_sched_params *scinfo) { char *domname; int rc = 0; int i; if (domid < 0) { printf("%-33s %4s %4s %9s %9s\n", "Name", "ID", "VCPU", "Period", "Budget"); return 0; } scinfo->num_vcpus = 0; rc = sched_vcpu_get_all(LIBXL_SCHEDULER_RTDS, domid, scinfo); if (rc) return 1; domname = libxl_domid_to_name(ctx, domid); for ( i = 0; i < scinfo->num_vcpus; i++ ) { printf("%-33s %4d %4d %9"PRIu32" %9"PRIu32"\n", domname, domid, scinfo->vcpus[i].vcpuid, scinfo->vcpus[i].period, scinfo->vcpus[i].budget); } free(domname); return 0; } static int sched_rtds_pool_output(uint32_t poolid) { char *poolname; poolname = libxl_cpupoolid_to_name(ctx, poolid); printf("Cpupool %s: sched=RTDS\n", poolname); free(poolname); return 0; } static int sched_domain_output(libxl_scheduler sched, int (*output)(int), int (*pooloutput)(uint32_t), const char *cpupool) { libxl_dominfo *info; libxl_cpupoolinfo *poolinfo = NULL; uint32_t poolid; int nb_domain, n_pools = 0, i, p; int rc = 0; if (cpupool) { if (libxl_cpupool_qualifier_to_cpupoolid(ctx, cpupool, &poolid, NULL) || !libxl_cpupoolid_is_valid(ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", cpupool); return 1; } } info = libxl_list_domain(ctx, &nb_domain); if (!info) { fprintf(stderr, "libxl_list_domain failed.\n"); return 1; } poolinfo = libxl_list_cpupool(ctx, &n_pools); if (!poolinfo) { fprintf(stderr, "error getting cpupool info\n"); libxl_dominfo_list_free(info, nb_domain); return 1; } for (p = 0; !rc && (p < n_pools); p++) { if ((poolinfo[p].sched != sched) || (cpupool && (poolid != poolinfo[p].poolid))) continue; pooloutput(poolinfo[p].poolid); output(-1); for (i = 0; i < nb_domain; i++) { if (info[i].cpupool != poolinfo[p].poolid) continue; rc = output(info[i].domid); if (rc) break; } } libxl_cpupoolinfo_list_free(poolinfo, n_pools); libxl_dominfo_list_free(info, nb_domain); return 0; } static int sched_vcpu_output(libxl_scheduler sched, int (*output)(int, libxl_vcpu_sched_params *), int (*pooloutput)(uint32_t), const char *cpupool) { libxl_dominfo *info; libxl_cpupoolinfo *poolinfo = NULL; uint32_t poolid; int nb_domain, n_pools = 0, i, p; int rc = 0; if (cpupool) { if (libxl_cpupool_qualifier_to_cpupoolid(ctx, cpupool, &poolid, NULL) || !libxl_cpupoolid_is_valid(ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", cpupool); return 1; } } info = libxl_list_domain(ctx, &nb_domain); if (!info) { fprintf(stderr, "libxl_list_domain failed.\n"); return 1; } poolinfo = libxl_list_cpupool(ctx, &n_pools); if (!poolinfo) { fprintf(stderr, "error getting cpupool info\n"); libxl_dominfo_list_free(info, nb_domain); return 1; } for (p = 0; !rc && (p < n_pools); p++) { if ((poolinfo[p].sched != sched) || (cpupool && (poolid != poolinfo[p].poolid))) continue; pooloutput(poolinfo[p].poolid); output(-1, NULL); for (i = 0; i < nb_domain; i++) { libxl_vcpu_sched_params scinfo; if (info[i].cpupool != poolinfo[p].poolid) continue; libxl_vcpu_sched_params_init(&scinfo); rc = output(info[i].domid, &scinfo); libxl_vcpu_sched_params_dispose(&scinfo); if (rc) break; } } libxl_cpupoolinfo_list_free(poolinfo, n_pools); libxl_dominfo_list_free(info, nb_domain); return 0; } /* * : List all domain params and sched params from all pools * -d [domid] : List domain params for domain * -d [domid] [params] : Set domain params for domain * -p [pool] : list all domains and sched params for pool * -s : List sched params for poolid 0 * -s [params] : Set sched params for poolid 0 * -p [pool] -s : List sched params for pool * -p [pool] -s [params] : Set sched params for pool * -p [pool] -d... : Illegal */ int main_sched_credit(int argc, char **argv) { const char *dom = NULL; const char *cpupool = NULL; int weight = 256, cap = 0; int tslice = 0, ratelimit = 0; bool opt_w = false, opt_c = false; bool opt_t = false, opt_r = false; bool opt_s = false; int opt, rc; static struct option opts[] = { {"domain", 1, 0, 'd'}, {"weight", 1, 0, 'w'}, {"cap", 1, 0, 'c'}, {"schedparam", 0, 0, 's'}, {"tslice_ms", 1, 0, 't'}, {"ratelimit_us", 1, 0, 'r'}, {"cpupool", 1, 0, 'p'}, COMMON_LONG_OPTS }; SWITCH_FOREACH_OPT(opt, "d:w:c:p:t:r:s", opts, "sched-credit", 0) { case 'd': dom = optarg; break; case 'w': weight = strtol(optarg, NULL, 10); opt_w = true; break; case 'c': cap = strtol(optarg, NULL, 10); opt_c = true; break; case 't': tslice = strtol(optarg, NULL, 10); opt_t = true; break; case 'r': ratelimit = strtol(optarg, NULL, 10); opt_r = true; break; case 's': opt_s = true; break; case 'p': cpupool = optarg; break; } if ((cpupool || opt_s) && (dom || opt_w || opt_c)) { fprintf(stderr, "Specifying a cpupool or schedparam is not " "allowed with domain options.\n"); return EXIT_FAILURE; } if (!dom && (opt_w || opt_c)) { fprintf(stderr, "Must specify a domain.\n"); return EXIT_FAILURE; } if (!opt_s && (opt_t || opt_r)) { fprintf(stderr, "Must specify schedparam to set schedule " "parameter values.\n"); return EXIT_FAILURE; } if (opt_s) { libxl_sched_credit_params scparam; uint32_t poolid = 0; if (cpupool) { if (libxl_cpupool_qualifier_to_cpupoolid(ctx, cpupool, &poolid, NULL) || !libxl_cpupoolid_is_valid(ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", cpupool); return EXIT_FAILURE; } } if (!opt_t && !opt_r) { /* Output scheduling parameters */ if (sched_credit_pool_output(poolid)) return EXIT_FAILURE; } else { /* Set scheduling parameters*/ if (sched_credit_params_get(poolid, &scparam)) return EXIT_FAILURE; if (opt_t) scparam.tslice_ms = tslice; if (opt_r) scparam.ratelimit_us = ratelimit; if (sched_credit_params_set(poolid, &scparam)) return EXIT_FAILURE; } } else if (!dom) { /* list all domain's credit scheduler info */ if (sched_domain_output(LIBXL_SCHEDULER_CREDIT, sched_credit_domain_output, sched_credit_pool_output, cpupool)) return EXIT_FAILURE; } else { uint32_t domid = find_domain(dom); if (!opt_w && !opt_c) { /* output credit scheduler info */ sched_credit_domain_output(-1); if (sched_credit_domain_output(domid)) return EXIT_FAILURE; } else { /* set credit scheduler paramaters */ libxl_domain_sched_params scinfo; libxl_domain_sched_params_init(&scinfo); scinfo.sched = LIBXL_SCHEDULER_CREDIT; if (opt_w) scinfo.weight = weight; if (opt_c) scinfo.cap = cap; rc = sched_domain_set(domid, &scinfo); libxl_domain_sched_params_dispose(&scinfo); if (rc) return EXIT_FAILURE; } } return EXIT_SUCCESS; } int main_sched_credit2(int argc, char **argv) { const char *dom = NULL; const char *cpupool = NULL; int ratelimit = 0; int weight = 256; bool opt_s = false; bool opt_r = false; bool opt_w = false; int opt, rc; static struct option opts[] = { {"domain", 1, 0, 'd'}, {"weight", 1, 0, 'w'}, {"schedparam", 0, 0, 's'}, {"ratelimit_us", 1, 0, 'r'}, {"cpupool", 1, 0, 'p'}, COMMON_LONG_OPTS }; SWITCH_FOREACH_OPT(opt, "d:w:p:r:s", opts, "sched-credit2", 0) { case 'd': dom = optarg; break; case 'w': weight = strtol(optarg, NULL, 10); opt_w = true; break; case 's': opt_s = true; break; case 'r': ratelimit = strtol(optarg, NULL, 10); opt_r = true; break; case 'p': cpupool = optarg; break; } if (cpupool && (dom || opt_w)) { fprintf(stderr, "Specifying a cpupool is not allowed with other " "options.\n"); return EXIT_FAILURE; } if (!dom && opt_w) { fprintf(stderr, "Must specify a domain.\n"); return EXIT_FAILURE; } if (opt_s) { libxl_sched_credit2_params scparam; uint32_t poolid = 0; if (cpupool) { if (libxl_cpupool_qualifier_to_cpupoolid(ctx, cpupool, &poolid, NULL) || !libxl_cpupoolid_is_valid(ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", cpupool); return EXIT_FAILURE; } } if (!opt_r) { /* Output scheduling parameters */ if (sched_credit2_pool_output(poolid)) return EXIT_FAILURE; } else { /* Set scheduling parameters (so far, just ratelimit) */ scparam.ratelimit_us = ratelimit; if (sched_credit2_params_set(poolid, &scparam)) return EXIT_FAILURE; } } else if (!dom) { /* list all domain's credit scheduler info */ if (sched_domain_output(LIBXL_SCHEDULER_CREDIT2, sched_credit2_domain_output, sched_credit2_pool_output, cpupool)) return EXIT_FAILURE; } else { uint32_t domid = find_domain(dom); if (!opt_w) { /* output credit2 scheduler info */ sched_credit2_domain_output(-1); if (sched_credit2_domain_output(domid)) return EXIT_FAILURE; } else { /* set credit2 scheduler paramaters */ libxl_domain_sched_params scinfo; libxl_domain_sched_params_init(&scinfo); scinfo.sched = LIBXL_SCHEDULER_CREDIT2; if (opt_w) scinfo.weight = weight; rc = sched_domain_set(domid, &scinfo); libxl_domain_sched_params_dispose(&scinfo); if (rc) return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* * : List all domain paramters and sched params * -d [domid] : List default domain params for domain * -d [domid] [params] : Set domain params for domain * -d [domid] -v [vcpuid 1] -v [vcpuid 2] ... : * List per-VCPU params for domain * -d [domid] -v all : List all per-VCPU params for domain * -v all : List all per-VCPU params for all domains * -d [domid] -v [vcpuid 1] [params] -v [vcpuid 2] [params] ... : * Set per-VCPU params for domain * -d [domid] -v all [params] : Set all per-VCPU params for domain */ int main_sched_rtds(int argc, char **argv) { const char *dom = NULL; const char *cpupool = NULL; int *vcpus = (int *)xmalloc(sizeof(int)); /* IDs of VCPUs that change */ int *periods = (int *)xmalloc(sizeof(int)); /* period is in microsecond */ int *budgets = (int *)xmalloc(sizeof(int)); /* budget is in microsecond */ int v_size = 1; /* size of vcpus array */ int p_size = 1; /* size of periods array */ int b_size = 1; /* size of budgets array */ int v_index = 0; /* index in vcpus array */ int p_index =0; /* index in periods array */ int b_index =0; /* index for in budgets array */ bool opt_p = false; bool opt_b = false; bool opt_v = false; bool opt_all = false; /* output per-dom parameters */ int opt, i, rc, r; static struct option opts[] = { {"domain", 1, 0, 'd'}, {"period", 1, 0, 'p'}, {"budget", 1, 0, 'b'}, {"vcpuid",1, 0, 'v'}, {"cpupool", 1, 0, 'c'}, COMMON_LONG_OPTS }; SWITCH_FOREACH_OPT(opt, "d:p:b:v:c", opts, "sched-rtds", 0) { case 'd': dom = optarg; break; case 'p': if (p_index >= p_size) { /* * periods array is full * double the array size for new elements */ p_size *= 2; periods = xrealloc(periods, p_size); } periods[p_index++] = strtol(optarg, NULL, 10); opt_p = 1; break; case 'b': if (b_index >= b_size) { /* budgets array is full */ b_size *= 2; budgets = xrealloc(budgets, b_size); } budgets[b_index++] = strtol(optarg, NULL, 10); opt_b = 1; break; case 'v': if (!strcmp(optarg, "all")) { /* get or set all vcpus of a domain */ opt_all = 1; break; } if (v_index >= v_size) { /* vcpus array is full */ v_size *= 2; vcpus = xrealloc(vcpus, v_size); } vcpus[v_index++] = strtol(optarg, NULL, 10); opt_v = 1; break; case 'c': cpupool = optarg; break; } if (cpupool && (dom || opt_p || opt_b || opt_v || opt_all)) { fprintf(stderr, "Specifying a cpupool is not allowed with " "other options.\n"); r = EXIT_FAILURE; goto out; } if (!dom && (opt_p || opt_b || opt_v)) { fprintf(stderr, "Missing parameters.\n"); r = EXIT_FAILURE; goto out; } if (dom && !opt_v && !opt_all && (opt_p || opt_b)) { fprintf(stderr, "Must specify VCPU.\n"); r = EXIT_FAILURE; goto out; } if (opt_v && opt_all) { fprintf(stderr, "Incorrect VCPU IDs.\n"); r = EXIT_FAILURE; goto out; } if (((v_index > b_index) && opt_b) || ((v_index > p_index) && opt_p) || p_index != b_index) { fprintf(stderr, "Incorrect number of period and budget\n"); r = EXIT_FAILURE; goto out; } if ((!dom) && opt_all) { /* get all domain's per-vcpu rtds scheduler parameters */ rc = -sched_vcpu_output(LIBXL_SCHEDULER_RTDS, sched_rtds_vcpu_output_all, sched_rtds_pool_output, cpupool); if (rc) { r = EXIT_FAILURE; goto out; } } else if (!dom && !opt_all) { /* list all domain's default scheduling parameters */ rc = -sched_domain_output(LIBXL_SCHEDULER_RTDS, sched_rtds_domain_output, sched_rtds_pool_output, cpupool); if (rc) { r = EXIT_FAILURE; goto out; } } else { uint32_t domid = find_domain(dom); if (!opt_v && !opt_all) { /* output default scheduling parameters */ sched_rtds_domain_output(-1); rc = -sched_rtds_domain_output(domid); if (rc) { r = EXIT_FAILURE; goto out; } } else if (!opt_p && !opt_b) { /* get per-vcpu rtds scheduling parameters */ libxl_vcpu_sched_params scinfo; libxl_vcpu_sched_params_init(&scinfo); sched_rtds_vcpu_output(-1, &scinfo); scinfo.num_vcpus = v_index; if (v_index > 0) { scinfo.vcpus = (libxl_sched_params *) xmalloc(sizeof(libxl_sched_params) * (v_index)); for (i = 0; i < v_index; i++) scinfo.vcpus[i].vcpuid = vcpus[i]; rc = -sched_rtds_vcpu_output(domid, &scinfo); } else /* get params for all vcpus */ rc = -sched_rtds_vcpu_output_all(domid, &scinfo); libxl_vcpu_sched_params_dispose(&scinfo); if (rc) { r = EXIT_FAILURE; goto out; } } else if (opt_v || opt_all) { /* set per-vcpu rtds scheduling parameters */ libxl_vcpu_sched_params scinfo; libxl_vcpu_sched_params_init(&scinfo); scinfo.sched = LIBXL_SCHEDULER_RTDS; if (v_index > 0) { scinfo.num_vcpus = v_index; scinfo.vcpus = (libxl_sched_params *) xmalloc(sizeof(libxl_sched_params) * (v_index)); for (i = 0; i < v_index; i++) { scinfo.vcpus[i].vcpuid = vcpus[i]; scinfo.vcpus[i].period = periods[i]; scinfo.vcpus[i].budget = budgets[i]; } rc = sched_vcpu_set(domid, &scinfo); } else { /* set params for all vcpus */ scinfo.num_vcpus = 1; scinfo.vcpus = (libxl_sched_params *) xmalloc(sizeof(libxl_sched_params)); scinfo.vcpus[0].period = periods[0]; scinfo.vcpus[0].budget = budgets[0]; rc = sched_vcpu_set_all(domid, &scinfo); } libxl_vcpu_sched_params_dispose(&scinfo); if (rc) { r = EXIT_FAILURE; goto out; } } } r = EXIT_SUCCESS; out: free(vcpus); free(periods); free(budgets); return r; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_vmcontrol.c0000664000175000017500000011006413256712137015134 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" static int fd_lock = -1; static void pause_domain(uint32_t domid) { libxl_domain_pause(ctx, domid); } static void unpause_domain(uint32_t domid) { libxl_domain_unpause(ctx, domid); } static void destroy_domain(uint32_t domid, int force) { int rc; if (domid == 0 && !force) { fprintf(stderr, "Not destroying domain 0; use -f to force.\n" "This can only be done when using a disaggregated " "hardware domain and toolstack.\n\n"); exit(EXIT_FAILURE); } rc = libxl_domain_destroy(ctx, domid, 0); if (rc) { fprintf(stderr,"destroy failed (rc=%d)\n",rc); exit(EXIT_FAILURE); } } int main_pause(int argc, char **argv) { int opt; SWITCH_FOREACH_OPT(opt, "", NULL, "pause", 1) { /* No options */ } pause_domain(find_domain(argv[optind])); return EXIT_SUCCESS; } int main_unpause(int argc, char **argv) { int opt; SWITCH_FOREACH_OPT(opt, "", NULL, "unpause", 1) { /* No options */ } unpause_domain(find_domain(argv[optind])); return EXIT_SUCCESS; } int main_destroy(int argc, char **argv) { int opt; int force = 0; SWITCH_FOREACH_OPT(opt, "f", NULL, "destroy", 1) { case 'f': force = 1; break; } destroy_domain(find_domain(argv[optind]), force); return EXIT_SUCCESS; } static void reboot_domain(uint32_t domid, libxl_evgen_domain_death **deathw, libxl_ev_user for_user, int fallback_trigger) { int rc; fprintf(stderr, "Rebooting domain %u\n", domid); rc=libxl_domain_reboot(ctx, domid); if (rc == ERROR_NOPARAVIRT) { if (fallback_trigger) { fprintf(stderr, "PV control interface not available:" " sending ACPI reset button event.\n"); rc = libxl_send_trigger(ctx, domid, LIBXL_TRIGGER_RESET, 0); } else { fprintf(stderr, "PV control interface not available:" " external graceful reboot not possible.\n"); fprintf(stderr, "Use \"-F\" to fallback to ACPI reset event.\n"); } } if (rc) { fprintf(stderr,"reboot failed (rc=%d)\n",rc);exit(EXIT_FAILURE); } if (deathw) { rc = libxl_evenable_domain_death(ctx, domid, for_user, deathw); if (rc) { fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc); exit(EXIT_FAILURE); } } } static void shutdown_domain(uint32_t domid, libxl_evgen_domain_death **deathw, libxl_ev_user for_user, int fallback_trigger) { int rc; fprintf(stderr, "Shutting down domain %u\n", domid); rc=libxl_domain_shutdown(ctx, domid); if (rc == ERROR_NOPARAVIRT) { if (fallback_trigger) { fprintf(stderr, "PV control interface not available:" " sending ACPI power button event.\n"); rc = libxl_send_trigger(ctx, domid, LIBXL_TRIGGER_POWER, 0); } else { fprintf(stderr, "PV control interface not available:" " external graceful shutdown not possible.\n"); fprintf(stderr, "Use \"-F\" to fallback to ACPI power event.\n"); } } if (rc) { fprintf(stderr,"shutdown failed (rc=%d)\n",rc);exit(EXIT_FAILURE); } if (deathw) { rc = libxl_evenable_domain_death(ctx, domid, for_user, deathw); if (rc) { fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc); exit(EXIT_FAILURE); } } } static void wait_for_domain_deaths(libxl_evgen_domain_death **deathws, int nr) { int rc, count = 0; LOG("Waiting for %d domains", nr); while(1 && count < nr) { libxl_event *event; rc = libxl_event_wait(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0); if (rc) { LOG("Failed to get event, quitting (rc=%d)", rc); exit(EXIT_FAILURE); } switch (event->type) { case LIBXL_EVENT_TYPE_DOMAIN_DEATH: LOG("Domain %d has been destroyed", event->domid); libxl_evdisable_domain_death(ctx, deathws[event->for_user]); count++; break; case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN: LOG("Domain %d has been shut down, reason code %d", event->domid, event->u.domain_shutdown.shutdown_reason); libxl_evdisable_domain_death(ctx, deathws[event->for_user]); count++; break; default: LOG("Unexpected event type %d", event->type); break; } libxl_event_free(ctx, event); } } static int main_shutdown_or_reboot(int do_reboot, int argc, char **argv) { const char *what = do_reboot ? "reboot" : "shutdown"; void (*fn)(uint32_t domid, libxl_evgen_domain_death **, libxl_ev_user, int) = do_reboot ? &reboot_domain : &shutdown_domain; int opt, i, nb_domain; int wait_for_it = 0, all = 0, nrdeathws = 0; int fallback_trigger = 0; static struct option opts[] = { {"all", 0, 0, 'a'}, {"wait", 0, 0, 'w'}, COMMON_LONG_OPTS }; SWITCH_FOREACH_OPT(opt, "awF", opts, what, 0) { case 'a': all = 1; break; case 'w': wait_for_it = 1; break; case 'F': fallback_trigger = 1; break; } if (!argv[optind] && !all) { fprintf(stderr, "You must specify -a or a domain id.\n\n"); return EXIT_FAILURE; } if (all) { libxl_dominfo *dominfo; libxl_evgen_domain_death **deathws = NULL; if (!(dominfo = libxl_list_domain(ctx, &nb_domain))) { fprintf(stderr, "libxl_list_domain failed.\n"); return EXIT_FAILURE; } if (wait_for_it) deathws = calloc(nb_domain, sizeof(*deathws)); for (i = 0; idomid != domid) { char *evstr = libxl_event_to_json(ctx, *event_r); LOG("INTERNAL PROBLEM - ignoring unexpected event for" " domain %d (expected %d): event=%s", (*event_r)->domid, domid, evstr); free(evstr); libxl_event_free(ctx, *event_r); continue; } return ret; } } /* * Returns false if memory can't be freed, but also if we encounter errors. * Returns true in case there is already, or we manage to free it, enough * memory, but also if autoballoon is false. */ static bool freemem(uint32_t domid, libxl_domain_build_info *b_info) { int rc, retries = 3; uint64_t need_memkb, free_memkb; if (!autoballoon) return true; rc = libxl_domain_need_memory(ctx, b_info, &need_memkb); if (rc < 0) return false; do { rc = libxl_get_free_memory(ctx, &free_memkb); if (rc < 0) return false; if (free_memkb >= need_memkb) return true; rc = libxl_set_memory_target(ctx, 0, free_memkb - need_memkb, 1, 0); if (rc < 0) return false; /* wait until dom0 reaches its target, as long as we are making * progress */ rc = libxl_wait_for_memory_target(ctx, 0, 10); if (rc < 0) return false; retries--; } while (retries > 0); return false; } static void reload_domain_config(uint32_t domid, libxl_domain_config *d_config) { int rc; uint8_t *t_data; int ret, t_len; libxl_domain_config d_config_new; /* In case user has used "config-update" to store a new config * file. */ ret = libxl_userdata_retrieve(ctx, domid, "xl", &t_data, &t_len); if (ret && errno != ENOENT) { LOG("\"xl\" configuration found but failed to load\n"); } if (t_len > 0) { LOG("\"xl\" configuration found, using it\n"); libxl_domain_config_dispose(d_config); libxl_domain_config_init(d_config); parse_config_data("", (const char *)t_data, t_len, d_config); free(t_data); libxl_userdata_unlink(ctx, domid, "xl"); return; } libxl_domain_config_init(&d_config_new); rc = libxl_retrieve_domain_configuration(ctx, domid, &d_config_new); if (rc) { LOG("failed to retrieve guest configuration (rc=%d). " "reusing old configuration", rc); libxl_domain_config_dispose(&d_config_new); } else { libxl_domain_config_dispose(d_config); /* Steal allocations */ memcpy(d_config, &d_config_new, sizeof(libxl_domain_config)); } } /* Can update r_domid if domain is destroyed */ static domain_restart_type handle_domain_death(uint32_t *r_domid, libxl_event *event, libxl_domain_config *d_config) { domain_restart_type restart = DOMAIN_RESTART_NONE; libxl_action_on_shutdown action; switch (event->u.domain_shutdown.shutdown_reason) { case LIBXL_SHUTDOWN_REASON_POWEROFF: action = d_config->on_poweroff; break; case LIBXL_SHUTDOWN_REASON_REBOOT: action = d_config->on_reboot; break; case LIBXL_SHUTDOWN_REASON_SUSPEND: LOG("Domain has suspended."); return 0; case LIBXL_SHUTDOWN_REASON_CRASH: action = d_config->on_crash; break; case LIBXL_SHUTDOWN_REASON_WATCHDOG: action = d_config->on_watchdog; break; case LIBXL_SHUTDOWN_REASON_SOFT_RESET: action = d_config->on_soft_reset; break; default: LOG("Unknown shutdown reason code %d. Destroying domain.", event->u.domain_shutdown.shutdown_reason); action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY; } LOG("Action for shutdown reason code %d is %s", event->u.domain_shutdown.shutdown_reason, get_action_on_shutdown_name(action)); if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY || action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART) { char *corefile; int rc; xasprintf(&corefile, XEN_DUMP_DIR "/%s", d_config->c_info.name); LOG("dumping core to %s", corefile); rc = libxl_domain_core_dump(ctx, *r_domid, corefile, NULL); if (rc) LOG("core dump failed (rc=%d).", rc); free(corefile); /* No point crying over spilled milk, continue on failure. */ if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY) action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY; else action = LIBXL_ACTION_ON_SHUTDOWN_RESTART; } switch (action) { case LIBXL_ACTION_ON_SHUTDOWN_PRESERVE: break; case LIBXL_ACTION_ON_SHUTDOWN_RESTART_RENAME: reload_domain_config(*r_domid, d_config); restart = DOMAIN_RESTART_RENAME; break; case LIBXL_ACTION_ON_SHUTDOWN_RESTART: reload_domain_config(*r_domid, d_config); restart = DOMAIN_RESTART_NORMAL; /* fall-through */ case LIBXL_ACTION_ON_SHUTDOWN_DESTROY: LOG("Domain %d needs to be cleaned up: destroying the domain", *r_domid); libxl_domain_destroy(ctx, *r_domid, 0); *r_domid = INVALID_DOMID; break; case LIBXL_ACTION_ON_SHUTDOWN_SOFT_RESET: reload_domain_config(*r_domid, d_config); restart = DOMAIN_RESTART_SOFT_RESET; break; case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY: case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART: /* Already handled these above. */ abort(); } return restart; } /* Preserve a copy of a domain under a new name. Updates *r_domid */ static int preserve_domain(uint32_t *r_domid, libxl_event *event, libxl_domain_config *d_config) { time_t now; struct tm tm; char strtime[24]; libxl_uuid new_uuid; int rc; now = time(NULL); if (now == ((time_t) -1)) { LOG("Failed to get current time for domain rename"); return 0; } tzset(); if (gmtime_r(&now, &tm) == NULL) { LOG("Failed to convert time to UTC"); return 0; } if (!strftime(&strtime[0], sizeof(strtime), "-%Y%m%dT%H%MZ", &tm)) { LOG("Failed to format time as a string"); return 0; } libxl_uuid_generate(&new_uuid); LOG("Preserving domain %u %s with suffix%s", *r_domid, d_config->c_info.name, strtime); rc = libxl_domain_preserve(ctx, *r_domid, &d_config->c_info, strtime, new_uuid); /* * Although the domain still exists it is no longer the one we are * concerned with. */ *r_domid = INVALID_DOMID; return rc == 0 ? 1 : 0; } static void console_child_report(xlchildnum child) { if (xl_child_pid(child)) child_report(child); } static int vncviewer(uint32_t domid, int autopass) { libxl_vncviewer_exec(ctx, domid, autopass); fprintf(stderr, "Unable to execute vncviewer\n"); return 1; } static void autoconnect_vncviewer(uint32_t domid, int autopass) { console_child_report(child_vncviewer); pid_t pid = xl_fork(child_vncviewer, "vncviewer child"); if (pid) return; postfork(); sleep(1); vncviewer(domid, autopass); _exit(EXIT_FAILURE); } static int acquire_lock(void) { int rc; struct flock fl; /* lock already acquired */ if (fd_lock >= 0) return ERROR_INVAL; fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; fd_lock = open(lockfile, O_WRONLY|O_CREAT, S_IWUSR); if (fd_lock < 0) { fprintf(stderr, "cannot open the lockfile %s errno=%d\n", lockfile, errno); return ERROR_FAIL; } if (fcntl(fd_lock, F_SETFD, FD_CLOEXEC) < 0) { close(fd_lock); fprintf(stderr, "cannot set cloexec to lockfile %s errno=%d\n", lockfile, errno); return ERROR_FAIL; } get_lock: rc = fcntl(fd_lock, F_SETLKW, &fl); if (rc < 0 && errno == EINTR) goto get_lock; if (rc < 0) { fprintf(stderr, "cannot acquire lock %s errno=%d\n", lockfile, errno); rc = ERROR_FAIL; } else rc = 0; return rc; } static int release_lock(void) { int rc; struct flock fl; /* lock not acquired */ if (fd_lock < 0) return ERROR_INVAL; release_lock: fl.l_type = F_UNLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; rc = fcntl(fd_lock, F_SETLKW, &fl); if (rc < 0 && errno == EINTR) goto release_lock; if (rc < 0) { fprintf(stderr, "cannot release lock %s, errno=%d\n", lockfile, errno); rc = ERROR_FAIL; } else rc = 0; close(fd_lock); fd_lock = -1; return rc; } static void autoconnect_console(libxl_ctx *ctx_ignored, libxl_event *ev, void *priv) { uint32_t bldomid = ev->domid; int notify_fd = *(int*)priv; /* write end of the notification pipe */ libxl_event_free(ctx, ev); console_child_report(child_console); pid_t pid = xl_fork(child_console, "console child"); if (pid) return; postfork(); sleep(1); libxl_primary_console_exec(ctx, bldomid, notify_fd); /* Do not return. xl continued in child process */ perror("xl: unable to exec console client"); _exit(1); } int create_domain(struct domain_create *dom_info) { uint32_t domid = INVALID_DOMID; libxl_domain_config d_config; int debug = dom_info->debug; int daemonize = dom_info->daemonize; int monitor = dom_info->monitor; int paused = dom_info->paused; int vncautopass = dom_info->vncautopass; const char *config_file = dom_info->config_file; const char *extra_config = dom_info->extra_config; const char *restore_file = dom_info->restore_file; const char *config_source = NULL; const char *restore_source = NULL; int migrate_fd = dom_info->migrate_fd; bool config_in_json; int i; int need_daemon = daemonize; int ret, rc; libxl_evgen_domain_death *deathw = NULL; libxl_evgen_disk_eject **diskws = NULL; /* one per disk */ unsigned int num_diskws = 0; void *config_data = 0; int config_len = 0; int restore_fd = -1; int restore_fd_to_close = -1; int send_back_fd = -1; const libxl_asyncprogress_how *autoconnect_console_how; int notify_pipe[2] = { -1, -1 }; struct save_file_header hdr; uint32_t domid_soft_reset = INVALID_DOMID; int restoring = (restore_file || (migrate_fd >= 0)); libxl_domain_config_init(&d_config); if (restoring) { uint8_t *optdata_begin = 0; const uint8_t *optdata_here = 0; union { uint32_t u32; char b[4]; } u32buf; uint32_t badflags; if (migrate_fd >= 0) { restore_source = ""; restore_fd = migrate_fd; send_back_fd = dom_info->send_back_fd; } else { restore_source = restore_file; restore_fd = open(restore_file, O_RDONLY); if (restore_fd == -1) { fprintf(stderr, "Can't open restore file: %s\n", strerror(errno)); return ERROR_INVAL; } restore_fd_to_close = restore_fd; rc = libxl_fd_set_cloexec(ctx, restore_fd, 1); if (rc) return rc; } CHK_ERRNOVAL(libxl_read_exactly( ctx, restore_fd, &hdr, sizeof(hdr), restore_source, "header")); if (memcmp(hdr.magic, savefileheader_magic, sizeof(hdr.magic))) { fprintf(stderr, "File has wrong magic number -" " corrupt or for a different tool?\n"); return ERROR_INVAL; } if (hdr.byteorder != SAVEFILE_BYTEORDER_VALUE) { fprintf(stderr, "File has wrong byte order\n"); return ERROR_INVAL; } fprintf(stderr, "Loading new save file %s" " (new xl fmt info" " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n", restore_source, hdr.mandatory_flags, hdr.optional_flags, hdr.optional_data_len); badflags = hdr.mandatory_flags & ~XL_MANDATORY_FLAG_ALL; if (badflags) { fprintf(stderr, "Savefile has mandatory flag(s) 0x%"PRIx32" " "which are not supported; need newer xl\n", badflags); return ERROR_INVAL; } if (hdr.optional_data_len) { optdata_begin = xmalloc(hdr.optional_data_len); CHK_ERRNOVAL(libxl_read_exactly( ctx, restore_fd, optdata_begin, hdr.optional_data_len, restore_source, "optdata")); } #define OPTDATA_LEFT (hdr.optional_data_len - (optdata_here - optdata_begin)) #define WITH_OPTDATA(amt, body) \ if (OPTDATA_LEFT < (amt)) { \ fprintf(stderr, "Savefile truncated.\n"); \ return ERROR_INVAL; \ } else { \ body; \ optdata_here += (amt); \ } optdata_here = optdata_begin; if (OPTDATA_LEFT) { fprintf(stderr, " Savefile contains xl domain config%s\n", !!(hdr.mandatory_flags & XL_MANDATORY_FLAG_JSON) ? " in JSON format" : ""); WITH_OPTDATA(4, { memcpy(u32buf.b, optdata_here, 4); config_len = u32buf.u32; }); WITH_OPTDATA(config_len, { config_data = xmalloc(config_len); memcpy(config_data, optdata_here, config_len); }); } } if (config_file) { free(config_data); config_data = 0; /* /dev/null represents special case (read config. from command line) */ if (!strcmp(config_file, "/dev/null")) { config_len = 0; } else { ret = libxl_read_file_contents(ctx, config_file, &config_data, &config_len); if (ret) { fprintf(stderr, "Failed to read config file: %s: %s\n", config_file, strerror(errno)); return ERROR_FAIL; } } if (!restoring && extra_config && strlen(extra_config)) { if (config_len > INT_MAX - (strlen(extra_config) + 2 + 1)) { fprintf(stderr, "Failed to attach extra configuration\n"); return ERROR_FAIL; } /* allocate space for the extra config plus two EOLs plus \0 */ config_data = xrealloc(config_data, config_len + strlen(extra_config) + 2 + 1); config_len += sprintf(config_data + config_len, "\n%s\n", extra_config); } config_source=config_file; config_in_json = false; } else { if (!config_data) { fprintf(stderr, "Config file not specified and" " none in save file\n"); return ERROR_INVAL; } config_source = ""; config_in_json = !!(hdr.mandatory_flags & XL_MANDATORY_FLAG_JSON); } if (!dom_info->quiet) fprintf(stderr, "Parsing config from %s\n", config_source); if (config_in_json) { libxl_domain_config_from_json(ctx, &d_config, (const char *)config_data); } else { parse_config_data(config_source, config_data, config_len, &d_config); } if (migrate_fd >= 0) { if (d_config.c_info.name) { /* when we receive a domain we get its name from the config * file; and we receive it to a temporary name */ assert(!common_domname); common_domname = d_config.c_info.name; d_config.c_info.name = 0; /* steals allocation from config */ xasprintf(&d_config.c_info.name, "%s--incoming", common_domname); *dom_info->migration_domname_r = strdup(d_config.c_info.name); } } if (debug || dom_info->dryrun) { FILE *cfg_print_fh = (debug && !dom_info->dryrun) ? stderr : stdout; if (default_output_format == OUTPUT_FORMAT_SXP) { printf_info_sexp(-1, &d_config, cfg_print_fh); } else { char *json = libxl_domain_config_to_json(ctx, &d_config); if (!json) { fprintf(stderr, "Failed to convert domain configuration to JSON\n"); exit(1); } fputs(json, cfg_print_fh); free(json); flush_stream(cfg_print_fh); } } ret = 0; if (dom_info->dryrun) goto out; start: assert(domid == INVALID_DOMID); rc = acquire_lock(); if (rc < 0) goto error_out; if (domid_soft_reset == INVALID_DOMID) { if (!freemem(domid, &d_config.b_info)) { fprintf(stderr, "failed to free memory for the domain\n"); ret = ERROR_FAIL; goto error_out; } } libxl_asyncprogress_how autoconnect_console_how_buf; if ( dom_info->console_autoconnect ) { if (libxl_pipe(ctx, notify_pipe)) { ret = ERROR_FAIL; goto error_out; } autoconnect_console_how_buf.callback = autoconnect_console; autoconnect_console_how_buf.for_callback = ¬ify_pipe[1]; autoconnect_console_how = &autoconnect_console_how_buf; }else{ autoconnect_console_how = 0; } if ( restoring ) { libxl_domain_restore_params params; libxl_domain_restore_params_init(¶ms); params.checkpointed_stream = dom_info->checkpointed_stream; params.stream_version = (hdr.mandatory_flags & XL_MANDATORY_FLAG_STREAMv2) ? 2 : 1; params.colo_proxy_script = dom_info->colo_proxy_script; libxl_defbool_set(¶ms.userspace_colo_proxy, dom_info->userspace_colo_proxy); ret = libxl_domain_create_restore(ctx, &d_config, &domid, restore_fd, send_back_fd, ¶ms, 0, autoconnect_console_how); libxl_domain_restore_params_dispose(¶ms); /* * On subsequent reboot etc we should create the domain, not * restore/migrate-receive it again. */ restoring = 0; } else if (domid_soft_reset != INVALID_DOMID) { /* Do soft reset. */ ret = libxl_domain_soft_reset(ctx, &d_config, domid_soft_reset, 0, autoconnect_console_how); domid = domid_soft_reset; domid_soft_reset = INVALID_DOMID; } else { ret = libxl_domain_create_new(ctx, &d_config, &domid, 0, autoconnect_console_how); } if ( ret ) goto error_out; release_lock(); if (restore_fd_to_close >= 0) { if (close(restore_fd_to_close)) fprintf(stderr, "Failed to close restoring file, fd %d, errno %d\n", restore_fd_to_close, errno); restore_fd_to_close = -1; } if (autoconnect_console_how) { char buf[1]; int r; /* Try to get notification from xenconsole. Just move on if * error occurs -- it's only minor annoyance if console * doesn't show up. */ do { r = read(notify_pipe[0], buf, 1); } while (r == -1 && errno == EINTR); if (r == -1) fprintf(stderr, "Failed to get notification from xenconsole: %s\n", strerror(errno)); else if (r == 0) fprintf(stderr, "Got EOF from xenconsole notification fd\n"); else if (r == 1 && buf[0] != 0x00) fprintf(stderr, "Got unexpected response from xenconsole: %#x\n", buf[0]); close(notify_pipe[0]); close(notify_pipe[1]); notify_pipe[0] = notify_pipe[1] = -1; } if (!paused) libxl_domain_unpause(ctx, domid); ret = domid; /* caller gets success in parent */ if (!daemonize && !monitor) goto out; if (dom_info->vnc) autoconnect_vncviewer(domid, vncautopass); if (need_daemon) { char *name; xasprintf(&name, "xl-%s", d_config.c_info.name); ret = do_daemonize(name, NULL); free(name); if (ret) { ret = (ret == 1) ? domid : ret; goto out; } need_daemon = 0; } LOG("Waiting for domain %s (domid %u) to die [pid %ld]", d_config.c_info.name, domid, (long)getpid()); ret = libxl_evenable_domain_death(ctx, domid, 0, &deathw); if (ret) goto out; if (!diskws) { diskws = xmalloc(sizeof(*diskws) * d_config.num_disks); for (i = 0; i < d_config.num_disks; i++) diskws[i] = NULL; num_diskws = d_config.num_disks; } for (i = 0; i < num_diskws; i++) { if (d_config.disks[i].removable) { ret = libxl_evenable_disk_eject(ctx, domid, d_config.disks[i].vdev, 0, &diskws[i]); if (ret) goto out; } } while (1) { libxl_event *event; ret = domain_wait_event(domid, &event); if (ret) goto out; switch (event->type) { case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN: LOG("Domain %u has shut down, reason code %d 0x%x", domid, event->u.domain_shutdown.shutdown_reason, event->u.domain_shutdown.shutdown_reason); switch (handle_domain_death(&domid, event, &d_config)) { case DOMAIN_RESTART_SOFT_RESET: domid_soft_reset = domid; domid = INVALID_DOMID; /* fall through */ case DOMAIN_RESTART_RENAME: if (domid_soft_reset == INVALID_DOMID && !preserve_domain(&domid, event, &d_config)) { libxl_event_free(ctx, event); /* If we fail then exit leaving the old domain in place. */ ret = -1; goto out; } /* Otherwise fall through and restart. */ case DOMAIN_RESTART_NORMAL: libxl_event_free(ctx, event); libxl_evdisable_domain_death(ctx, deathw); deathw = NULL; evdisable_disk_ejects(diskws, num_diskws); free(diskws); diskws = NULL; num_diskws = 0; /* discard any other events which may have been generated */ while (!(ret = libxl_event_check(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0))) { libxl_event_free(ctx, event); } if (ret != ERROR_NOT_READY) { LOG("warning, libxl_event_check (cleanup) failed (rc=%d)", ret); } /* * Do not attempt to reconnect if we come round again due to a * guest reboot -- the stdin/out will be disconnected by then. */ dom_info->console_autoconnect = 0; /* Some settings only make sense on first boot. */ paused = 0; if (common_domname && strcmp(d_config.c_info.name, common_domname)) { d_config.c_info.name = strdup(common_domname); } /* * XXX FIXME: If this sleep is not there then domain * re-creation fails sometimes. */ LOG("Done. Rebooting now"); sleep(2); goto start; case DOMAIN_RESTART_NONE: LOG("Done. Exiting now"); libxl_event_free(ctx, event); ret = 0; goto out; default: abort(); } case LIBXL_EVENT_TYPE_DOMAIN_DEATH: LOG("Domain %u has been destroyed.", domid); libxl_event_free(ctx, event); ret = 0; goto out; case LIBXL_EVENT_TYPE_DISK_EJECT: /* XXX what is this for? */ libxl_cdrom_insert(ctx, domid, &event->u.disk_eject.disk, NULL); break; default:; char *evstr = libxl_event_to_json(ctx, event); LOG("warning, got unexpected event type %d, event=%s", event->type, evstr); free(evstr); } libxl_event_free(ctx, event); } error_out: release_lock(); if (libxl_domid_valid_guest(domid)) { libxl_domain_destroy(ctx, domid, 0); domid = INVALID_DOMID; } out: if (restore_fd_to_close >= 0) { if (close(restore_fd_to_close)) fprintf(stderr, "Failed to close restoring file, fd %d, errno %d\n", restore_fd_to_close, errno); restore_fd_to_close = -1; } if (logfile != 2) close(logfile); libxl_domain_config_dispose(&d_config); free(config_data); console_child_report(child_console); if (deathw) libxl_evdisable_domain_death(ctx, deathw); if (diskws) { evdisable_disk_ejects(diskws, d_config.num_disks); free(diskws); } /* * If we have daemonized then do not return to the caller -- this has * already happened in the parent. */ if ( daemonize && !need_daemon ) exit(ret); return ret; } int main_create(int argc, char **argv) { const char *filename = NULL; struct domain_create dom_info; int paused = 0, debug = 0, daemonize = 1, console_autoconnect = 0, quiet = 0, monitor = 1, vnc = 0, vncautopass = 0; int opt, rc; static struct option opts[] = { {"dryrun", 0, 0, 'n'}, {"quiet", 0, 0, 'q'}, {"defconfig", 1, 0, 'f'}, {"vncviewer", 0, 0, 'V'}, {"vncviewer-autopass", 0, 0, 'A'}, COMMON_LONG_OPTS }; dom_info.extra_config = NULL; if (argv[1] && argv[1][0] != '-' && !strchr(argv[1], '=')) { filename = argv[1]; argc--; argv++; } SWITCH_FOREACH_OPT(opt, "Fnqf:pcdeVA", opts, "create", 0) { case 'f': filename = optarg; break; case 'p': paused = 1; break; case 'c': console_autoconnect = 1; break; case 'd': debug = 1; break; case 'F': daemonize = 0; break; case 'e': daemonize = 0; monitor = 0; break; case 'n': dryrun_only = 1; break; case 'q': quiet = 1; break; case 'V': vnc = 1; break; case 'A': vnc = vncautopass = 1; break; } memset(&dom_info, 0, sizeof(dom_info)); for (; optind < argc; optind++) { if (strchr(argv[optind], '=') != NULL) { string_realloc_append(&dom_info.extra_config, argv[optind]); string_realloc_append(&dom_info.extra_config, "\n"); } else if (!filename) { filename = argv[optind]; } else { help("create"); free(dom_info.extra_config); return 2; } } dom_info.debug = debug; dom_info.daemonize = daemonize; dom_info.monitor = monitor; dom_info.paused = paused; dom_info.dryrun = dryrun_only; dom_info.quiet = quiet; dom_info.config_file = filename; dom_info.migrate_fd = -1; dom_info.send_back_fd = -1; dom_info.vnc = vnc; dom_info.vncautopass = vncautopass; dom_info.console_autoconnect = console_autoconnect; rc = create_domain(&dom_info); if (rc < 0) { free(dom_info.extra_config); return -rc; } free(dom_info.extra_config); return 0; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_parse.h0000664000175000017500000000465113256712137014234 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef XL_PARSE_H #define XL_PARSE_H #include void parse_config_data(const char *config_source, const char *config_data, int config_len, libxl_domain_config *d_config); int parse_range(const char *str, unsigned long *a, unsigned long *b); int64_t parse_mem_size_kb(const char *mem); void parse_disk_config(XLU_Config **config, const char *spec, libxl_device_disk *disk); void parse_disk_config_multistring(XLU_Config **config, int nspecs, const char *const *specs, libxl_device_disk *disk); int parse_usbctrl_config(libxl_device_usbctrl *usbctrl, char *token); int parse_usbdev_config(libxl_device_usbdev *usbdev, char *token); int parse_cpurange(const char *cpu, libxl_bitmap *cpumap); int parse_nic_config(libxl_device_nic *nic, XLU_Config **config, char *token); int match_option_size(const char *prefix, size_t len, char *arg, char **argopt); #define MATCH_OPTION(prefix, arg, oparg) \ match_option_size((prefix "="), sizeof((prefix)), (arg), &(oparg)) void split_string_into_string_list(const char *str, const char *delim, libxl_string_list *psl); int split_string_into_pair(const char *str, const char *delim, char **a, char **b); void replace_string(char **str, const char *val); /* NB: this follows the interface used by . See 'man 3 ctype' and look for CTYPE in libxl_internal.h */ typedef int (*char_predicate_t)(const int c); void trim(char_predicate_t predicate, const char *input, char **output); const char *get_action_on_shutdown_name(libxl_action_on_shutdown a); #endif /* XL_PARSE_H */ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_mem.c0000664000175000017500000001026713256712137013673 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" static int set_memory_max(uint32_t domid, const char *mem) { int64_t memorykb; memorykb = parse_mem_size_kb(mem); if (memorykb == -1) { fprintf(stderr, "invalid memory size: %s\n", mem); return EXIT_FAILURE; } if (libxl_domain_setmaxmem(ctx, domid, memorykb)) { fprintf(stderr, "cannot set domid %u static max memory to : %s\n", domid, mem); return EXIT_FAILURE; } return EXIT_SUCCESS; } int main_memmax(int argc, char **argv) { uint32_t domid; int opt = 0; char *mem; SWITCH_FOREACH_OPT(opt, "", NULL, "mem-max", 2) { /* No options */ } domid = find_domain(argv[optind]); mem = argv[optind + 1]; return set_memory_max(domid, mem); } static int set_memory_target(uint32_t domid, const char *mem) { int64_t memorykb; memorykb = parse_mem_size_kb(mem); if (memorykb == -1) { fprintf(stderr, "invalid memory size: %s\n", mem); return EXIT_FAILURE; } if (libxl_set_memory_target(ctx, domid, memorykb, 0, /* enforce */ 1)) { fprintf(stderr, "cannot set domid %u dynamic max memory to : %s\n", domid, mem); return EXIT_FAILURE; } return EXIT_SUCCESS; } int main_memset(int argc, char **argv) { uint32_t domid; int opt = 0; const char *mem; SWITCH_FOREACH_OPT(opt, "", NULL, "mem-set", 2) { /* No options */ } domid = find_domain(argv[optind]); mem = argv[optind + 1]; return set_memory_target(domid, mem); } static void sharing(const libxl_dominfo *info, int nb_domain) { int i; printf("Name ID Mem Shared\n"); for (i = 0; i < nb_domain; i++) { char *domname; unsigned shutdown_reason; domname = libxl_domid_to_name(ctx, info[i].domid); shutdown_reason = info[i].shutdown ? info[i].shutdown_reason : 0; printf("%-40s %5d %5lu %5lu\n", domname, info[i].domid, (unsigned long) ((info[i].current_memkb + info[i].outstanding_memkb) / 1024), (unsigned long) (info[i].shared_memkb / 1024)); free(domname); } } int main_sharing(int argc, char **argv) { int opt = 0; libxl_dominfo info_buf; libxl_dominfo *info, *info_free = NULL; int nb_domain, rc; SWITCH_FOREACH_OPT(opt, "", NULL, "sharing", 0) { /* No options */ } if (optind >= argc) { info = libxl_list_domain(ctx, &nb_domain); if (!info) { fprintf(stderr, "libxl_list_domain failed.\n"); return EXIT_FAILURE; } info_free = info; } else if (optind == argc-1) { uint32_t domid = find_domain(argv[optind]); rc = libxl_domain_info(ctx, &info_buf, domid); if (rc == ERROR_DOMAIN_NOTFOUND) { fprintf(stderr, "Error: Domain \'%s\' does not exist.\n", argv[optind]); return EXIT_FAILURE; } if (rc) { fprintf(stderr, "libxl_domain_info failed (code %d).\n", rc); return EXIT_FAILURE; } info = &info_buf; nb_domain = 1; } else { help("sharing"); return EXIT_FAILURE; } sharing(info, nb_domain); if (info_free) libxl_dominfo_list_free(info_free, nb_domain); else libxl_dominfo_dispose(info); return EXIT_SUCCESS; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_console.c0000664000175000017500000000732413256712137014557 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" int main_console(int argc, char **argv) { uint32_t domid; int opt = 0, num = 0; libxl_console_type type = 0; SWITCH_FOREACH_OPT(opt, "n:t:", NULL, "console", 1) { case 't': if (!strcmp(optarg, "pv")) type = LIBXL_CONSOLE_TYPE_PV; else if (!strcmp(optarg, "serial")) type = LIBXL_CONSOLE_TYPE_SERIAL; else { fprintf(stderr, "console type supported are: pv, serial\n"); return EXIT_FAILURE; } break; case 'n': num = atoi(optarg); break; } domid = find_domain(argv[optind]); if (!type) libxl_primary_console_exec(ctx, domid, -1); else libxl_console_exec(ctx, domid, num, type, -1); fprintf(stderr, "Unable to attach console\n"); return EXIT_FAILURE; } int main_vncviewer(int argc, char **argv) { static const struct option opts[] = { {"autopass", 0, 0, 'a'}, {"vncviewer-autopass", 0, 0, 'a'}, COMMON_LONG_OPTS }; uint32_t domid; int opt, autopass = 0; SWITCH_FOREACH_OPT(opt, "a", opts, "vncviewer", 1) { case 'a': autopass = 1; break; } domid = find_domain(argv[optind]); libxl_vncviewer_exec(ctx, domid, autopass); return EXIT_FAILURE; } /* Channel is just a console in disguise, so put it here */ int main_channellist(int argc, char **argv) { int opt; libxl_device_channel *channels; libxl_channelinfo channelinfo; int nb, i; SWITCH_FOREACH_OPT(opt, "", NULL, "channel-list", 1) { /* No options */ } /* Idx BE state evt-ch ring-ref connection params*/ printf("%-3s %-2s %-5s %-6s %8s %-10s %-30s\n", "Idx", "BE", "state", "evt-ch", "ring-ref", "connection", ""); for (argv += optind, argc -= optind; argc > 0; --argc, ++argv) { uint32_t domid = find_domain(*argv); channels = libxl_device_channel_list(ctx, domid, &nb); if (!channels) continue; for (i = 0; i < nb; ++i) { if (!libxl_device_channel_getinfo(ctx, domid, &channels[i], &channelinfo)) { printf("%-3d %-2d ", channels[i].devid, channelinfo.backend_id); printf("%-5d ", channelinfo.state); printf("%-6d %-8d ", channelinfo.evtch, channelinfo.rref); printf("%-10s ", libxl_channel_connection_to_string( channels[i].connection)); switch (channels[i].connection) { case LIBXL_CHANNEL_CONNECTION_PTY: printf("%-30s ", channelinfo.u.pty.path); break; default: break; } printf("\n"); libxl_channelinfo_dispose(&channelinfo); } libxl_device_channel_dispose(&channels[i]); } free(channels); } return 0; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_usb.c0000664000175000017500000001316613256712137013707 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" int main_usbctrl_attach(int argc, char **argv) { uint32_t domid; int opt, rc = 0; libxl_device_usbctrl usbctrl; SWITCH_FOREACH_OPT(opt, "", NULL, "usbctrl-attach", 1) { /* No options */ } domid = find_domain(argv[optind++]); libxl_device_usbctrl_init(&usbctrl); for (argv += optind, argc -= optind; argc > 0; ++argv, --argc) { if (parse_usbctrl_config(&usbctrl, *argv)) return 1; } rc = libxl_device_usbctrl_add(ctx, domid, &usbctrl, 0); if (rc) { fprintf(stderr, "libxl_device_usbctrl_add failed.\n"); rc = 1; } libxl_device_usbctrl_dispose(&usbctrl); return rc; } int main_usbctrl_detach(int argc, char **argv) { uint32_t domid; int opt, devid, rc; libxl_device_usbctrl usbctrl; SWITCH_FOREACH_OPT(opt, "", NULL, "usbctrl-detach", 2) { /* No options */ } domid = find_domain(argv[optind]); devid = atoi(argv[optind+1]); libxl_device_usbctrl_init(&usbctrl); if (libxl_devid_to_device_usbctrl(ctx, domid, devid, &usbctrl)) { fprintf(stderr, "Unknown device %s.\n", argv[optind+1]); return 1; } rc = libxl_device_usbctrl_remove(ctx, domid, &usbctrl, 0); if (rc) { fprintf(stderr, "libxl_device_usbctrl_remove failed.\n"); rc = 1; } libxl_device_usbctrl_dispose(&usbctrl); return rc; } int main_usbdev_attach(int argc, char **argv) { uint32_t domid; int opt, rc; libxl_device_usbdev usbdev; SWITCH_FOREACH_OPT(opt, "", NULL, "usbdev-attach", 2) { /* No options */ } libxl_device_usbdev_init(&usbdev); domid = find_domain(argv[optind++]); for (argv += optind, argc -= optind; argc > 0; ++argv, --argc) { if (parse_usbdev_config(&usbdev, *argv)) return 1; } rc = libxl_device_usbdev_add(ctx, domid, &usbdev, 0); if (rc) { fprintf(stderr, "libxl_device_usbdev_add failed.\n"); rc = 1; } libxl_device_usbdev_dispose(&usbdev); return rc; } int main_usbdev_detach(int argc, char **argv) { uint32_t domid; int ctrl, port; int opt, rc = 1; libxl_device_usbdev usbdev; SWITCH_FOREACH_OPT(opt, "", NULL, "usbdev-detach", 3) { /* No options */ } domid = find_domain(argv[optind]); ctrl = atoi(argv[optind+1]); port = atoi(argv[optind+2]); if (argc - optind > 3) { fprintf(stderr, "Invalid arguments.\n"); return 1; } libxl_device_usbdev_init(&usbdev); if (libxl_ctrlport_to_device_usbdev(ctx, domid, ctrl, port, &usbdev)) { fprintf(stderr, "Unknown device at controller %d port %d.\n", ctrl, port); return 1; } rc = libxl_device_usbdev_remove(ctx, domid, &usbdev, 0); if (rc) { fprintf(stderr, "libxl_device_usbdev_remove failed.\n"); rc = 1; } libxl_device_usbdev_dispose(&usbdev); return rc; } int main_usblist(int argc, char **argv) { uint32_t domid; libxl_device_usbctrl *usbctrls; libxl_usbctrlinfo usbctrlinfo; int numctrl, i, j, opt; SWITCH_FOREACH_OPT(opt, "", NULL, "usb-list", 1) { /* No options */ } domid = find_domain(argv[optind++]); if (argc > optind) { fprintf(stderr, "Invalid arguments.\n"); exit(-1); } usbctrls = libxl_device_usbctrl_list(ctx, domid, &numctrl); if (!usbctrls) { return 0; } for (i = 0; i < numctrl; ++i) { printf("%-6s %-12s %-3s %-5s %-7s %-5s\n", "Devid", "Type", "BE", "state", "usb-ver", "ports"); libxl_usbctrlinfo_init(&usbctrlinfo); if (!libxl_device_usbctrl_getinfo(ctx, domid, &usbctrls[i], &usbctrlinfo)) { printf("%-6d %-12s %-3d %-5d %-7d %-5d\n", usbctrlinfo.devid, libxl_usbctrl_type_to_string(usbctrlinfo.type), usbctrlinfo.backend_id, usbctrlinfo.state, usbctrlinfo.version, usbctrlinfo.ports); for (j = 1; j <= usbctrlinfo.ports; j++) { libxl_device_usbdev usbdev; libxl_device_usbdev_init(&usbdev); printf(" Port %d:", j); if (!libxl_ctrlport_to_device_usbdev(ctx, domid, usbctrlinfo.devid, j, &usbdev)) { printf(" Bus %03x Device %03x\n", usbdev.u.hostdev.hostbus, usbdev.u.hostdev.hostaddr); } else { printf("\n"); } libxl_device_usbdev_dispose(&usbdev); } } libxl_usbctrlinfo_dispose(&usbctrlinfo); } libxl_device_usbctrl_list_free(usbctrls, numctrl); return 0; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/Makefile0000664000175000017500000000262213256712137013702 0ustar smbsmb# # tools/xl/Makefile # XEN_ROOT = $(CURDIR)/../.. include $(XEN_ROOT)/tools/Rules.mk CFLAGS += -Werror -Wno-format-zero-length -Wmissing-declarations \ -Wno-declaration-after-statement -Wformat-nonliteral CFLAGS += -I. -fPIC CFLAGS += $(PTHREAD_CFLAGS) LDFLAGS += $(PTHREAD_LDFLAGS) CFLAGS_XL += $(CFLAGS_libxenlight) CFLAGS_XL += -Wshadow XL_OBJS = xl.o xl_cmdtable.o xl_sxp.o xl_utils.o XL_OBJS += xl_tmem.o xl_parse.o xl_cpupool.o xl_flask.o XL_OBJS += xl_vtpm.o xl_block.o xl_nic.o xl_usb.o XL_OBJS += xl_sched.o xl_pci.o xl_vcpu.o xl_cdrom.o xl_mem.o XL_OBJS += xl_psr.o xl_info.o xl_console.o xl_misc.o XL_OBJS += xl_vmcontrol.o xl_saverestore.o xl_migrate.o $(XL_OBJS): CFLAGS += $(CFLAGS_libxentoollog) $(XL_OBJS): CFLAGS += $(CFLAGS_XL) $(XL_OBJS): CFLAGS += -include $(XEN_ROOT)/tools/config.h # libxl_json.h needs it. genpath-target = $(call buildmakevars2header,_paths.h) $(eval $(genpath-target)) $(XL_OBJS): _paths.h .PHONY: all all: xl xl: $(XL_OBJS) $(CC) $(LDFLAGS) -o $@ $(XL_OBJS) $(LDLIBS_libxlutil) $(LDLIBS_libxenlight) $(LDLIBS_libxentoollog) -lyajl $(APPEND_LDFLAGS) .PHONY: install install: all $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_DIR) $(DESTDIR)$(BASH_COMPLETION_DIR) $(INSTALL_PROG) xl $(DESTDIR)$(sbindir) $(INSTALL_DATA) bash-completion $(DESTDIR)$(BASH_COMPLETION_DIR)/xl.sh .PHONY: clean clean: $(RM) -f *.o xl _paths.h $(DEPS) distclean: clean -include $(DEPS) xen-4.9.2/tools/xl/xl_psr.c0000664000175000017500000003620413256712137013720 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" #ifdef LIBXL_HAVE_PSR_CMT static int psr_cmt_hwinfo(void) { int rc; int enabled; uint32_t total_rmid; printf("Cache Monitoring Technology (CMT):\n"); enabled = libxl_psr_cmt_enabled(ctx); printf("%-16s: %s\n", "Enabled", enabled ? "1" : "0"); if (!enabled) return 0; rc = libxl_psr_cmt_get_total_rmid(ctx, &total_rmid); if (rc) { fprintf(stderr, "Failed to get max RMID value\n"); return rc; } printf("%-16s: %u\n", "Total RMID", total_rmid); printf("Supported monitor types:\n"); if (libxl_psr_cmt_type_supported(ctx, LIBXL_PSR_CMT_TYPE_CACHE_OCCUPANCY)) printf("cache-occupancy\n"); if (libxl_psr_cmt_type_supported(ctx, LIBXL_PSR_CMT_TYPE_TOTAL_MEM_COUNT)) printf("total-mem-bandwidth\n"); if (libxl_psr_cmt_type_supported(ctx, LIBXL_PSR_CMT_TYPE_LOCAL_MEM_COUNT)) printf("local-mem-bandwidth\n"); return rc; } #define MBM_SAMPLE_RETRY_MAX 4 static int psr_cmt_get_mem_bandwidth(uint32_t domid, libxl_psr_cmt_type type, uint32_t socketid, uint64_t *bandwidth_r) { uint64_t sample1, sample2; uint64_t tsc1, tsc2; int retry_attempts = 0; int rc; while (1) { rc = libxl_psr_cmt_get_sample(ctx, domid, type, socketid, &sample1, &tsc1); if (rc < 0) return rc; usleep(10000); rc = libxl_psr_cmt_get_sample(ctx, domid, type, socketid, &sample2, &tsc2); if (rc < 0) return rc; if (tsc2 <= tsc1) return -1; /* * Hardware guarantees at most 1 overflow can happen if the duration * between two samples is less than 1 second. Note that tsc returned * from hypervisor is already-scaled time(ns). */ if (tsc2 - tsc1 < 1000000000 && sample2 >= sample1) break; if (retry_attempts < MBM_SAMPLE_RETRY_MAX) { retry_attempts++; } else { fprintf(stderr, "event counter overflowed\n"); return -1; } } *bandwidth_r = (sample2 - sample1) * 1000000000 / (tsc2 - tsc1) / 1024; return 0; } static void psr_cmt_print_domain_info(libxl_dominfo *dominfo, libxl_psr_cmt_type type, libxl_bitmap *socketmap) { char *domain_name; uint32_t socketid; uint64_t monitor_data; if (!libxl_psr_cmt_domain_attached(ctx, dominfo->domid)) return; domain_name = libxl_domid_to_name(ctx, dominfo->domid); printf("%-40s %5d", domain_name, dominfo->domid); free(domain_name); libxl_for_each_set_bit(socketid, *socketmap) { switch (type) { case LIBXL_PSR_CMT_TYPE_CACHE_OCCUPANCY: if (!libxl_psr_cmt_get_sample(ctx, dominfo->domid, type, socketid, &monitor_data, NULL)) printf("%13"PRIu64" KB", monitor_data / 1024); break; case LIBXL_PSR_CMT_TYPE_TOTAL_MEM_COUNT: case LIBXL_PSR_CMT_TYPE_LOCAL_MEM_COUNT: if (!psr_cmt_get_mem_bandwidth(dominfo->domid, type, socketid, &monitor_data)) printf("%11"PRIu64" KB/s", monitor_data); break; default: return; } } printf("\n"); } static int psr_cmt_show(libxl_psr_cmt_type type, uint32_t domid) { uint32_t i, socketid, total_rmid; uint32_t l3_cache_size; libxl_bitmap socketmap; int rc, nr_domains; if (!libxl_psr_cmt_enabled(ctx)) { fprintf(stderr, "CMT is disabled in the system\n"); return -1; } if (!libxl_psr_cmt_type_supported(ctx, type)) { fprintf(stderr, "Monitor type '%s' is not supported in the system\n", libxl_psr_cmt_type_to_string(type)); return -1; } libxl_bitmap_init(&socketmap); libxl_socket_bitmap_alloc(ctx, &socketmap, 0); rc = libxl_get_online_socketmap(ctx, &socketmap); if (rc < 0) { fprintf(stderr, "Failed getting available sockets, rc: %d\n", rc); goto out; } rc = libxl_psr_cmt_get_total_rmid(ctx, &total_rmid); if (rc < 0) { fprintf(stderr, "Failed to get max RMID value\n"); goto out; } printf("Total RMID: %d\n", total_rmid); /* Header */ printf("%-40s %5s", "Name", "ID"); libxl_for_each_set_bit(socketid, socketmap) printf("%14s %d", "Socket", socketid); printf("\n"); if (type == LIBXL_PSR_CMT_TYPE_CACHE_OCCUPANCY) { /* Total L3 cache size */ printf("%-46s", "Total L3 Cache Size"); libxl_for_each_set_bit(socketid, socketmap) { rc = libxl_psr_cmt_get_l3_cache_size(ctx, socketid, &l3_cache_size); if (rc < 0) { fprintf(stderr, "Failed to get system l3 cache size for socket:%d\n", socketid); goto out; } printf("%13u KB", l3_cache_size); } printf("\n"); } /* Each domain */ if (domid != INVALID_DOMID) { libxl_dominfo dominfo; libxl_dominfo_init(&dominfo); if (libxl_domain_info(ctx, &dominfo, domid)) { fprintf(stderr, "Failed to get domain info for %d\n", domid); rc = -1; goto out; } psr_cmt_print_domain_info(&dominfo, type, &socketmap); libxl_dominfo_dispose(&dominfo); } else { libxl_dominfo *list; if (!(list = libxl_list_domain(ctx, &nr_domains))) { fprintf(stderr, "Failed to get domain info for domain list.\n"); rc = -1; goto out; } for (i = 0; i < nr_domains; i++) psr_cmt_print_domain_info(list + i, type, &socketmap); libxl_dominfo_list_free(list, nr_domains); } out: libxl_bitmap_dispose(&socketmap); return rc; } int main_psr_cmt_attach(int argc, char **argv) { uint32_t domid; int opt, ret = 0; SWITCH_FOREACH_OPT(opt, "", NULL, "psr-cmt-attach", 1) { /* No options */ } domid = find_domain(argv[optind]); ret = libxl_psr_cmt_attach(ctx, domid); return ret; } int main_psr_cmt_detach(int argc, char **argv) { uint32_t domid; int opt, ret = 0; SWITCH_FOREACH_OPT(opt, "", NULL, "psr-cmt-detach", 1) { /* No options */ } domid = find_domain(argv[optind]); ret = libxl_psr_cmt_detach(ctx, domid); return ret; } int main_psr_cmt_show(int argc, char **argv) { int opt, ret = 0; uint32_t domid; libxl_psr_cmt_type type; SWITCH_FOREACH_OPT(opt, "", NULL, "psr-cmt-show", 1) { /* No options */ } if (!strcmp(argv[optind], "cache-occupancy")) type = LIBXL_PSR_CMT_TYPE_CACHE_OCCUPANCY; else if (!strcmp(argv[optind], "total-mem-bandwidth")) type = LIBXL_PSR_CMT_TYPE_TOTAL_MEM_COUNT; else if (!strcmp(argv[optind], "local-mem-bandwidth")) type = LIBXL_PSR_CMT_TYPE_LOCAL_MEM_COUNT; else { help("psr-cmt-show"); return 2; } if (optind + 1 >= argc) domid = INVALID_DOMID; else if (optind + 1 == argc - 1) domid = find_domain(argv[optind + 1]); else { help("psr-cmt-show"); return 2; } ret = psr_cmt_show(type, domid); return ret; } #endif #ifdef LIBXL_HAVE_PSR_CAT static int psr_cat_hwinfo(void) { int rc; int i, nr; uint32_t l3_cache_size; libxl_psr_cat_info *info; printf("Cache Allocation Technology (CAT):\n"); rc = libxl_psr_cat_get_l3_info(ctx, &info, &nr); if (rc) { fprintf(stderr, "Failed to get cat info\n"); return rc; } for (i = 0; i < nr; i++) { rc = libxl_psr_cmt_get_l3_cache_size(ctx, info[i].id, &l3_cache_size); if (rc) { fprintf(stderr, "Failed to get l3 cache size for socket:%d\n", info[i].id); goto out; } printf("%-16s: %u\n", "Socket ID", info[i].id); printf("%-16s: %uKB\n", "L3 Cache", l3_cache_size); printf("%-16s: %s\n", "CDP Status", info[i].cdp_enabled ? "Enabled" : "Disabled"); printf("%-16s: %u\n", "Maximum COS", info[i].cos_max); printf("%-16s: %u\n", "CBM length", info[i].cbm_len); printf("%-16s: %#llx\n", "Default CBM", (1ull << info[i].cbm_len) - 1); } out: libxl_psr_cat_info_list_free(info, nr); return rc; } static void psr_cat_print_one_domain_cbm_type(uint32_t domid, uint32_t socketid, libxl_psr_cbm_type type) { uint64_t cbm; if (!libxl_psr_cat_get_cbm(ctx, domid, type, socketid, &cbm)) printf("%#16"PRIx64, cbm); else printf("%16s", "error"); } static void psr_cat_print_one_domain_cbm(uint32_t domid, uint32_t socketid, bool cdp_enabled) { char *domain_name; domain_name = libxl_domid_to_name(ctx, domid); printf("%5d%25s", domid, domain_name); free(domain_name); if (!cdp_enabled) { psr_cat_print_one_domain_cbm_type(domid, socketid, LIBXL_PSR_CBM_TYPE_L3_CBM); } else { psr_cat_print_one_domain_cbm_type(domid, socketid, LIBXL_PSR_CBM_TYPE_L3_CBM_CODE); psr_cat_print_one_domain_cbm_type(domid, socketid, LIBXL_PSR_CBM_TYPE_L3_CBM_DATA); } printf("\n"); } static int psr_cat_print_domain_cbm(uint32_t domid, uint32_t socketid, bool cdp_enabled) { int i, nr_domains; libxl_dominfo *list; if (domid != INVALID_DOMID) { psr_cat_print_one_domain_cbm(domid, socketid, cdp_enabled); return 0; } if (!(list = libxl_list_domain(ctx, &nr_domains))) { fprintf(stderr, "Failed to get domain list for cbm display\n"); return -1; } for (i = 0; i < nr_domains; i++) psr_cat_print_one_domain_cbm(list[i].domid, socketid, cdp_enabled); libxl_dominfo_list_free(list, nr_domains); return 0; } static int psr_cat_print_socket(uint32_t domid, libxl_psr_cat_info *info) { int rc; uint32_t l3_cache_size; rc = libxl_psr_cmt_get_l3_cache_size(ctx, info->id, &l3_cache_size); if (rc) { fprintf(stderr, "Failed to get l3 cache size for socket:%d\n", info->id); return -1; } printf("%-16s: %u\n", "Socket ID", info->id); printf("%-16s: %uKB\n", "L3 Cache", l3_cache_size); printf("%-16s: %#llx\n", "Default CBM", (1ull << info->cbm_len) - 1); if (info->cdp_enabled) printf("%5s%25s%16s%16s\n", "ID", "NAME", "CBM (code)", "CBM (data)"); else printf("%5s%25s%16s\n", "ID", "NAME", "CBM"); return psr_cat_print_domain_cbm(domid, info->id, info->cdp_enabled); } static int psr_cat_show(uint32_t domid) { int i, nr; int rc; libxl_psr_cat_info *info; rc = libxl_psr_cat_get_l3_info(ctx, &info, &nr); if (rc) { fprintf(stderr, "Failed to get cat info\n"); return rc; } for (i = 0; i < nr; i++) { rc = psr_cat_print_socket(domid, info + i); if (rc) goto out; } out: libxl_psr_cat_info_list_free(info, nr); return rc; } int main_psr_cat_cbm_set(int argc, char **argv) { uint32_t domid; libxl_psr_cbm_type type; uint64_t cbm; int ret, opt = 0; int opt_data = 0, opt_code = 0; libxl_bitmap target_map; char *value; libxl_string_list socket_list; unsigned long start, end; int i, j, len; static struct option opts[] = { {"socket", 1, 0, 's'}, {"data", 0, 0, 'd'}, {"code", 0, 0, 'c'}, COMMON_LONG_OPTS }; libxl_socket_bitmap_alloc(ctx, &target_map, 0); libxl_bitmap_set_none(&target_map); SWITCH_FOREACH_OPT(opt, "s:cd", opts, "psr-cat-cbm-set", 2) { case 's': trim(isspace, optarg, &value); split_string_into_string_list(value, ",", &socket_list); len = libxl_string_list_length(&socket_list); for (i = 0; i < len; i++) { parse_range(socket_list[i], &start, &end); for (j = start; j <= end; j++) libxl_bitmap_set(&target_map, j); } libxl_string_list_dispose(&socket_list); free(value); break; case 'd': opt_data = 1; break; case 'c': opt_code = 1; break; } if (opt_data && opt_code) { fprintf(stderr, "Cannot handle -c and -d at the same time\n"); return -1; } else if (opt_data) { type = LIBXL_PSR_CBM_TYPE_L3_CBM_DATA; } else if (opt_code) { type = LIBXL_PSR_CBM_TYPE_L3_CBM_CODE; } else { type = LIBXL_PSR_CBM_TYPE_L3_CBM; } if (libxl_bitmap_is_empty(&target_map)) libxl_bitmap_set_any(&target_map); if (argc != optind + 2) { help("psr-cat-cbm-set"); return 2; } domid = find_domain(argv[optind]); cbm = strtoll(argv[optind + 1], NULL , 0); ret = libxl_psr_cat_set_cbm(ctx, domid, type, &target_map, cbm); libxl_bitmap_dispose(&target_map); return ret; } int main_psr_cat_show(int argc, char **argv) { int opt; uint32_t domid; SWITCH_FOREACH_OPT(opt, "", NULL, "psr-cat-show", 0) { /* No options */ } if (optind >= argc) domid = INVALID_DOMID; else if (optind == argc - 1) domid = find_domain(argv[optind]); else { help("psr-cat-show"); return 2; } return psr_cat_show(domid); } int main_psr_hwinfo(int argc, char **argv) { int opt, ret = 0; bool all = true, cmt = false, cat = false; static struct option opts[] = { {"cmt", 0, 0, 'm'}, {"cat", 0, 0, 'a'}, COMMON_LONG_OPTS }; SWITCH_FOREACH_OPT(opt, "ma", opts, "psr-hwinfo", 0) { case 'm': all = false; cmt = true; break; case 'a': all = false; cat = true; break; } if (!ret && (all || cmt)) ret = psr_cmt_hwinfo(); if (!ret && (all || cat)) ret = psr_cat_hwinfo(); return ret; } #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_cdrom.c0000664000175000017500000000532713256712137014222 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" static int cd_insert(uint32_t domid, const char *virtdev, char *phys) { libxl_device_disk disk; char *buf = NULL; XLU_Config *config = 0; struct stat b; int r; xasprintf(&buf, "vdev=%s,access=r,devtype=cdrom,target=%s", virtdev, phys ? phys : ""); parse_disk_config(&config, buf, &disk); /* ATM the existence of the backing file is not checked for qdisk * in libxl_cdrom_insert() because RAW is used for remote * protocols as well as plain files. This will ideally be changed * for 4.4, but this work-around fixes the problem of "cd-insert" * returning success for non-existent files. */ if (disk.format != LIBXL_DISK_FORMAT_EMPTY && stat(disk.pdev_path, &b)) { fprintf(stderr, "Cannot stat file: %s\n", disk.pdev_path); r = 1; goto out; } if (libxl_cdrom_insert(ctx, domid, &disk, NULL)) { r = 1; goto out; } r = 0; out: libxl_device_disk_dispose(&disk); free(buf); return r; } int main_cd_eject(int argc, char **argv) { uint32_t domid; int opt = 0; const char *virtdev; SWITCH_FOREACH_OPT(opt, "", NULL, "cd-eject", 2) { /* No options */ } domid = find_domain(argv[optind]); virtdev = argv[optind + 1]; if (cd_insert(domid, virtdev, NULL)) return EXIT_FAILURE; return EXIT_SUCCESS; } int main_cd_insert(int argc, char **argv) { uint32_t domid; int opt = 0; const char *virtdev; char *file = NULL; /* modified by cd_insert tokenising it */ SWITCH_FOREACH_OPT(opt, "", NULL, "cd-insert", 3) { /* No options */ } domid = find_domain(argv[optind]); virtdev = argv[optind + 1]; file = argv[optind + 2]; if (cd_insert(domid, virtdev, file)) return EXIT_FAILURE; return EXIT_SUCCESS; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_vcpu.c0000664000175000017500000002240713256712137014071 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" static void print_vcpuinfo(uint32_t tdomid, const libxl_vcpuinfo *vcpuinfo, uint32_t nr_cpus) { char *domname; /* NAME ID VCPU */ domname = libxl_domid_to_name(ctx, tdomid); printf("%-32s %5u %5u", domname, tdomid, vcpuinfo->vcpuid); free(domname); if (!vcpuinfo->online) { /* CPU STA */ printf("%5c %3c%cp ", '-', '-', '-'); } else { /* CPU STA */ printf("%5u %3c%c- ", vcpuinfo->cpu, vcpuinfo->running ? 'r' : '-', vcpuinfo->blocked ? 'b' : '-'); } /* TIM */ printf("%9.1f ", ((float)vcpuinfo->vcpu_time / 1e9)); /* CPU HARD AND SOFT AFFINITY */ print_bitmap(vcpuinfo->cpumap.map, nr_cpus, stdout); printf(" / "); print_bitmap(vcpuinfo->cpumap_soft.map, nr_cpus, stdout); printf("\n"); } static void print_domain_vcpuinfo(uint32_t domid, uint32_t nr_cpus) { libxl_vcpuinfo *vcpuinfo; int i, nb_vcpu, nrcpus; vcpuinfo = libxl_list_vcpu(ctx, domid, &nb_vcpu, &nrcpus); if (!vcpuinfo) return; for (i = 0; i < nb_vcpu; i++) { print_vcpuinfo(domid, &vcpuinfo[i], nr_cpus); } libxl_vcpuinfo_list_free(vcpuinfo, nb_vcpu); } static void vcpulist(int argc, char **argv) { libxl_dominfo *dominfo; libxl_physinfo physinfo; int i, nb_domain; if (libxl_get_physinfo(ctx, &physinfo) != 0) { fprintf(stderr, "libxl_physinfo failed.\n"); goto vcpulist_out; } printf("%-32s %5s %5s %5s %5s %9s %s\n", "Name", "ID", "VCPU", "CPU", "State", "Time(s)", "Affinity (Hard / Soft)"); if (!argc) { if (!(dominfo = libxl_list_domain(ctx, &nb_domain))) { fprintf(stderr, "libxl_list_domain failed.\n"); goto vcpulist_out; } for (i = 0; i 0; ++argv, --argc) { uint32_t domid = find_domain(*argv); print_domain_vcpuinfo(domid, physinfo.nr_cpus); } } vcpulist_out: libxl_physinfo_dispose(&physinfo); } int main_vcpulist(int argc, char **argv) { int opt; SWITCH_FOREACH_OPT(opt, "", NULL, "vcpu-list", 0) { /* No options */ } vcpulist(argc - optind, argv + optind); return EXIT_SUCCESS; } int main_vcpupin(int argc, char **argv) { static struct option opts[] = { {"force", 0, 0, 'f'}, COMMON_LONG_OPTS }; libxl_vcpuinfo *vcpuinfo; libxl_bitmap cpumap_hard, cpumap_soft;; libxl_bitmap *soft = &cpumap_soft, *hard = &cpumap_hard; uint32_t domid; /* * int would be enough for vcpuid, but we don't want to * mess aroung range checking the return value of strtol(). */ long vcpuid; const char *vcpu, *hard_str, *soft_str; char *endptr; int opt, nb_cpu, nb_vcpu, rc = EXIT_FAILURE; bool force = false; libxl_bitmap_init(&cpumap_hard); libxl_bitmap_init(&cpumap_soft); SWITCH_FOREACH_OPT(opt, "f", opts, "vcpu-pin", 3) { case 'f': force = true; break; default: break; } domid = find_domain(argv[optind]); vcpu = argv[optind+1]; hard_str = argv[optind+2]; soft_str = (argc > optind+3) ? argv[optind+3] : NULL; /* Figure out with which vCPU we are dealing with */ vcpuid = strtol(vcpu, &endptr, 10); if (vcpu == endptr || vcpuid < 0) { if (strcmp(vcpu, "all")) { fprintf(stderr, "Error: Invalid argument %s as VCPU.\n", vcpu); goto out; } if (force) { fprintf(stderr, "Error: --force and 'all' as VCPU not allowed.\n"); goto out; } vcpuid = -1; } if (libxl_cpu_bitmap_alloc(ctx, &cpumap_hard, 0) || libxl_cpu_bitmap_alloc(ctx, &cpumap_soft, 0)) goto out; /* * Syntax is: xl vcpu-pin * We want to handle all the following cases ('-' means * "leave it alone"): * xl vcpu-pin 0 3 3,4 * xl vcpu-pin 0 3 3,4 - * xl vcpu-pin 0 3 - 6-9 * xl vcpu-pin 0 3 3,4 6-9 */ /* * Hard affinity is always present. However, if it's "-", all we need * is passing a NULL pointer to the libxl_set_vcpuaffinity() call below. */ if (!strcmp(hard_str, "-")) hard = NULL; else if (parse_cpurange(hard_str, hard)) goto out; /* * Soft affinity is handled similarly. Only difference: we also want * to pass NULL to libxl_set_vcpuaffinity() if it is not specified. */ if (argc <= optind+3 || !strcmp(soft_str, "-")) soft = NULL; else if (parse_cpurange(soft_str, soft)) goto out; if (dryrun_only) { nb_cpu = libxl_get_online_cpus(ctx); if (nb_cpu < 0) { fprintf(stderr, "libxl_get_online_cpus failed.\n"); goto out; } fprintf(stdout, "cpumap: "); if (hard) print_bitmap(hard->map, nb_cpu, stdout); else fprintf(stdout, "-"); if (soft) { fprintf(stdout, " "); print_bitmap(soft->map, nb_cpu, stdout); } fprintf(stdout, "\n"); if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(EXIT_FAILURE); } rc = EXIT_SUCCESS; goto out; } if (force) { if (libxl_set_vcpuaffinity_force(ctx, domid, vcpuid, hard, soft)) { fprintf(stderr, "Could not set affinity for vcpu `%ld'.\n", vcpuid); goto out; } } else if (vcpuid != -1) { if (libxl_set_vcpuaffinity(ctx, domid, vcpuid, hard, soft)) { fprintf(stderr, "Could not set affinity for vcpu `%ld'.\n", vcpuid); goto out; } } else { if (!(vcpuinfo = libxl_list_vcpu(ctx, domid, &nb_vcpu, &nb_cpu))) { fprintf(stderr, "libxl_list_vcpu failed.\n"); goto out; } if (libxl_set_vcpuaffinity_all(ctx, domid, nb_vcpu, hard, soft)) fprintf(stderr, "Could not set affinity.\n"); libxl_vcpuinfo_list_free(vcpuinfo, nb_vcpu); } rc = EXIT_SUCCESS; out: libxl_bitmap_dispose(&cpumap_soft); libxl_bitmap_dispose(&cpumap_hard); return rc; } static int vcpuset(uint32_t domid, const char* nr_vcpus, int check_host) { char *endptr; unsigned int max_vcpus, i; libxl_bitmap cpumap; int rc; libxl_bitmap_init(&cpumap); max_vcpus = strtoul(nr_vcpus, &endptr, 10); if (nr_vcpus == endptr) { fprintf(stderr, "Error: Invalid argument.\n"); return 1; } /* * Maximum amount of vCPUS the guest is allowed to set is limited * by the host's amount of pCPUs. */ if (check_host) { unsigned int online_vcpus, host_cpu = libxl_get_max_cpus(ctx); libxl_dominfo dominfo; if (libxl_domain_info(ctx, &dominfo, domid)) return 1; online_vcpus = dominfo.vcpu_online; libxl_dominfo_dispose(&dominfo); if (max_vcpus > online_vcpus && max_vcpus > host_cpu) { fprintf(stderr, "You are overcommmitting! You have %d physical" \ " CPUs and want %d vCPUs! Aborting, use --ignore-host to" \ " continue\n", host_cpu, max_vcpus); return 1; } } rc = libxl_cpu_bitmap_alloc(ctx, &cpumap, max_vcpus); if (rc) { fprintf(stderr, "libxl_cpu_bitmap_alloc failed, rc: %d\n", rc); return 1; } for (i = 0; i < max_vcpus; i++) libxl_bitmap_set(&cpumap, i); rc = libxl_set_vcpuonline(ctx, domid, &cpumap); if (rc == ERROR_DOMAIN_NOTFOUND) fprintf(stderr, "Domain %u does not exist.\n", domid); else if (rc) fprintf(stderr, "libxl_set_vcpuonline failed domid=%u max_vcpus=%d," \ " rc: %d\n", domid, max_vcpus, rc); libxl_bitmap_dispose(&cpumap); return rc ? 1 : 0; } int main_vcpuset(int argc, char **argv) { static struct option opts[] = { {"ignore-host", 0, 0, 'i'}, COMMON_LONG_OPTS }; int opt, check_host = 1; SWITCH_FOREACH_OPT(opt, "i", opts, "vcpu-set", 2) { case 'i': check_host = 0; break; default: break; } if (vcpuset(find_domain(argv[optind]), argv[optind + 1], check_host)) return EXIT_FAILURE; return EXIT_SUCCESS; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_migrate.c0000664000175000017500000006062113256712137014544 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" #ifndef LIBXL_HAVE_NO_SUSPEND_RESUME static pid_t create_migration_child(const char *rune, int *send_fd, int *recv_fd) { int sendpipe[2], recvpipe[2]; pid_t child; if (!rune || !send_fd || !recv_fd) return -1; MUST( libxl_pipe(ctx, sendpipe) ); MUST( libxl_pipe(ctx, recvpipe) ); child = xl_fork(child_migration, "migration transport process"); if (!child) { dup2(sendpipe[0], 0); dup2(recvpipe[1], 1); close(sendpipe[0]); close(sendpipe[1]); close(recvpipe[0]); close(recvpipe[1]); execlp("sh","sh","-c",rune,(char*)0); perror("failed to exec sh"); exit(EXIT_FAILURE); } close(sendpipe[0]); close(recvpipe[1]); *send_fd = sendpipe[1]; *recv_fd = recvpipe[0]; /* if receiver dies, we get an error and can clean up rather than just dying */ signal(SIGPIPE, SIG_IGN); return child; } static int migrate_read_fixedmessage(int fd, const void *msg, int msgsz, const char *what, const char *rune) { char buf[msgsz]; const char *stream; int rc; stream = rune ? "migration receiver stream" : "migration stream"; rc = libxl_read_exactly(ctx, fd, buf, msgsz, stream, what); if (rc) return 1; if (memcmp(buf, msg, msgsz)) { fprintf(stderr, "%s contained unexpected data instead of %s\n", stream, what); if (rune) fprintf(stderr, "(command run was: %s )\n", rune); return 1; } return 0; } static void migration_child_report(int recv_fd) { pid_t child; int status, sr; struct timeval now, waituntil, timeout; static const struct timeval pollinterval = { 0, 1000 }; /* 1ms */ if (!xl_child_pid(child_migration)) return; CHK_SYSCALL(gettimeofday(&waituntil, 0)); waituntil.tv_sec += 2; for (;;) { pid_t migration_child = xl_child_pid(child_migration); child = xl_waitpid(child_migration, &status, WNOHANG); if (child == migration_child) { if (status) xl_report_child_exitstatus(XTL_INFO, child_migration, migration_child, status); break; } if (child == -1) { fprintf(stderr, "wait for migration child [%ld] failed: %s\n", (long)migration_child, strerror(errno)); break; } assert(child == 0); CHK_SYSCALL(gettimeofday(&now, 0)); if (timercmp(&now, &waituntil, >)) { fprintf(stderr, "migration child [%ld] not exiting, no longer" " waiting (exit status will be unreported)\n", (long)migration_child); break; } timersub(&waituntil, &now, &timeout); if (recv_fd >= 0) { fd_set readfds, exceptfds; FD_ZERO(&readfds); FD_ZERO(&exceptfds); FD_SET(recv_fd, &readfds); FD_SET(recv_fd, &exceptfds); sr = select(recv_fd+1, &readfds,0,&exceptfds, &timeout); } else { if (timercmp(&timeout, &pollinterval, >)) timeout = pollinterval; sr = select(0,0,0,0, &timeout); } if (sr > 0) { recv_fd = -1; } else if (sr == 0) { } else if (sr == -1) { if (errno != EINTR) { fprintf(stderr, "migration child [%ld] exit wait select" " failed unexpectedly: %s\n", (long)migration_child, strerror(errno)); break; } } } } static void migrate_do_preamble(int send_fd, int recv_fd, pid_t child, uint8_t *config_data, int config_len, const char *rune) { int rc = 0; if (send_fd < 0 || recv_fd < 0) { fprintf(stderr, "migrate_do_preamble: invalid file descriptors\n"); exit(EXIT_FAILURE); } rc = migrate_read_fixedmessage(recv_fd, migrate_receiver_banner, sizeof(migrate_receiver_banner)-1, "banner", rune); if (rc) { close(send_fd); migration_child_report(recv_fd); exit(EXIT_FAILURE); } save_domain_core_writeconfig(send_fd, "migration stream", config_data, config_len); } static void migrate_domain(uint32_t domid, const char *rune, int debug, const char *override_config_file) { pid_t child = -1; int rc; int send_fd = -1, recv_fd = -1; char *away_domname; char rc_buf; uint8_t *config_data; int config_len, flags = LIBXL_SUSPEND_LIVE; save_domain_core_begin(domid, override_config_file, &config_data, &config_len); if (!config_len) { fprintf(stderr, "No config file stored for running domain and " "none supplied - cannot migrate.\n"); exit(EXIT_FAILURE); } child = create_migration_child(rune, &send_fd, &recv_fd); migrate_do_preamble(send_fd, recv_fd, child, config_data, config_len, rune); xtl_stdiostream_adjust_flags(logger, XTL_STDIOSTREAM_HIDE_PROGRESS, 0); if (debug) flags |= LIBXL_SUSPEND_DEBUG; rc = libxl_domain_suspend(ctx, domid, send_fd, flags, NULL); if (rc) { fprintf(stderr, "migration sender: libxl_domain_suspend failed" " (rc=%d)\n", rc); if (rc == ERROR_GUEST_TIMEDOUT) goto failed_suspend; else goto failed_resume; } //fprintf(stderr, "migration sender: Transfer complete.\n"); // Should only be printed when debugging as it's a bit messy with // progress indication. rc = migrate_read_fixedmessage(recv_fd, migrate_receiver_ready, sizeof(migrate_receiver_ready), "ready message", rune); if (rc) goto failed_resume; xtl_stdiostream_adjust_flags(logger, 0, XTL_STDIOSTREAM_HIDE_PROGRESS); /* right, at this point we are about give the destination * permission to rename and resume, so we must first rename the * domain away ourselves */ fprintf(stderr, "migration sender: Target has acknowledged transfer.\n"); if (common_domname) { xasprintf(&away_domname, "%s--migratedaway", common_domname); rc = libxl_domain_rename(ctx, domid, common_domname, away_domname); if (rc) goto failed_resume; } /* point of no return - as soon as we have tried to say * "go" to the receiver, it's not safe to carry on. We leave * the domain renamed to %s--migratedaway in case that's helpful. */ fprintf(stderr, "migration sender: Giving target permission to start.\n"); rc = libxl_write_exactly(ctx, send_fd, migrate_permission_to_go, sizeof(migrate_permission_to_go), "migration stream", "GO message"); if (rc) goto failed_badly; rc = migrate_read_fixedmessage(recv_fd, migrate_report, sizeof(migrate_report), "success/failure report message", rune); if (rc) goto failed_badly; rc = libxl_read_exactly(ctx, recv_fd, &rc_buf, 1, "migration ack stream", "success/failure status"); if (rc) goto failed_badly; if (rc_buf) { fprintf(stderr, "migration sender: Target reports startup failure" " (status code %d).\n", rc_buf); rc = migrate_read_fixedmessage(recv_fd, migrate_permission_to_go, sizeof(migrate_permission_to_go), "permission for sender to resume", rune); if (rc) goto failed_badly; fprintf(stderr, "migration sender: Trying to resume at our end.\n"); if (common_domname) { libxl_domain_rename(ctx, domid, away_domname, common_domname); } rc = libxl_domain_resume(ctx, domid, 1, 0); if (!rc) fprintf(stderr, "migration sender: Resumed OK.\n"); fprintf(stderr, "Migration failed due to problems at target.\n"); exit(EXIT_FAILURE); } fprintf(stderr, "migration sender: Target reports successful startup.\n"); libxl_domain_destroy(ctx, domid, 0); /* bang! */ fprintf(stderr, "Migration successful.\n"); exit(EXIT_SUCCESS); failed_suspend: close(send_fd); migration_child_report(recv_fd); fprintf(stderr, "Migration failed, failed to suspend at sender.\n"); exit(EXIT_FAILURE); failed_resume: close(send_fd); migration_child_report(recv_fd); fprintf(stderr, "Migration failed, resuming at sender.\n"); libxl_domain_resume(ctx, domid, 1, 0); exit(EXIT_FAILURE); failed_badly: fprintf(stderr, "** Migration failed during final handshake **\n" "Domain state is now undefined !\n" "Please CHECK AT BOTH ENDS for running instances, before renaming and\n" " resuming at most one instance. Two simultaneous instances of the domain\n" " would probably result in SEVERE DATA LOSS and it is now your\n" " responsibility to avoid that. Sorry.\n"); close(send_fd); migration_child_report(recv_fd); exit(EXIT_FAILURE); } static void migrate_receive(int debug, int daemonize, int monitor, int pause_after_migration, int send_fd, int recv_fd, libxl_checkpointed_stream checkpointed, char *colo_proxy_script, bool userspace_colo_proxy) { uint32_t domid; int rc, rc2; char rc_buf; char *migration_domname; struct domain_create dom_info; signal(SIGPIPE, SIG_IGN); /* if we get SIGPIPE we'd rather just have it as an error */ fprintf(stderr, "migration target: Ready to receive domain.\n"); CHK_ERRNOVAL(libxl_write_exactly( ctx, send_fd, migrate_receiver_banner, sizeof(migrate_receiver_banner)-1, "migration ack stream", "banner") ); memset(&dom_info, 0, sizeof(dom_info)); dom_info.debug = debug; dom_info.daemonize = daemonize; dom_info.monitor = monitor; dom_info.paused = 1; dom_info.migrate_fd = recv_fd; dom_info.send_back_fd = send_fd; dom_info.migration_domname_r = &migration_domname; dom_info.checkpointed_stream = checkpointed; dom_info.colo_proxy_script = colo_proxy_script; dom_info.userspace_colo_proxy = userspace_colo_proxy; rc = create_domain(&dom_info); if (rc < 0) { fprintf(stderr, "migration target: Domain creation failed" " (code %d).\n", rc); exit(EXIT_FAILURE); } domid = rc; switch (checkpointed) { case LIBXL_CHECKPOINTED_STREAM_REMUS: case LIBXL_CHECKPOINTED_STREAM_COLO: { const char *ha = checkpointed == LIBXL_CHECKPOINTED_STREAM_COLO ? "COLO" : "Remus"; /* If we are here, it means that the sender (primary) has crashed. * TODO: Split-Brain Check. */ fprintf(stderr, "migration target: %s Failover for domain %u\n", ha, domid); /* * If domain renaming fails, lets just continue (as we need the domain * to be up & dom names may not matter much, as long as its reachable * over network). * * If domain unpausing fails, destroy domain ? Or is it better to have * a consistent copy of the domain (memory, cpu state, disk) * on atleast one physical host ? Right now, lets just leave the domain * as is and let the Administrator decide (or troubleshoot). */ if (migration_domname) { rc = libxl_domain_rename(ctx, domid, migration_domname, common_domname); if (rc) fprintf(stderr, "migration target (%s): " "Failed to rename domain from %s to %s:%d\n", ha, migration_domname, common_domname, rc); } if (checkpointed == LIBXL_CHECKPOINTED_STREAM_COLO) /* The guest is running after failover in COLO mode */ exit(rc ? -ERROR_FAIL: 0); rc = libxl_domain_unpause(ctx, domid); if (rc) fprintf(stderr, "migration target (%s): " "Failed to unpause domain %s (id: %u):%d\n", ha, common_domname, domid, rc); exit(rc ? EXIT_FAILURE : EXIT_SUCCESS); } default: /* do nothing */ break; } fprintf(stderr, "migration target: Transfer complete," " requesting permission to start domain.\n"); rc = libxl_write_exactly(ctx, send_fd, migrate_receiver_ready, sizeof(migrate_receiver_ready), "migration ack stream", "ready message"); if (rc) exit(EXIT_FAILURE); rc = migrate_read_fixedmessage(recv_fd, migrate_permission_to_go, sizeof(migrate_permission_to_go), "GO message", 0); if (rc) goto perhaps_destroy_notify_rc; fprintf(stderr, "migration target: Got permission, starting domain.\n"); if (migration_domname) { rc = libxl_domain_rename(ctx, domid, migration_domname, common_domname); if (rc) goto perhaps_destroy_notify_rc; } if (!pause_after_migration) { rc = libxl_domain_unpause(ctx, domid); if (rc) goto perhaps_destroy_notify_rc; } fprintf(stderr, "migration target: Domain started successsfully.\n"); rc = 0; perhaps_destroy_notify_rc: rc2 = libxl_write_exactly(ctx, send_fd, migrate_report, sizeof(migrate_report), "migration ack stream", "success/failure report"); if (rc2) exit(EXIT_FAILURE); rc_buf = -rc; assert(!!rc_buf == !!rc); rc2 = libxl_write_exactly(ctx, send_fd, &rc_buf, 1, "migration ack stream", "success/failure code"); if (rc2) exit(EXIT_FAILURE); if (rc) { fprintf(stderr, "migration target: Failure, destroying our copy.\n"); rc2 = libxl_domain_destroy(ctx, domid, 0); if (rc2) { fprintf(stderr, "migration target: Failed to destroy our copy" " (code %d).\n", rc2); exit(EXIT_FAILURE); } fprintf(stderr, "migration target: Cleanup OK, granting sender" " permission to resume.\n"); rc2 = libxl_write_exactly(ctx, send_fd, migrate_permission_to_go, sizeof(migrate_permission_to_go), "migration ack stream", "permission to sender to have domain back"); if (rc2) exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } int main_migrate_receive(int argc, char **argv) { int debug = 0, daemonize = 1, monitor = 1, pause_after_migration = 0; libxl_checkpointed_stream checkpointed = LIBXL_CHECKPOINTED_STREAM_NONE; int opt; bool userspace_colo_proxy = false; char *script = NULL; static struct option opts[] = { {"colo", 0, 0, 0x100}, /* It is a shame that the management code for disk is not here. */ {"coloft-script", 1, 0, 0x200}, {"userspace-colo-proxy", 0, 0, 0x300}, COMMON_LONG_OPTS }; SWITCH_FOREACH_OPT(opt, "Fedrp", opts, "migrate-receive", 0) { case 'F': daemonize = 0; break; case 'e': daemonize = 0; monitor = 0; break; case 'd': debug = 1; break; case 'r': checkpointed = LIBXL_CHECKPOINTED_STREAM_REMUS; break; case 0x100: checkpointed = LIBXL_CHECKPOINTED_STREAM_COLO; break; case 0x200: script = optarg; break; case 0x300: userspace_colo_proxy = true; break; case 'p': pause_after_migration = 1; break; } if (argc-optind != 0) { help("migrate-receive"); return EXIT_FAILURE; } migrate_receive(debug, daemonize, monitor, pause_after_migration, STDOUT_FILENO, STDIN_FILENO, checkpointed, script, userspace_colo_proxy); return EXIT_SUCCESS; } int main_migrate(int argc, char **argv) { uint32_t domid; const char *config_filename = NULL; const char *ssh_command = "ssh"; char *rune = NULL; char *host; int opt, daemonize = 1, monitor = 1, debug = 0, pause_after_migration = 0; static struct option opts[] = { {"debug", 0, 0, 0x100}, {"live", 0, 0, 0x200}, COMMON_LONG_OPTS }; SWITCH_FOREACH_OPT(opt, "FC:s:ep", opts, "migrate", 2) { case 'C': config_filename = optarg; break; case 's': ssh_command = optarg; break; case 'F': daemonize = 0; break; case 'e': daemonize = 0; monitor = 0; break; case 'p': pause_after_migration = 1; break; case 0x100: /* --debug */ debug = 1; break; case 0x200: /* --live */ /* ignored for compatibility with xm */ break; } domid = find_domain(argv[optind]); host = argv[optind + 1]; bool pass_tty_arg = progress_use_cr || (isatty(2) > 0); if (!ssh_command[0]) { rune= host; } else { char verbose_buf[minmsglevel_default+3]; int verbose_len; verbose_buf[0] = ' '; verbose_buf[1] = '-'; memset(verbose_buf+2, 'v', minmsglevel_default); verbose_buf[sizeof(verbose_buf)-1] = 0; if (minmsglevel == minmsglevel_default) { verbose_len = 0; } else { verbose_len = (minmsglevel_default - minmsglevel) + 2; } xasprintf(&rune, "exec %s %s xl%s%.*s migrate-receive%s%s%s", ssh_command, host, pass_tty_arg ? " -t" : "", verbose_len, verbose_buf, daemonize ? "" : " -e", debug ? " -d" : "", pause_after_migration ? " -p" : ""); } migrate_domain(domid, rune, debug, config_filename); return EXIT_SUCCESS; } int main_remus(int argc, char **argv) { uint32_t domid; int opt, rc, daemonize = 1; const char *ssh_command = "ssh"; char *host = NULL, *rune = NULL; libxl_domain_remus_info r_info; int send_fd = -1, recv_fd = -1; pid_t child = -1; uint8_t *config_data; int config_len; memset(&r_info, 0, sizeof(libxl_domain_remus_info)); SWITCH_FOREACH_OPT(opt, "Fbundi:s:N:ecp", NULL, "remus", 2) { case 'i': r_info.interval = atoi(optarg); break; case 'F': libxl_defbool_set(&r_info.allow_unsafe, true); break; case 'b': libxl_defbool_set(&r_info.blackhole, true); break; case 'u': libxl_defbool_set(&r_info.compression, false); break; case 'n': libxl_defbool_set(&r_info.netbuf, false); break; case 'N': r_info.netbufscript = optarg; break; case 'd': libxl_defbool_set(&r_info.diskbuf, false); break; case 's': ssh_command = optarg; break; case 'e': daemonize = 0; break; case 'c': libxl_defbool_set(&r_info.colo, true); break; case 'p': libxl_defbool_set(&r_info.userspace_colo_proxy, true); } domid = find_domain(argv[optind]); host = argv[optind + 1]; /* Defaults */ libxl_defbool_setdefault(&r_info.blackhole, false); libxl_defbool_setdefault(&r_info.colo, false); libxl_defbool_setdefault(&r_info.userspace_colo_proxy, false); if (!libxl_defbool_val(r_info.colo) && !r_info.interval) r_info.interval = 200; if (libxl_defbool_val(r_info.userspace_colo_proxy) && !libxl_defbool_val(r_info.colo)) { fprintf(stderr, "Option -p must be used in conjunction with -c"); exit(-1); } if (libxl_defbool_val(r_info.colo)) { if (r_info.interval || libxl_defbool_val(r_info.blackhole) || !libxl_defbool_is_default(r_info.netbuf) || !libxl_defbool_is_default(r_info.diskbuf)) { perror("option -c is conflict with -i, -d, -n or -b"); exit(-1); } if (libxl_defbool_is_default(r_info.compression)) { perror("COLO can't be used with memory compression. " "Disable memory checkpoint compression now..."); libxl_defbool_set(&r_info.compression, false); } } if (!r_info.netbufscript) { if (libxl_defbool_val(r_info.colo)) r_info.netbufscript = default_colo_proxy_script; else r_info.netbufscript = default_remus_netbufscript; } if (libxl_defbool_val(r_info.blackhole)) { send_fd = open("/dev/null", O_RDWR, 0644); if (send_fd < 0) { perror("failed to open /dev/null"); exit(EXIT_FAILURE); } } else { if (!ssh_command[0]) { rune = host; } else { if (!libxl_defbool_val(r_info.colo)) { xasprintf(&rune, "exec %s %s xl migrate-receive %s %s", ssh_command, host, "-r", daemonize ? "" : " -e"); } else { xasprintf(&rune, "exec %s %s xl migrate-receive %s %s %s %s %s", ssh_command, host, "--colo", r_info.netbufscript ? "--coloft-script" : "", r_info.netbufscript ? r_info.netbufscript : "", libxl_defbool_val(r_info.userspace_colo_proxy) ? "--userspace-colo-proxy" : "", daemonize ? "" : " -e"); } } save_domain_core_begin(domid, NULL, &config_data, &config_len); if (!config_len) { fprintf(stderr, "No config file stored for running domain and " "none supplied - cannot start remus.\n"); exit(EXIT_FAILURE); } child = create_migration_child(rune, &send_fd, &recv_fd); migrate_do_preamble(send_fd, recv_fd, child, config_data, config_len, rune); if (ssh_command[0]) free(rune); } /* Point of no return */ rc = libxl_domain_remus_start(ctx, &r_info, domid, send_fd, recv_fd, 0); /* check if the domain exists. User may have xl destroyed the * domain to force failover */ if (libxl_domain_info(ctx, 0, domid)) { fprintf(stderr, "%s: Primary domain has been destroyed.\n", libxl_defbool_val(r_info.colo) ? "COLO" : "Remus"); close(send_fd); return EXIT_SUCCESS; } /* If we are here, it means remus setup/domain suspend/backup has * failed. Try to resume the domain and exit gracefully. * TODO: Split-Brain check. */ if (rc == ERROR_GUEST_TIMEDOUT) fprintf(stderr, "Failed to suspend domain at primary.\n"); else { fprintf(stderr, "%s: Backup failed? resuming domain at primary.\n", libxl_defbool_val(r_info.colo) ? "COLO" : "Remus"); libxl_domain_resume(ctx, domid, 1, 0); } close(send_fd); return EXIT_FAILURE; } #endif /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_misc.c0000664000175000017500000002113313256712137014042 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" static void button_press(uint32_t domid, const char *b) { libxl_trigger trigger; if (!strcmp(b, "power")) { trigger = LIBXL_TRIGGER_POWER; } else if (!strcmp(b, "sleep")) { trigger = LIBXL_TRIGGER_SLEEP; } else { fprintf(stderr, "%s is an invalid button identifier\n", b); exit(EXIT_FAILURE); } libxl_send_trigger(ctx, domid, trigger, 0); } int main_button_press(int argc, char **argv) { int opt; fprintf(stderr, "WARNING: \"button-press\" is deprecated. " "Please use \"trigger\"\n"); SWITCH_FOREACH_OPT(opt, "", NULL, "button-press", 2) { /* No options */ } button_press(find_domain(argv[optind]), argv[optind + 1]); return 0; } int main_rename(int argc, char **argv) { uint32_t domid; int opt; const char *dom, *new_name; SWITCH_FOREACH_OPT(opt, "", NULL, "rename", 2) { /* No options */ } dom = argv[optind++]; new_name = argv[optind]; domid = find_domain(dom); if (libxl_domain_rename(ctx, domid, common_domname, new_name)) { fprintf(stderr, "Can't rename domain '%s'.\n", dom); return 1; } return 0; } int main_trigger(int argc, char **argv) { uint32_t domid; int opt; char *endptr = NULL; int vcpuid = 0; const char *trigger_name = NULL; libxl_trigger trigger; SWITCH_FOREACH_OPT(opt, "", NULL, "trigger", 2) { /* No options */ } domid = find_domain(argv[optind++]); trigger_name = argv[optind++]; if (libxl_trigger_from_string(trigger_name, &trigger)) { fprintf(stderr, "Invalid trigger \"%s\"\n", trigger_name); return EXIT_FAILURE; } if (argv[optind]) { vcpuid = strtol(argv[optind], &endptr, 10); if (vcpuid == 0 && !strcmp(endptr, argv[optind])) { fprintf(stderr, "Invalid vcpuid, using default vcpuid=0.\n\n"); } } libxl_send_trigger(ctx, domid, trigger, vcpuid); return EXIT_SUCCESS; } int main_sysrq(int argc, char **argv) { uint32_t domid; int opt; const char *sysrq = NULL; SWITCH_FOREACH_OPT(opt, "", NULL, "sysrq", 2) { /* No options */ } domid = find_domain(argv[optind++]); sysrq = argv[optind]; if (sysrq[1] != '\0') { fprintf(stderr, "Invalid sysrq.\n\n"); help("sysrq"); return EXIT_FAILURE; } libxl_send_sysrq(ctx, domid, sysrq[0]); return EXIT_SUCCESS; } int main_debug_keys(int argc, char **argv) { int opt; char *keys; SWITCH_FOREACH_OPT(opt, "", NULL, "debug-keys", 1) { /* No options */ } keys = argv[optind]; if (libxl_send_debug_keys(ctx, keys)) { fprintf(stderr, "cannot send debug keys: %s\n", keys); return EXIT_FAILURE; } return EXIT_SUCCESS; } int main_devd(int argc, char **argv) { int ret = 0, opt = 0, daemonize = 1; const char *pidfile = NULL; static const struct option opts[] = { {"pidfile", 1, 0, 'p'}, COMMON_LONG_OPTS, {0, 0, 0, 0} }; SWITCH_FOREACH_OPT(opt, "Fp:", opts, "devd", 0) { case 'F': daemonize = 0; break; case 'p': pidfile = optarg; break; } if (daemonize) { ret = do_daemonize("xldevd", pidfile); if (ret) { ret = (ret == 1) ? 0 : ret; goto out; } } ret = libxl_device_events_handler(ctx, 0) ? EXIT_FAILURE : EXIT_SUCCESS; out: return ret; } int main_qemu_monitor_command(int argc, char **argv) { int opt; uint32_t domid; char *cmd; char *output; int ret; SWITCH_FOREACH_OPT(opt, "", NULL, "qemu-monitor-command", 2) { /* No options */ } domid = find_domain(argv[optind]); cmd = argv[optind + 1]; if (argc - optind > 2) { fprintf(stderr, "Invalid arguments.\n"); return EXIT_FAILURE; } ret = libxl_qemu_monitor_command(ctx, domid, cmd, &output); if (!ret && output) { printf("%s\n", output); free(output); } return ret ? EXIT_FAILURE : EXIT_SUCCESS; } static void core_dump_domain(uint32_t domid, const char *filename) { int rc; rc=libxl_domain_core_dump(ctx, domid, filename, NULL); if (rc) { fprintf(stderr,"core dump failed (rc=%d)\n",rc);exit(EXIT_FAILURE); } } int main_dump_core(int argc, char **argv) { int opt; SWITCH_FOREACH_OPT(opt, "", NULL, "dump-core", 2) { /* No options */ } core_dump_domain(find_domain(argv[optind]), argv[optind + 1]); return EXIT_SUCCESS; } extern void printf_info(enum output_format output_format, int domid, libxl_domain_config *d_config, FILE *fh); int main_config_update(int argc, char **argv) { uint32_t domid; const char *filename = NULL; char *extra_config = NULL; void *config_data = 0; int config_len = 0; libxl_domain_config d_config; int opt, rc; int debug = 0; static struct option opts[] = { {"defconfig", 1, 0, 'f'}, COMMON_LONG_OPTS }; if (argc < 2) { fprintf(stderr, "xl config-update requires a domain argument\n"); help("config-update"); exit(1); } fprintf(stderr, "WARNING: xl now has better capability to manage domain configuration, " "avoid using this command when possible\n"); domid = find_domain(argv[1]); argc--; argv++; if (argv[1] && argv[1][0] != '-' && !strchr(argv[1], '=')) { filename = argv[1]; argc--; argv++; } SWITCH_FOREACH_OPT(opt, "dqf:", opts, "config_update", 0) { case 'd': debug = 1; break; case 'f': filename = optarg; break; } for (; optind < argc; optind++) { if (strchr(argv[optind], '=') != NULL) { string_realloc_append(&extra_config, argv[optind]); string_realloc_append(&extra_config, "\n"); } else if (!filename) { filename = argv[optind]; } else { help("create"); free(extra_config); return 2; } } if (filename) { free(config_data); config_data = 0; rc = libxl_read_file_contents(ctx, filename, &config_data, &config_len); if (rc) { fprintf(stderr, "Failed to read config file: %s: %s\n", filename, strerror(errno)); free(extra_config); return ERROR_FAIL; } if (extra_config && strlen(extra_config)) { if (config_len > INT_MAX - (strlen(extra_config) + 2 + 1)) { fprintf(stderr, "Failed to attach extra configuration\n"); exit(1); } /* allocate space for the extra config plus two EOLs plus \0 */ config_data = realloc(config_data, config_len + strlen(extra_config) + 2 + 1); if (!config_data) { fprintf(stderr, "Failed to realloc config_data\n"); exit(1); } config_len += sprintf(config_data + config_len, "\n%s\n", extra_config); } } else { fprintf(stderr, "Config file not specified\n"); exit(1); } libxl_domain_config_init(&d_config); parse_config_data(filename, config_data, config_len, &d_config); if (debug || dryrun_only) printf_info(default_output_format, -1, &d_config, stdout); if (!dryrun_only) { fprintf(stderr, "setting dom%u configuration\n", domid); rc = libxl_userdata_store(ctx, domid, "xl", config_data, config_len); if (rc) { fprintf(stderr, "failed to update configuration\n"); exit(1); } } libxl_domain_config_dispose(&d_config); free(config_data); free(extra_config); return 0; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_nic.c0000664000175000017500000001131013256712137013654 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include "xl.h" #include "xl_utils.h" #include "xl_parse.h" void set_default_nic_values(libxl_device_nic *nic); void set_default_nic_values(libxl_device_nic *nic) { if (default_vifscript) { free(nic->script); nic->script = strdup(default_vifscript); } if (default_bridge) { free(nic->bridge); nic->bridge = strdup(default_bridge); } if (default_gatewaydev) { free(nic->gatewaydev); nic->gatewaydev = strdup(default_gatewaydev); } if (default_vifbackend) { free(nic->backend_domname); nic->backend_domname = strdup(default_vifbackend); } } int main_networkattach(int argc, char **argv) { uint32_t domid; int opt; libxl_device_nic nic; XLU_Config *config = 0; SWITCH_FOREACH_OPT(opt, "", NULL, "network-attach", 1) { /* No options */ } domid = find_domain(argv[optind]); config= xlu_cfg_init(stderr, "command line"); if (!config) { fprintf(stderr, "Failed to allocate for configuration\n"); return 1; } libxl_device_nic_init(&nic); set_default_nic_values(&nic); for (argv += optind+1, argc -= optind+1; argc > 0; ++argv, --argc) { if (parse_nic_config(&nic, &config, *argv)) return 1; } if (dryrun_only) { char *json = libxl_device_nic_to_json(ctx, &nic); printf("vif: %s\n", json); free(json); libxl_device_nic_dispose(&nic); if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); } return 0; } if (libxl_device_nic_add(ctx, domid, &nic, 0)) { fprintf(stderr, "libxl_device_nic_add failed.\n"); return 1; } libxl_device_nic_dispose(&nic); xlu_cfg_destroy(config); return 0; } int main_networklist(int argc, char **argv) { int opt; libxl_device_nic *nics; libxl_nicinfo nicinfo; int nb, i; SWITCH_FOREACH_OPT(opt, "", NULL, "network-list", 1) { /* No options */ } /* Idx BE MAC Hdl Sta evch txr/rxr BE-path */ printf("%-3s %-2s %-17s %-6s %-5s %-6s %5s/%-5s %-30s\n", "Idx", "BE", "Mac Addr.", "handle", "state", "evt-ch", "tx-", "rx-ring-ref", "BE-path"); for (argv += optind, argc -= optind; argc > 0; --argc, ++argv) { uint32_t domid = find_domain(*argv); nics = libxl_device_nic_list(ctx, domid, &nb); if (!nics) { continue; } for (i = 0; i < nb; ++i) { if (!libxl_device_nic_getinfo(ctx, domid, &nics[i], &nicinfo)) { /* Idx BE */ printf("%-3d %-2d ", nicinfo.devid, nicinfo.backend_id); /* MAC */ printf(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nics[i].mac)); /* Hdl Sta evch txr/rxr BE-path */ printf("%6d %5d %6d %5d/%-11d %-30s\n", nicinfo.devid, nicinfo.state, nicinfo.evtch, nicinfo.rref_tx, nicinfo.rref_rx, nicinfo.backend); libxl_nicinfo_dispose(&nicinfo); } libxl_device_nic_dispose(&nics[i]); } free(nics); } return 0; } int main_networkdetach(int argc, char **argv) { uint32_t domid; int opt; libxl_device_nic nic; SWITCH_FOREACH_OPT(opt, "", NULL, "network-detach", 2) { /* No options */ } domid = find_domain(argv[optind]); if (!strchr(argv[optind+1], ':')) { if (libxl_devid_to_device_nic(ctx, domid, atoi(argv[optind+1]), &nic)) { fprintf(stderr, "Unknown device %s.\n", argv[optind+1]); return 1; } } else { if (libxl_mac_to_device_nic(ctx, domid, argv[optind+1], &nic)) { fprintf(stderr, "Unknown device %s.\n", argv[optind+1]); return 1; } } if (libxl_device_nic_remove(ctx, domid, &nic, 0)) { fprintf(stderr, "libxl_device_nic_del failed.\n"); return 1; } libxl_device_nic_dispose(&nic); return 0; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl_utils.h0000664000175000017500000001413413256712137014257 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #ifndef XL_UTILS_H #define XL_UTILS_H #include /* For calls which return an errno on failure */ #define CHK_ERRNOVAL( call ) ({ \ int chk_errnoval = (call); \ if (chk_errnoval < 0) \ abort(); \ else if (chk_errnoval > 0) { \ fprintf(stderr,"xl: fatal error: %s:%d: %s: %s\n", \ __FILE__,__LINE__, strerror(chk_errnoval), #call); \ exit(EXIT_FAILURE); \ } \ }) /* For calls which return -1 and set errno on failure */ #define CHK_SYSCALL( call ) ({ \ if ((call) == -1) { \ fprintf(stderr,"xl: fatal error: %s:%d: %s: %s\n", \ __FILE__,__LINE__, strerror(errno), #call); \ exit(EXIT_FAILURE); \ } \ }) #define MUST( call ) ({ \ int must_rc = (call); \ if (must_rc < 0) { \ fprintf(stderr,"xl: fatal error: %s:%d, rc=%d: %s\n", \ __FILE__,__LINE__, must_rc, #call); \ exit(EXIT_FAILURE); \ } \ }) #define STR_HAS_PREFIX( a, b ) \ ( strncmp(a, b, strlen(b)) == 0 ) #define STR_SKIP_PREFIX( a, b ) \ ( STR_HAS_PREFIX(a, b) ? ((a) += strlen(b), 1) : 0 ) #define INVALID_DOMID ~0 #define LOG(_f, _a...) dolog(__FILE__, __LINE__, __func__, _f "\n", ##_a) /* * Wraps def_getopt into a convenient loop+switch to process all * arguments. This macro is intended to be called from main_XXX(). * * SWITCH_FOREACH_OPT(int *opt, "OPTS", * const struct option *longopts, * const char *commandname, * int num_opts_req) { ... * * opt: pointer to an int variable, holds the current option * during processing. * OPTS: short options, as per getopt_long(3)'s optstring argument. * do not include "h"; will be provided automatically * longopts: long options, as per getopt_long(3)'s longopts argument. * May be null. * commandname: name of this command, for usage string. * num_required_opts: number of non-option command line parameters * which are required. * * In addition the calling context is expected to contain variables * "argc" and "argv" in the conventional C-style: * main(int argc, char **argv) * manner. * * Callers should treat SWITCH_FOREACH_OPT as they would a switch * statement over the value of `opt`. Each option given in `opts` (or * `lopts`) should be handled by a case statement as if it were inside * a switch statement. * * In addition to the options provided in opts the macro will handle * the "help" option and enforce a minimum number of non-option * command line pearameters as follows: * -- if the user passes a -h or --help option. help will be printed, * and the macro will cause the process to exit with code 0. * -- if the user does not provided `num_required_opts` non-option * arguments, the macro will cause the process to exit with code 2. * * Example: * * int main_foo(int argc, char **argv) { * int opt; * * SWITCH_FOREACH_OPT(opt, "blah", NULL, "foo", 0) { * case 'b': * ... handle b option... * break; * case 'l': * ... handle l option ... * break; * case etc etc... * } * ... do something useful with the options ... * } */ #define SWITCH_FOREACH_OPT(opt, opts, longopts, \ commandname, num_required_opts) \ while (((opt) = def_getopt(argc, argv, "h" opts, (longopts), \ (commandname), (num_required_opts))) != -1) \ switch (opt) /* Must be last in list */ #define COMMON_LONG_OPTS {"help", 0, 0, 'h'}, \ {0, 0, 0, 0} int def_getopt(int argc, char * const argv[], const char *optstring, const struct option *longopts, const char* helpstr, int reqargs); void dolog(const char *file, int line, const char *func, char *fmt, ...) __attribute__((format(printf,4,5))); void xvasprintf(char **strp, const char *fmt, va_list ap) __attribute__((format(printf,2,0))); void xasprintf(char **strp, const char *fmt, ...) __attribute__((format(printf,2,3))); void *xmalloc(size_t sz); void *xcalloc(size_t n, size_t sz); void *xrealloc(void *ptr, size_t sz); char *xstrdup(const char *x); void string_realloc_append(char **accumulate, const char *more); void flush_stream(FILE *fh); uint32_t find_domain(const char *p) __attribute__((warn_unused_result)); void print_bitmap(uint8_t *map, int maplen, FILE *stream); int do_daemonize(char *name, const char *pidfile); #endif /* XL_UTILS_H */ /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ xen-4.9.2/tools/xl/xl.c0000664000175000017500000002652613256712137013042 0ustar smbsmb/* * Copyright 2009-2017 Citrix Ltd and other contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xl.h" xentoollog_logger_stdiostream *logger; int dryrun_only; int force_execution; int autoballoon = -1; char *blkdev_start; int run_hotplug_scripts = 1; char *lockfile; char *default_vifscript = NULL; char *default_bridge = NULL; char *default_gatewaydev = NULL; char *default_vifbackend = NULL; char *default_remus_netbufscript = NULL; char *default_colo_proxy_script = NULL; enum output_format default_output_format = OUTPUT_FORMAT_JSON; int claim_mode = 1; bool progress_use_cr = 0; xentoollog_level minmsglevel = minmsglevel_default; int logfile = 2; /* every libxl action in xl uses this same libxl context */ libxl_ctx *ctx; xlchild children[child_max]; const char *common_domname; /* Get autoballoon option based on presence of dom0_mem Xen command line option. */ static int auto_autoballoon(void) { const libxl_version_info *info; regex_t regex; int ret; info = libxl_get_version_info(ctx); if (!info) return 1; /* default to on */ ret = regcomp(®ex, "(^| )dom0_mem=((|min:|max:)[0-9]+[bBkKmMgG]?,?)+($| )", REG_NOSUB | REG_EXTENDED); if (ret) return 1; ret = regexec(®ex, info->commandline, 0, NULL, 0); regfree(®ex); return ret == REG_NOMATCH; } static void parse_global_config(const char *configfile, const char *configfile_data, int configfile_len) { long l; XLU_Config *config; int e; const char *buf; config = xlu_cfg_init(stderr, configfile); if (!config) { fprintf(stderr, "Failed to allocate for configuration\n"); exit(1); } e = xlu_cfg_readdata(config, configfile_data, configfile_len); if (e) { fprintf(stderr, "Failed to parse config file: %s\n", strerror(e)); exit(1); } if (!xlu_cfg_get_string(config, "autoballoon", &buf, 0)) { if (!strcmp(buf, "on") || !strcmp(buf, "1")) autoballoon = 1; else if (!strcmp(buf, "off") || !strcmp(buf, "0")) autoballoon = 0; else if (!strcmp(buf, "auto")) autoballoon = -1; else fprintf(stderr, "invalid autoballoon option"); } if (autoballoon == -1) autoballoon = auto_autoballoon(); if (!xlu_cfg_get_long (config, "run_hotplug_scripts", &l, 0)) run_hotplug_scripts = l; if (!xlu_cfg_get_string (config, "lockfile", &buf, 0)) lockfile = strdup(buf); else { lockfile = strdup(XL_LOCK_FILE); } if (!lockfile) { fprintf(stderr, "failed to allocate lockfile\n"); exit(1); } /* * For global options that are related to a specific type of device * we use the following nomenclature: * * .default.